import React, { useEffect, useRef, useState } from 'react';
import {
  Alert,
  Badge,
  Card,
  Col,
  Container,
  Form,
  InputGroup,
  ListGroup,
  Row
} from 'react-bootstrap';
import { Hash } from 'react-bootstrap-icons';
import { useNavigate, useParams } from 'react-router';
import { useTranslation } from 'react-i18next';
import { NoteResponse } from '../../types/NoteResponse';
import api from '../../api-service/api';
import ApiConfig from '../../api-service/apiConfig';
import { translateServerResponse } from '../../utils/TranslatorUtils';
import FormInput from '../../components/FormInput';
import ModalMarkdown from '../../components/ModalMarkdown';
import AlertError from '../../components/AlertError';
import ContentHeader from '../../components/ContentHeader';

type NoteAction = 'add' | 'edit';

interface NoteDraft {
  title: string;
  content: string;
  noteUrl: string;
  tags: string[];
}

/**
 * NoteAdd component for adding and editing notes.
 *
 * @returns {React.ReactNode} The rendered NoteAdd component.
 */
function NoteAdd(): React.ReactNode {
  const [validated, setValidated] = useState<boolean>(false);
  const [errorMessage, setErrorMessage] = useState<string>('');
  const [noteId, setNoteId] = useState<number>(0);
  const [noteTitle, setNoteTitle] = useState<string>('');
  const [noteContent, setNoteContent] = useState<string>('');
  const [noteUrl, setNoteUrl] = useState<string>('');
  const [currentTag, setCurrentTag] = useState<string>('');
  const [selectedTags, setSelectedTags] = useState<string[]>([]);
  const [tags, setTags] = useState<string[]>([]);
  const [showTagDropdown, setShowTagDropdown] = useState<boolean>(false);
  const [action, setAction] = useState<NoteAction>('add');
  const [showPreviewMd, setShowPreviewMd] = useState<boolean>(false);
  const [draftBanner, setDraftBanner] = useState<boolean>(false);
  const { i18n, t } = useTranslation();
  const params = useParams();
  const navigate = useNavigate();
  const tagContainerRef = useRef<HTMLDivElement>(null);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const hasUserEdited = useRef<boolean>(false);

  const draftKey = params?.id ? `draft:note:edit:${params.id}` : 'draft:note:new';

  const loadTags = async (): Promise<void> => {
    try {
      const response: string[] = await api.getJSON(`${ApiConfig.homeUrl}/tasks/tags`);
      setTags(response.filter(tag => tag !== 'untagged'));
    }
    catch (e) {
      handleError(e);
    }
  };

  /**
   * Handles errors by setting the error message and form invalid state.
   *
   * @param {unknown} e - The error to handle.
   */
  const handleError = (e: unknown): void => {
    if (typeof e === 'string') {
      setErrorMessage(translateServerResponse(e, i18n.language));
    }
    else if (e instanceof Error) {
      setErrorMessage(translateServerResponse(e.message, i18n.language));
    }
  };

  /**
   * Adds a new note.
   *
   * @param {NoteResponse} payload - The note data to add.
   * @returns {Promise<boolean>} True if the note was added successfully, false otherwise.
   */
  const addNote = async (payload: NoteResponse): Promise<boolean> => {
    try {
      await api.postJSON(ApiConfig.notesUrl, payload);
      return true;
    }
    catch (e) {
      handleError(e);
    }
    return false;
  };

  /**
   * Submits the edited note.
   *
   * @param {NoteResponse} payload - The note data to edit.
   * @returns {Promise<boolean>} True if the note was edited successfully, false otherwise.
   */
  const submitEditNote = async (payload: NoteResponse): Promise<boolean> => {
    try {
      await api.patchJSON(`${ApiConfig.notesUrl}/${payload.id}`, payload);
      return true;
    }
    catch (e) {
      handleError(e);
    }
    return false;
  };

  /**
   * Resets the input fields to their default values.
   */
  const resetInputs = () => {
    setNoteId(0);
    setNoteTitle('');
    setNoteUrl('');
    setNoteContent('');
    setCurrentTag('');
    setSelectedTags([]);
    setAction('add');
    setValidated(false);
  };

  const saveDraft = (title: string, content: string, noteUrl: string, draftTags: string[]): void => {
    if (!hasUserEdited.current) return;
    if (debounceRef.current) clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => {
      const draft: NoteDraft = { title, content, noteUrl, tags: draftTags };
      localStorage.setItem(draftKey, JSON.stringify(draft));
    }, 1500);
  };

  const clearDraft = (): void => {
    if (debounceRef.current) clearTimeout(debounceRef.current);
    localStorage.removeItem(draftKey);
  };

  const applyDraft = (): void => {
    const raw = localStorage.getItem(draftKey);
    if (!raw) return;
    try {
      const draft: NoteDraft = JSON.parse(raw);
      setNoteTitle(draft.title ?? '');
      setNoteContent(draft.content ?? '');
      setNoteUrl(draft.noteUrl ?? '');
      setSelectedTags(draft.tags ?? []);
      setDraftBanner(true);
    }
    catch {
      localStorage.removeItem(draftKey);
    }
  };

  const handleDiscardDraft = async (): Promise<void> => {
    setDraftBanner(false);
    if (params?.id) {
      try {
        const noteToEdit: NoteResponse = await api.getJSON(`${ApiConfig.notesUrl}/${params.id}`);
        setNoteFromServer(noteToEdit);
      }
      catch (e) {
        handleError(e);
      }
      finally {
        clearDraft();
      }
    }
    else {
      clearDraft();
      resetInputs();
    }
  };

  const addTag = (tagName: string): void => {
    const normalized = tagName.trim().toLowerCase();
    let newTags = [...selectedTags];
    if (normalized && !selectedTags.includes(normalized)) {
      newTags = [...selectedTags, normalized];
      setSelectedTags(newTags);
    }
    hasUserEdited.current = true;
    saveDraft(noteTitle, noteContent, noteUrl, newTags);
    setCurrentTag('');
    setShowTagDropdown(false);
  };

  const removeTag = (tagToRemove: string): void => {
    const newTags = selectedTags.filter(t => t !== tagToRemove);
    setSelectedTags(newTags);
    hasUserEdited.current = true;
    saveDraft(noteTitle, noteContent, noteUrl, newTags);
  };

  /**
   * Saves the note, either adding a new one or editing an existing one.
   *
   * @returns {Promise<boolean>} True if the note was saved successfully, false otherwise.
   */
  const saveNote = async (): Promise<boolean> => {
    setValidated(true);

    if (!noteTitle.trim() || !noteContent.trim()) {
      setErrorMessage(translateServerResponse('Please fill in all the fields', i18n.language));
      return false;
    }

    const finalTags = [...selectedTags];
    if (currentTag.trim()) {
      const normalized = currentTag.trim().toLowerCase();
      if (!finalTags.includes(normalized)) {
        finalTags.push(normalized);
      }
    }

    const payload: NoteResponse = {
      id: action === 'edit' ? noteId : 0,
      title: noteTitle,
      description: noteContent,
      url: noteUrl,
      tags: finalTags,
      lastUpdate: '',
      shared: false,
      shareToken: null
    };

    const saved = action === 'add'
      ? await addNote(payload)
      : await submitEditNote(payload);

    if (saved) {
      clearDraft();
      resetInputs();
      navigate('/home');
    }

    return saved;
  };

  /**
   * Handles the form submission.
   *
   * @param {React.SubmitEvent<HTMLFormElement>} event - The form submission event.
   */
  const handleSubmit = async (event: React.SubmitEvent<HTMLFormElement>): Promise<void> => {
    event.preventDefault();
    event.stopPropagation();
    setValidated(true);

    const form = event.currentTarget;
    if (!form.checkValidity()) {
      setErrorMessage(translateServerResponse('Please fill in all the fields', i18n.language));
      return;
    }

    await saveNote();
  };

  /**
   * Checks if the URL is for editing a note and loads the note data if it is.
   */
  const checkEditUrl = async (): Promise<void> => {
    if (params?.id) {
      try {
        const noteToEdit: NoteResponse = await api.getJSON(`${ApiConfig.notesUrl}/${params.id}`);
        setNoteFromServer(noteToEdit);
        setAction('edit');
        applyDraft();
      }
      catch (e) {
        handleError(e);
      }
    }
  };

  const checkCloneUrl = async (): Promise<void> => {
    const { search } = window.location;

    if (!search || !search.startsWith('?') || !search.includes('cloneFrom=')) {
      return;
    }

    const index = search.indexOf('=');
    if (search.length - index > 4) {
      return;
    }

    const idToClone = search.substring(index + 1);
    if (idToClone && !isNaN(parseInt(idToClone))) {
      try {
        const noteToClone: NoteResponse = await api.getJSON(`${ApiConfig.notesUrl}/${idToClone}`);
        setNoteFromServer(noteToClone);
      }
      catch (e) {
        handleError(e);
      }
    }
  };

  const setNoteFromServer = (noteData: NoteResponse) => {
    setNoteId(noteData.id);
    setNoteTitle(noteData.title);
    if (noteData.url) {
      setNoteUrl(noteData.url);
    }
    if (noteData.tags) {
      setSelectedTags(noteData.tags);
    }
    setNoteContent(noteData.description);
  };

  /**
   * Display the Markdown text in Markdown format on a modal.
   *
   * @param {React.MouseEvent<Element, MouseEvent>} e The mouse click event.
   */
  const previewMarkdown = (e: React.MouseEvent<Element, MouseEvent>): void => {
    e.preventDefault();
    e.stopPropagation();
    setShowPreviewMd(noteContent.length > 0);
  };

  /**
   * Closes the Markdown preview modal.
   */
  const handleCloseModal = (): void => setShowPreviewMd(false);

  useEffect(() => {
    loadTags();
    checkEditUrl();
    checkCloneUrl();

    if (!params?.id && !window.location.search.includes('cloneFrom=')) {
      applyDraft();
    }

    const handleClickOutside = (event: MouseEvent): void => {
      if (tagContainerRef.current && !tagContainerRef.current.contains(event.target as Node)) {
        setShowTagDropdown(false);
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
      if (debounceRef.current) clearTimeout(debounceRef.current);
    };
  }, []);

  return (
    <Container fluid>
      <ContentHeader
        h1TextRegular="Add"
        h1TextBold="Note"
        subtitle="Save your notes in plain text or Markdown format"
        h2BlackText="Create, Filter, and Easily Find"
        h2GreenText="Them"
      />

      <Row className="main-margin">
        <Col xs={12}>
          <Card>
            <Card.Body>
              <Card.Title>{t('note_form_title')}</Card.Title>

              <AlertError
                errorMessage={errorMessage}
                onClose={() => setErrorMessage('')}
              />

              {draftBanner && (
                <Alert variant="warning" dismissible onClose={() => setDraftBanner(false)}>
                  Draft restored from a previous session.
                  {' '}
                  <Alert.Link
                    href="#"
                    onClick={(e: React.MouseEvent) => {
                      e.preventDefault();
                      void handleDiscardDraft();
                    }}
                  >
                    Discard draft
                  </Alert.Link>
                </Alert>
              )}

              <Form
                noValidate
                validated={validated}
                onSubmit={handleSubmit}
                autoComplete="off"
              >
                <Row>
                  <Col xs={12} md={6} xxl={6}>
                    {/* Note title */}
                    <FormInput
                      labelText={t('note_form_title_label')}
                      iconName="JournalCheck"
                      required={true}
                      type="text"
                      name="note_title"
                      placeholder={t('note_form_title_placeholder')}
                      value={noteTitle}
                      onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                        setNoteTitle(e.target.value);
                        hasUserEdited.current = true;
                        saveDraft(e.target.value, noteContent, noteUrl, selectedTags);
                      }}
                    />
                  </Col>
                  <Col xs={12} md={6} xxl={6}>
                    {/* Note URL */}
                    <FormInput
                      labelText={t('task_form_url_label')}
                      iconName="At"
                      required={false}
                      type="text"
                      name="url"
                      placeholder={t('task_form_url_placeholder')}
                      value={noteUrl}
                      onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                        setNoteUrl(e.target.value);
                        hasUserEdited.current = true;
                        saveDraft(noteTitle, noteContent, e.target.value, selectedTags);
                      }}
                    />
                  </Col>
                </Row>
                <Row>
                  <Col xs={12}>
                    {/* Tag with suggestion dropdown */}
                    <Form.Group className="mb-3" ref={tagContainerRef} style={{ position: 'relative' }}>
                      <Form.Label>Tags</Form.Label>
                      <InputGroup>
                        <InputGroup.Text>
                          <Hash />
                        </InputGroup.Text>
                        <Form.Control
                          type="text"
                          name="tag"
                          placeholder="my-tag (Optional)"
                          value={currentTag}
                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                            setCurrentTag(e.target.value);
                            setShowTagDropdown(true);
                          }}
                          onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
                            if (e.key === 'Enter' && currentTag.trim()) {
                              e.preventDefault();
                              addTag(currentTag);
                            }
                          }}
                          onFocus={() => setShowTagDropdown(true)}
                          autoComplete="off"
                        />
                      </InputGroup>
                      <Form.Text className="text-muted">
                        Type a tag and press Enter
                      </Form.Text>
                      <div className="mb-2 d-flex flex-wrap gap-1">
                        {selectedTags.map(t => (
                          <Badge
                            key={t}
                            bg="warning"
                            text="dark"
                            className="p-2 mt-3"
                            style={{ cursor: 'pointer' }}
                            onClick={() => removeTag(t)}
                          >
                            #
                            {t}
                            {' '}
                            &times;
                          </Badge>
                        ))}
                      </div>
                      {showTagDropdown && tags.filter(t => t.toLowerCase().includes(currentTag.toLowerCase())).length > 0 && (
                        <ListGroup
                          style={{
                            position: 'absolute',
                            zIndex: 1000,
                            width: '100%',
                            maxHeight: '200px',
                            overflowY: 'auto',
                            left: 0
                          }}
                        >
                          {tags
                            .filter(t => t.toLowerCase().includes(currentTag.toLowerCase()))
                            .map(t => (
                              <ListGroup.Item
                                key={t}
                                action
                                variant="warning"
                                className="d-flex align-items-center gap-2"
                                onMouseDown={(e: React.MouseEvent) => {
                                  e.preventDefault();
                                  addTag(t);
                                }}
                              >
                                <i className="bi bi-tag"></i>
                                #
                                {t}
                              </ListGroup.Item>
                            ))}
                        </ListGroup>
                      )}
                    </Form.Group>
                  </Col>
                </Row>

                <Form.Group controlId="form_noteDescription">
                  <Form.Label>
                    {t('note_form_content_label')}
                    <small>
                      <a href="#" onClick={previewMarkdown}>
                        {' '}
                        Preview Markdown
                      </a>
                    </small>
                  </Form.Label>
                  <Form.Control
                    className="font-size-14"
                    as="textarea"
                    required={true}
                    size="lg"
                    rows={15}
                    name="note_description"
                    aria-describedby="noteDescriptionHelper"
                    placeholder={t('note_form_content_placeholder')}
                    value={noteContent}
                    onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
                      setNoteContent(e.target.value);
                      hasUserEdited.current = true;
                      saveDraft(noteTitle, e.target.value, noteUrl, selectedTags);
                    }}
                    data-testid="note-content-input-area"
                  />
                </Form.Group>

                <div className="d-flex justify-content-end gap-2 mt-3">
                  <button
                    type="submit"
                    className="home-new-item task-note-btn"
                  >
                    {t('note_form_submit')}
                  </button>

                  <button
                    type="button"
                    className="home-new-item-secondary task-note-btn"
                    onClick={() => {
                      clearDraft();
                      navigate('/home');
                    }}
                  >
                    Cancel
                  </button>
                </div>
              </Form>

            </Card.Body>
          </Card>
        </Col>
      </Row>

      <ModalMarkdown
        show={showPreviewMd}
        onHide={handleCloseModal}
        title={noteTitle}
        markdownText={noteContent}
        onSave={saveNote}
        saveButtonLabel={t('note_form_submit')}
      />
    </Container>
  );
}

export default NoteAdd;
