/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import { memo, useState, FC, useEffect } from 'react';
import { Form, Button, Alert } from 'react-bootstrap';
import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom';

import { marked } from 'marked';
import classNames from 'classnames';

import { usePromptWithUnload } from '@/hooks';
import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Editor, Modal, TextArea } from '@/components';
import { FormDataType, PostAnswerReq } from '@/common/interface';
import { postAnswer } from '@/services';
import { guard, handleFormError, SaveDraft, storageExpires } from '@/utils';
import { DRAFT_ANSWER_STORAGE_KEY } from '@/common/constants';
import { writeSettingStore } from '@/stores';

interface Props {
  visible?: boolean;
  data: {
    /** question  id */
    qid: string;
    answered?: boolean;
    loggedUserRank: number;
    first_answer_id?: string;
  };
  callback?: (obj) => void;
}

const saveDraft = new SaveDraft({ type: 'answer' });

const Index: FC<Props> = ({ visible = false, data, callback }) => {
  const { t } = useTranslation('translation', {
    keyPrefix: 'question_detail.write_answer',
  });
  const [formData, setFormData] = useState<FormDataType>({
    content: {
      value: '',
      isInvalid: false,
      errorMsg: '',
    },
  });
  const [showEditor, setShowEditor] = useState<boolean>(visible);
  const [focusType, setFocusType] = useState('');
  const [editorFocusState, setEditorFocusState] = useState(false);
  const [hasDraft, setHasDraft] = useState(false);
  const [showTips, setShowTips] = useState(data.loggedUserRank < 100);
  const aCaptcha = useCaptchaPlugin('answer');
  const writeInfo = writeSettingStore((state) => state.write);
  const [editorCanSave, setEditorCanSave] = useState(false);

  usePromptWithUnload({
    when: Boolean(formData.content.value),
  });

  const removeDraft = () => {
    // immediately remove debounced save
    saveDraft.save.cancel();
    saveDraft.remove();
    setHasDraft(false);
  };

  useEffect(() => {
    const draft = storageExpires.get(DRAFT_ANSWER_STORAGE_KEY);
    if (draft?.questionId === data.qid && draft?.content) {
      setShowEditor(true);
      setFormData({
        content: {
          value: draft.content,
          isInvalid: false,
          errorMsg: '',
        },
      });
      setHasDraft(true);
    }
    setTimeout(() => {
      setEditorCanSave(true);
    }, 100);
  }, []);

  useEffect(() => {
    const draft = storageExpires.get(DRAFT_ANSWER_STORAGE_KEY);
    const { content } = formData;

    if (content.value) {
      // save Draft
      saveDraft.save({
        questionId: data?.qid,
        content: content.value,
      });

      setHasDraft(true);
    } else if (draft?.questionId === data.qid && !content.value) {
      removeDraft();
    }
  }, [formData.content.value]);

  const checkValidated = (): boolean => {
    let bol = true;
    const { content } = formData;

    if (!content.value || Array.from(content.value.trim()).length < 6) {
      bol = false;
      formData.content = {
        value: content.value,
        isInvalid: true,
        errorMsg: t('characters'),
      };
    } else {
      formData.content = {
        value: content.value,
        isInvalid: false,
        errorMsg: '',
      };
    }

    setFormData({
      ...formData,
    });
    return bol;
  };

  const resetForm = () => {
    setFormData({
      content: {
        value: '',
        isInvalid: false,
        errorMsg: '',
      },
    });
  };

  const deleteDraft = () => {
    const res = window.confirm(t('discard_confirm', { keyPrefix: 'draft' }));
    if (res) {
      removeDraft();
      resetForm();
    }
  };

  const submitAnswer = () => {
    const params: PostAnswerReq = {
      question_id: data?.qid,
      content: formData.content.value,
      html: marked.parse(formData.content.value),
    };
    const imgCode = aCaptcha?.getCaptcha();
    if (imgCode?.verify) {
      params.captcha_code = imgCode.captcha_code;
      params.captcha_id = imgCode.captcha_id;
    }
    postAnswer(params)
      .then(async (res) => {
        await aCaptcha?.close();
        setShowEditor(false);
        setFormData({
          content: {
            value: '',
            isInvalid: false,
            errorMsg: '',
          },
        });
        removeDraft();
        callback?.(res.info);
      })
      .catch((ex) => {
        if (ex.isError) {
          aCaptcha?.handleCaptchaError(ex.list);
          const stateData = handleFormError(ex, formData);
          setFormData({ ...stateData });
        }
      });
  };

  const handleSubmit = () => {
    if (!guard.tryNormalLogged(true)) {
      return;
    }
    if (!checkValidated()) {
      return;
    }
    if (!aCaptcha) {
      submitAnswer();
      return;
    }
    aCaptcha.check(() => submitAnswer());
  };

  const clickBtn = () => {
    if (!guard.tryNormalLogged(true)) {
      return;
    }

    if (data?.answered && !showEditor) {
      Modal.confirm({
        title: t('confirm_title'),
        content: t('confirm_info'),
        confirmText: t('continue'),
        onConfirm: () => {
          setShowEditor(true);
        },
      });
      return;
    }

    if (!showEditor) {
      setShowEditor(true);
      return;
    }

    handleSubmit();
  };
  const handleFocusForTextArea = (evt) => {
    if (!guard.tryNormalLogged(true)) {
      evt.currentTarget.blur();
      return;
    }
    setFocusType('answer');
    setShowEditor(true);
    setEditorFocusState(true);
  };

  return (
    <Form noValidate className="mt-4">
      {(!data.answered || showEditor) && (
        <Form.Group className="mb-3">
          <Form.Label>
            <h5>{t('title')}</h5>
          </Form.Label>
          <Form.Control
            isInvalid={formData.content.isInvalid}
            className="d-none"
          />
          {!showEditor && !data.answered && (
            <div className="d-flex">
              <TextArea
                className="w-100"
                rows={8}
                autoFocus={false}
                onFocus={handleFocusForTextArea}
              />
            </div>
          )}
          {showEditor && (
            <>
              <Editor
                className={classNames(
                  'form-control p-0',
                  focusType === 'answer' && 'focus',
                  formData.content.isInvalid && 'is-invalid',
                )}
                value={formData.content.value}
                autoFocus={editorFocusState}
                onChange={(val) => {
                  if (editorCanSave) {
                    setFormData({
                      content: {
                        value: val,
                        isInvalid: false,
                        errorMsg: '',
                      },
                    });
                  }
                }}
                onFocus={() => {
                  setFocusType('answer');
                }}
                onBlur={() => {
                  setFocusType('');
                }}
              />

              <Alert
                variant="warning"
                show={data.loggedUserRank < 100 && showTips}
                onClose={() => setShowTips(false)}
                dismissible
                className="mt-3">
                <p>{t('tips.header_1')}</p>
                <ul>
                  <li>
                    <Trans
                      i18nKey="question_detail.write_answer.tips.li1_1"
                      components={{ strong: <strong /> }}
                    />
                  </li>
                  <li>{t('tips.li1_2')}</li>
                </ul>
                <p>
                  <Trans
                    i18nKey="question_detail.write_answer.tips.header_2"
                    components={{ strong: <strong /> }}
                  />
                </p>
                <ul className="mb-0">
                  <li>{t('tips.li2_1')}</li>
                </ul>
              </Alert>
            </>
          )}

          <Form.Control.Feedback type="invalid">
            {formData.content.errorMsg}
          </Form.Control.Feedback>
        </Form.Group>
      )}

      {data.answered && !showEditor ? (
        // the 0th answer is the oldest one
        <Link
          to={`/posts/${data.qid}/${data.first_answer_id}/edit`}
          className="btn btn-primary">
          {t('edit_answer')}
        </Link>
      ) : (
        <Button onClick={clickBtn}>{t('btn_name')}</Button>
      )}

      {data.answered && !showEditor && !writeInfo.restrict_answer && (
        <Button onClick={clickBtn} className="ms-2 " variant="outline-primary">
          {t('add_another_answer')}
        </Button>
      )}

      {hasDraft && (
        <Button variant="link" className="ms-2" onClick={deleteDraft}>
          {t('discard_draft', { keyPrefix: 'btns' })}
        </Button>
      )}
    </Form>
  );
};

export default memo(Index);
