import clsx from 'clsx';
import dayjs from 'dayjs';
import React, { useMemo, useRef, useState } from 'react';
import { MdEdit, MdDelete } from 'react-icons/md';

import { marked } from 'marked';
import { useEnv } from '@/context/EnvContext';
import { BookNote, HighlightColor } from '@/types/book';
import { useSettingsStore } from '@/store/settingsStore';
import { useReaderStore } from '@/store/readerStore';
import { useNotebookStore } from '@/store/notebookStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { eventDispatcher } from '@/utils/event';
import { isCfiInLocation } from '@/utils/cfi';
import { removeBookNoteOverlays } from '../../utils/annotatorUtil';
import TextButton from '@/components/TextButton';
import TextEditor, { TextEditorRef } from '@/components/TextEditor';

interface BooknoteItemProps {
  bookKey: string;
  item: BookNote;
  isNearest?: boolean;
  onClick?: () => void;
}

const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, isNearest, onClick }) => {
  const _ = useTranslation();
  const { envConfig } = useEnv();
  const { settings } = useSettingsStore();
  const { getConfig, saveConfig, updateBooknotes } = useBookDataStore();
  const { getProgress, getView, getViewsById } = useReaderStore();
  const { setNotebookEditAnnotation, setNotebookVisible } = useNotebookStore();

  const globalReadSettings = settings.globalReadSettings;
  const customColors = globalReadSettings.customHighlightColors;

  const { text, cfi, note } = item;
  const editorRef = useRef<TextEditorRef>(null);
  const [editorDraft, setEditorDraft] = useState(text || '');
  const [inlineEditMode, setInlineEditMode] = useState(false);
  const separatorWidth = useResponsiveSize(3);
  const size18 = useResponsiveSize(18);

  const progress = getProgress(bookKey);
  // Active highlight: keep visual "current" state but don't scroll from the
  // item itself anymore — the parent (virtualized) BooknoteView handles
  // scrolling via virtuosoRef.scrollToIndex, avoiding N getBoundingClientRect
  // calls when the list grows large.
  const isCurrent = useMemo(
    () => isCfiInLocation(cfi, progress?.location) || !!isNearest,
    [cfi, progress?.location, isNearest],
  );

  // marked.parse is heavy when called on every list scroll re-render across
  // hundreds of items. Cache by note text — note edits change item.note and
  // bust the cache automatically.
  const noteHtml = useMemo(() => (note ? marked.parse(note) : ''), [note]);

  // dayjs().fromNow() reformats every render; cache per createdAt.
  const createdAtLabel = useMemo(() => dayjs(item.createdAt).fromNow(), [item.createdAt]);

  const handleClickItem = (event: React.MouseEvent | React.KeyboardEvent) => {
    event.preventDefault();
    eventDispatcher.dispatch('navigate', { bookKey, cfi });

    onClick?.();
    getView(bookKey)?.goTo(cfi);
    if (note) {
      setNotebookVisible(true);
    }
  };

  const deleteNote = (note: BookNote) => {
    if (!bookKey) return;
    const config = getConfig(bookKey);
    if (!config) return;
    const { booknotes = [] } = config;
    booknotes.forEach((item) => {
      if (item.id === note.id) {
        item.deletedAt = Date.now();
        const views = getViewsById(bookKey.split('-')[0]!);
        views.forEach((view) => removeBookNoteOverlays(view, item));
      }
    });
    const updatedConfig = updateBooknotes(bookKey, booknotes);
    if (updatedConfig) {
      saveConfig(envConfig, bookKey, updatedConfig, settings);
    }
  };

  const editNote = (note: BookNote) => {
    setNotebookVisible(true);
    setNotebookEditAnnotation(note);
  };

  const editBookmark = () => {
    setEditorDraft(text || '');
    setInlineEditMode(true);
  };

  const handleSaveBookmark = () => {
    setInlineEditMode(false);
    const config = getConfig(bookKey);
    if (!config || !editorDraft) return;

    const { booknotes: annotations = [] } = config;
    const existingIndex = annotations.findIndex((annotation) => item.id === annotation.id);
    if (existingIndex === -1) return;
    annotations[existingIndex]!.updatedAt = Date.now();
    annotations[existingIndex]!.text = editorDraft;
    const updatedConfig = updateBooknotes(bookKey, annotations);
    if (updatedConfig) {
      saveConfig(envConfig, bookKey, updatedConfig, settings);
    }
  };

  if (inlineEditMode) {
    return (
      <div
        className={clsx(
          'border-base-300 content group relative my-2 cursor-pointer rounded-lg p-2',
          isCurrent ? 'bg-base-300/85 hover:bg-base-300' : 'hover:bg-base-300/55 bg-base-100',
          'transition-all duration-300 ease-in-out',
        )}
      >
        <div className='flex w-full'>
          <TextEditor
            className='!leading-normal'
            ref={editorRef}
            value={editorDraft}
            onChange={setEditorDraft}
            onSave={handleSaveBookmark}
            onEscape={() => setInlineEditMode(false)}
            spellCheck={false}
          />
        </div>
        <div className='flex justify-end space-x-3 p-2' dir='ltr'>
          <TextButton onClick={() => setInlineEditMode(false)}>{_('Cancel')}</TextButton>
          <TextButton onClick={handleSaveBookmark} disabled={!editorDraft}>
            {_('Save')}
          </TextButton>
        </div>
      </div>
    );
  }

  const isEditable = item.note || item.type === 'bookmark';

  return (
    <li
      // eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
      role='button'
      aria-current={isCurrent ? 'page' : undefined}
      className={clsx(
        'booknote-item border-base-300 content group relative my-2 cursor-pointer rounded-lg p-2',
        isCurrent
          ? 'bg-base-300/85 hover:bg-base-300 focus:bg-base-300'
          : 'hover:bg-base-300/55 focus:bg-base-300/55 bg-base-100',
        'transition-all duration-300 ease-in-out',
      )}
      tabIndex={0}
      onClick={handleClickItem}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          handleClickItem(e);
        } else {
          e.stopPropagation();
        }
      }}
    >
      <div
        className={clsx('min-h-4 p-0 transition-all duration-300 ease-in-out')}
        style={
          {
            '--top-override': '0.7rem',
            '--end-override': '0.3rem',
          } as React.CSSProperties
        }
      >
        {item.note && (
          <div
            className='content prose prose-sm font-size-sm'
            dir='auto'
            dangerouslySetInnerHTML={{ __html: noteHtml }}
          ></div>
        )}
        <div className='flex items-start'>
          {item.note && (
            <div
              className='me-2 mt-2.5 min-h-full self-stretch rounded-xl bg-gray-300'
              style={{
                minWidth: `${separatorWidth}px`,
              }}
            ></div>
          )}
          <div className={clsx('content font-size-sm line-clamp-3', item.note && 'mt-2')}>
            <span
              className={clsx(
                'booknote-text inline leading-normal',
                item.note && 'content font-size-xs text-base-content',
                (item.style === 'underline' || item.style === 'squiggly') &&
                  'underline decoration-2',
                item.style === 'highlight' && 'rounded-[4px] px-[2px] py-[1px]',
                item.style === 'squiggly' && 'decoration-wavy',
              )}
              style={
                {
                  ...(item.style === 'highlight'
                    ? {
                        backgroundColor: `color-mix(in srgb, ${customColors[item.color as HighlightColor] || item.color} calc(var(--overlayer-highlight-opacity, 0.3) * 100%), transparent)`,
                      }
                    : {}),
                  ...(item.style === 'underline' || item.style === 'squiggly'
                    ? {
                        textDecorationColor: `color-mix(in srgb, ${customColors[item.color as HighlightColor] || item.color} 80%, transparent)`,
                      }
                    : {}),
                } as React.CSSProperties
              }
            >
              {text || ''}
            </span>
          </div>
        </div>
      </div>
      {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
      <div
        className={clsx(
          'max-h-0 overflow-hidden p-0',
          'transition-[max-height] duration-300 ease-in-out',
          'group-focus-within:overflow-visible group-hover:overflow-visible',
          isEditable
            ? 'group-focus-within:max-h-12 group-hover:max-h-12'
            : 'group-focus-within:max-h-8 group-hover:max-h-8',
        )}
        style={
          {
            '--bottom-override': 0,
          } as React.CSSProperties
        }
        // This is needed to prevent the parent onClick from being triggered
        onClick={(e) => e.stopPropagation()}
      >
        <div
          className={clsx(
            'flex cursor-default items-center justify-between py-2',
            isEditable && 'flex-col',
          )}
        >
          <div className='flex w-full items-center gap-1 truncate'>
            <span className='truncate text-sm text-gray-500 sm:text-xs'>
              {item.page ? _('p {{page}}' + ' · ', { page: item.page }) : ''}
            </span>
            <span className='truncate text-sm text-gray-500 sm:text-xs'>{createdAtLabel}</span>
          </div>
          <div
            className={clsx('flex items-center justify-end gap-4', isEditable && 'w-full')}
            dir='ltr'
          >
            <button
              onClick={deleteNote.bind(null, item)}
              className='btn btn-ghost btn-xs p-0 text-red-500 opacity-0 transition duration-300 ease-in-out hover:bg-transparent group-focus-within:opacity-100 group-hover:opacity-100'
              aria-label={_('Delete')}
            >
              <MdDelete size={size18} />
            </button>

            {isEditable && (
              <button
                onClick={item.type === 'bookmark' ? editBookmark : editNote.bind(null, item)}
                className='btn btn-ghost btn-xs p-0 text-blue-500 opacity-0 transition duration-300 ease-in-out hover:bg-transparent group-focus-within:opacity-100 group-hover:opacity-100'
                aria-label={_('Edit')}
              >
                <MdEdit size={size18} />
              </button>
            )}
          </div>
        </div>
      </div>
    </li>
  );
};

// Memoize: BooknoteView re-renders on every progress tick / config change.
// Without React.memo each tick would re-render every visible note row even
// though their props are unchanged. Default shallow compare is enough since
// `item` and `onClick` are stable references from the parent's useMemo /
// useCallback.
export default React.memo(BooknoteItem);
