import React, { useCallback, useEffect, useMemo, useState, type FormEvent } from 'react';
import { Link } from 'react-router-dom';
import { EditIcon, IconX, CheckIcon, PlusIcon } from '../ui/icons';
import SelectionAssignmentMenu, {
  SelectionAssignmentMenuItem,
  type NormalizedSelectionAssignmentItem,
} from './SelectionAssignmentMenu';
import { getTagColorStyle } from '../utils/colors';
import {
  formatDate,
  toDateInputValue,
  toIssuedTimestamp,
} from '../utils/date';
import { describeDocumentSummary, type DocumentSummaryRow } from './documentSummary';

import { useFolderManager } from '../folders/FolderManagerContext';
import type { FolderId, Identifier, TagId } from '../types/identifiers';

interface TagEntry {
  id?: TagId;
  label?: string;
  color?: string | null;
}

interface CorrespondentEntry {
  id?: Identifier;
  name?: string;
  count?: number;
}

import type { Document } from '../types/documents';

interface TagSectionProps {
  tags?: TagEntry[];
  onRemove?: (tag: TagEntry) => void;
  onAdd?: (payload: { value: string; option?: unknown; input?: unknown }) => void;
  emptyMessage?: string;
  addPlaceholder?: string;
  addButtonLabel?: string;
  datalistOptions?: Array<SelectionAssignmentMenuItem | string>;
  className?: string;
}

interface CorrespondentSectionProps {
  entries?: CorrespondentEntry[];
  onRemove?: (entry: CorrespondentEntry) => void;
  onAdd?: (payload: { name: string; option?: unknown; input?: unknown }) => void;
  showCount?: boolean;
  addPlaceholder?: string;
  addButtonLabel?: string;
  datalistOptions?: Array<SelectionAssignmentMenuItem | string>;
  className?: string;
}

export interface DocumentSummarySectionProps {
  document?: Document | null;
  tagLookupById?: Map<TagId, TagEntry>;
  tagOptions?: SelectionAssignmentMenuItem[];
  onTagAdd?: (doc: Document, value: string, context?: { option?: unknown }) => void;
  onTagRemove?: (docId: Identifier | undefined, tagId: TagId | undefined) => void;
  correspondents?: CorrespondentEntry[];
  correspondentOptions?: SelectionAssignmentMenuItem[];
  onCorrespondentAdd?: (payload: { document: Document; name: string; option?: unknown }) => void;
  onCorrespondentRemove?: (payload: { documentId: Identifier | undefined; correspondentId: Identifier | undefined }) => void;
  onUpdateTitle?: (docId: Identifier | undefined, title: string) => Promise<boolean> | boolean;
  onUpdateIssued?: (docId: Identifier | undefined, timestamp: number | null) => Promise<boolean> | boolean;
  onFolderNavigate?: (folderId: FolderId | null) => void;
  layout?: 'default' | 'compact';
}

interface MetaItem {
  key: string;
  label: string;
  valueContent?: React.ReactNode | null;
  fallbackValue?: string | null;
  error?: string | null;
}

export const sortCorrespondents = (entries = []) =>
  entries
    .filter((entry) => entry && entry.name)
    .map(({ id, name, count }) => ({ id, name, count }))
    .sort((a, b) => a.name.localeCompare(b.name));

export const buildCorrespondentOptions = (entries = []) => {
  const seen = new Set();
  return entries.reduce((options, entry) => {
    const name = entry?.name?.trim?.() || '';
    if (!name) {
      return options;
    }
    const key = name.toLowerCase();
    if (seen.has(key)) {
      return options;
    }
    seen.add(key);
    options.push(name);
    return options;
  }, []);
};

const normalizeOptions = <T,>(options?: T[] | null): T[] => (Array.isArray(options) ? options : []);

interface QuickAddOption {
  id?: Identifier;
  label?: string;
  name?: string;
  [key: string]: unknown;
}

interface QuickAddEntry {
  id: Identifier | string;
  label: string;
  original: QuickAddOption | string;
}

const resolveOptionName = (source?: QuickAddOption | string | null): string => {
  if (!source) {
    return '';
  }
  if (typeof source === 'string') {
    return source.trim();
  }
  const raw = source.name ?? source.label ?? '';
  return `${raw}`.trim();
};

const normalizeQuickAddOption = (option?: QuickAddOption | string | null): QuickAddEntry | null => {
  if (option == null) {
    return null;
  }
  const label = (() => {
    if (typeof option === 'string') {
      return option.trim();
    }
    const sourceLabel = option.label ?? option.name ?? '';
    return `${sourceLabel}`.trim();
  })();
  if (!label) {
    return null;
  }
  return {
    id: typeof option !== 'string' && option.id ? option.id : label,
    label,
    original: option,
  };
};

export const TagSection: React.FC<TagSectionProps> = ({
  tags = [],
  onRemove,
  onAdd,
  emptyMessage = 'No tags yet.',
  addPlaceholder = 'Add or create tag',
  addButtonLabel = 'Add',
  datalistOptions = [],
  className,
}) => {
  const handleCreate = useCallback(
    (label: string) => onAdd?.({ value: label, input: null }),
    [onAdd],
  );

  const handleSelect = useCallback(
    (option: { label?: string; name?: string } | string | null) => {
      if (!onAdd) return;
      const label = resolveOptionName(option as QuickAddOption | string | null);
      if (!label) {
        return;
      }
      onAdd({ value: label, option });
    },
    [onAdd],
  );

  const normalizedOptions = useMemo(
    () =>
      normalizeOptions(datalistOptions)
        .map((option) => normalizeQuickAddOption(option))
        .filter((option): option is QuickAddEntry => Boolean(option)),
    [datalistOptions],
  );
  const containerClass = className ? `tag-list ${className}` : 'tag-list';
  const showQuickAdd = Boolean(onAdd);

  const assignmentItems = useMemo<SelectionAssignmentMenuItem[]>(() => {
    const map = new Map<string, SelectionAssignmentMenuItem>();

    normalizedOptions.forEach((option) => {
      const label = option?.label?.trim();
      if (!label) {
        return;
      }
      const key = label.toLowerCase();
      if (map.has(key)) {
        return;
      }
      map.set(key, {
        id: option.id ?? label,
        label,
        state: 'none',
        payload: option.original ?? { label },
      });
    });

    tags.forEach((tag) => {
      const label = tag?.label?.trim?.() || '';
      if (!label) {
        return;
      }
      const key = label.toLowerCase();
      const payload = { id: tag.id, label, color: tag.color ?? null };
      if (map.has(key)) {
        const entry = map.get(key);
        if (entry) {
          entry.state = 'all';
          entry.payload = payload;
        }
        return;
      }
      map.set(key, {
        id: tag.id ?? label,
        label,
        state: 'all',
        payload,
      });
    });

    return Array.from(map.values());
  }, [normalizedOptions, tags]);

  const handleAssignmentSelect = useCallback(
    (item: NormalizedSelectionAssignmentItem) => {
      if (!item) {
        return;
      }
      if (item.state === 'all' && onRemove) {
        const payload = item.payload && typeof item.payload === 'object' && 'id' in item.payload
          ? (item.payload as TagEntry)
          : tags.find((tag) => (tag.id ?? tag.label) === item.id) ?? { id: item.id, label: item.label };
        onRemove(payload);
        return;
      }
      const payload = item.payload ?? { label: item.label };
      handleSelect(payload);
    },
    [handleSelect, onRemove, tags],
  );

  return (
    <div className={containerClass}>
      {tags.map((tag) => {
        const key = tag.id ?? tag.label;
        const style = getTagColorStyle(tag.color);
        return (
          <span key={key} className="badge tag-chip" style={style || undefined}>
            <span className="tag-chip__label">{tag.label}</span>
            {onRemove ? (
              <button
                type="button"
                className="tag-chip__remove"
                onClick={() => onRemove(tag)}
                aria-label={`Remove tag ${tag.label}`}
              >
                <IconX className="icon-inline" aria-hidden="true" />
              </button>
            ) : null}
          </span>
        );
      })}
      {showQuickAdd ? (
        <SelectionAssignmentMenu
          label="Add tag"
          items={assignmentItems}
          placeholder={addPlaceholder}
          emptyMessage="No tags"
          createLabel={addButtonLabel}
          onToggle={handleAssignmentSelect}
          onCreate={handleCreate}
          showStateIndicators
          showCounts={false}
          positionStrategy="fixed"
          triggerClassName="quick-add__chip quick-add__trigger"
          triggerContent={<PlusIcon className="icon-inline" aria-hidden="true" />}
          closeOnSelection={false}
          freezeSortOnOpen
        />
      ) : null}
      {!tags.length && !showQuickAdd ? <span className="tag-list__empty meta">{emptyMessage}</span> : null}
    </div>
  );
};

export const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({
  entries = [],
  onRemove,
  onAdd,
  showCount = false,
  addPlaceholder = 'Add or create correspondent',
  addButtonLabel = 'Add',
  datalistOptions = [],
  className,
}) => {
  const handleCreate = useCallback(
    (name: string) => onAdd?.({ name, input: null }),
    [onAdd],
  );

  const normalizedOptions = useMemo(
    () =>
      normalizeOptions(datalistOptions)
        .map((option) => normalizeQuickAddOption(option))
        .filter((option): option is QuickAddEntry => Boolean(option)),
    [datalistOptions],
  );
  const hasEntries = entries && entries.length > 0;
  const showQuickAdd = Boolean(onAdd);
  const containerClass = className ? `correspondent-list ${className}` : 'correspondent-list';

  const assignmentItems = useMemo<SelectionAssignmentMenuItem[]>(() => {
    const map = new Map<string, SelectionAssignmentMenuItem>();

    normalizedOptions.forEach((option) => {
      const label = option?.label?.trim();
      if (!label) {
        return;
      }
      const key = label.toLowerCase();
      if (map.has(key)) {
        return;
      }
      map.set(key, {
        id: option.id ?? label,
        label,
        state: 'none',
        payload: option.original ?? { name: label },
      });
    });

    entries.forEach((entry) => {
      const label = entry?.name?.trim?.() || '';
      if (!label) {
        return;
      }
      const key = label.toLowerCase();
      const payload = { id: entry.id, name: label };
      if (map.has(key)) {
        const item = map.get(key);
        if (item) {
          item.state = 'all';
          item.payload = payload;
        }
        return;
      }
      map.set(key, {
        id: entry.id ?? label,
        label,
        state: 'all',
        payload,
      });
    });

    return Array.from(map.values());
  }, [normalizedOptions, entries]);

  const handleAssignmentSelect = useCallback(
    (item: NormalizedSelectionAssignmentItem) => {
      if (!item) {
        return;
      }
      if (item.state === 'all' && onRemove) {
        const payload = item.payload && typeof item.payload === 'object' && 'id' in item.payload
          ? (item.payload as CorrespondentEntry)
          : entries.find((entry) => (entry.id ?? entry.name) === item.id) ?? { id: item.id, name: item.label };
        onRemove(payload);
        return;
      }
      if (!onAdd) {
        return;
      }
      const source = (item.payload ?? item) as QuickAddOption | string | null;
      const resolvedName = resolveOptionName(source);
      if (!resolvedName) {
        return;
      }
      const payload = typeof source !== 'string'
        ? { ...source, name: resolvedName }
        : { id: null, name: resolvedName };
      onAdd({ name: resolvedName, option: payload, input: null });
    },
    [entries, onAdd, onRemove],
  );

  return (
    <div className={containerClass}>
      {hasEntries
        ? entries.map((entry) => {
          const key = entry.id ?? entry.name;
          return (
            <span key={key} className="correspondent-pill">
              <span className="correspondent-pill__label">
                {entry.name}
                {showCount && entry.count ? ` (${entry.count})` : ''}
              </span>
              {onRemove ? (
                <button
                  type="button"
                  className="correspondent-pill__remove"
                  onClick={() => onRemove(entry)}
                  aria-label={`Remove ${entry.name}`}
                >
                  <IconX className="icon-inline" aria-hidden="true" />
                </button>
              ) : null}
            </span>
          );
        })
        : !showQuickAdd && <span className="meta">No correspondents yet.</span>}
      {showQuickAdd ? (
        <SelectionAssignmentMenu
          label="Add correspondent"
          items={assignmentItems}
          placeholder={addPlaceholder}
          emptyMessage="No correspondents"
          createLabel={addButtonLabel}
          onToggle={handleAssignmentSelect}
          onCreate={handleCreate}
          showStateIndicators
          showCounts={false}
          positionStrategy="fixed"
          triggerClassName="quick-add__chip quick-add__trigger"
          triggerContent={<PlusIcon className="icon-inline" aria-hidden="true" />}
          closeOnSelection={false}
          freezeSortOnOpen
        />
      ) : null}
    </div>
  );
};

const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
  document,
  tagLookupById = new Map(),
  tagOptions = [],
  onTagAdd,
  onTagRemove,
  correspondents,
  correspondentOptions = [],
  onCorrespondentAdd,
  onCorrespondentRemove,
  onUpdateTitle,
  onUpdateIssued,
  onFolderNavigate,
  layout = 'default',
}) => {
  const folderManager = useFolderManager();
  const isCompactLayout = layout === 'compact';
  const summaryRows = useMemo(() => describeDocumentSummary(document), [document]);
  const issuedDateLabel = useMemo(
    () => formatDate(document?.issued_at, { fallback: null }),
    [document?.issued_at],
  );

  const editableTitle = Boolean(document && onUpdateTitle);
  const editableIssued = Boolean(document && onUpdateIssued);

  const resolvedTags = useMemo(() => {
    if (!Array.isArray(document?.tags)) {
      return [];
    }
    return document.tags.map((tag) => ({
      id: tag.id,
      label: tag.label,
      color: tag.color ?? tagLookupById.get(tag.id)?.color ?? null,
    })).sort((a, b) => {
      const labelA = (a.label || '').toLowerCase();
      const labelB = (b.label || '').toLowerCase();
      return labelA.localeCompare(labelB);
    });
  }, [document?.tags, tagLookupById]);

  const resolvedCorrespondents = useMemo(() => {
    if (Array.isArray(correspondents) && correspondents.length) {
      return correspondents;
    }
    return sortCorrespondents(document?.correspondents || []);
  }, [correspondents, document?.correspondents]);

  const extraSummaryRows = useMemo(() => {
    const rows: DocumentSummaryRow[] = [];
    const currentVersionNumber = document?.current_version?.version_number;
    if (currentVersionNumber != null) {
      rows.push({
        key: 'current-version',
        label: 'Current version',
        value: `#${currentVersionNumber}`,
      });
    }
    return rows;
  }, [document?.current_version?.version_number]);

  const resolvedFolderId = document?.folder_id ?? null;

  const [folderName, setFolderName] = useState<string | null>(() => folderManager.getNameSync(resolvedFolderId));

  useEffect(() => {
    let active = true;
    const cached = folderManager.getNameSync(resolvedFolderId);
    setFolderName(cached);
    if (!cached && resolvedFolderId != null) {
      folderManager.resolveName(resolvedFolderId).then((name) => {
        if (active) {
          setFolderName(name);
        }
      }).catch(() => { });
    }
    return () => {
      active = false;
    };
  }, [resolvedFolderId, folderManager]);

  const folderHref = resolvedFolderId == null ? '/documents' : `/documents/folder/${resolvedFolderId}`;

  const handleFolderClick = useCallback(
    (event: React.MouseEvent) => {
      if (!onFolderNavigate) {
        return;
      }
      event.preventDefault();
      onFolderNavigate(resolvedFolderId);
    },
    [onFolderNavigate, resolvedFolderId],
  );

  const [titleDraft, setTitleDraft] = useState('');
  const [titleSaving, setTitleSaving] = useState(false);
  const [titleError, setTitleError] = useState(null);
  const [isTitleEditing, setIsTitleEditing] = useState(false);

  const [issuedDraft, setIssuedDraft] = useState('');
  const [issuedSaving, setIssuedSaving] = useState(false);
  const [issuedError, setIssuedError] = useState(null);
  const [isIssuedEditing, setIsIssuedEditing] = useState(false);

  useEffect(() => {
    setIsTitleEditing(false);
    setTitleDraft('');
    setTitleError(null);
    setTitleSaving(false);

    setIsIssuedEditing(false);
    setIssuedDraft('');
    setIssuedError(null);
    setIssuedSaving(false);
  }, [document?.id]);

  const startTitleEdit = useCallback(() => {
    if (!editableTitle || !document) return;
    setIsTitleEditing(true);
    setTitleDraft(document.title || '');
    setTitleError(null);
  }, [document, editableTitle]);

  const cancelTitleEdit = useCallback(() => {
    setIsTitleEditing(false);
    setTitleDraft('');
    setTitleError(null);
    setTitleSaving(false);
  }, []);

  const submitTitleEdit = useCallback(
    async (event: FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      if (!editableTitle || !document || !onUpdateTitle) return;
      const trimmed = titleDraft.trim();
      if (!trimmed) {
        setTitleError('Title cannot be empty.');
        return;
      }
      setTitleSaving(true);
      try {
        const ok = await onUpdateTitle(document.id, trimmed);
        if (ok) {
          cancelTitleEdit();
        } else {
          setTitleError('Failed to update title.');
        }
      } finally {
        setTitleSaving(false);
      }
    },
    [cancelTitleEdit, document, editableTitle, onUpdateTitle, titleDraft],
  );

  const startIssuedEdit = useCallback(() => {
    if (!editableIssued || !document) return;
    setIsIssuedEditing(true);
    setIssuedDraft(toDateInputValue(document.issued_at));
    setIssuedError(null);
  }, [document, editableIssued]);

  const cancelIssuedEdit = useCallback(() => {
    setIsIssuedEditing(false);
    setIssuedDraft('');
    setIssuedError(null);
    setIssuedSaving(false);
  }, []);

  const submitIssuedEdit = useCallback(
    async (event: FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      if (!editableIssued || !document || !onUpdateIssued) return;
      const normalizedValue = issuedDraft ? toIssuedTimestamp(issuedDraft, document.issued_at) : null;
      setIssuedSaving(true);
      try {
        const ok = await onUpdateIssued(document.id, normalizedValue);
        if (ok) {
          cancelIssuedEdit();
        } else {
          setIssuedError('Failed to update issued date.');
        }
      } finally {
        setIssuedSaving(false);
      }
    },
    [cancelIssuedEdit, document, editableIssued, issuedDraft, onUpdateIssued],
  );

  if (!document) {
    return null;
  }

  const renderTitleEditForm = (extraClassName?: string) => (
    <form className={`doc-title-edit${extraClassName ? ` ${extraClassName}` : ''}`} onSubmit={submitTitleEdit}>
      <input
        value={titleDraft}
        onChange={(event) => {
          setTitleDraft(event.target.value);
          if (titleError) {
            setTitleError(null);
          }
        }}
        onKeyDown={(event) => {
          if (event.key === 'Escape') {
            event.preventDefault();
            cancelTitleEdit();
          }
        }}
        aria-label="Document title"
        autoFocus
        disabled={titleSaving}
      />
      <button type="submit" className="icon-button icon-button--accent" disabled={titleSaving} aria-label="Save title">
        <CheckIcon size={16} />
      </button>
      <button
        type="button"
        className="icon-button"
        onClick={cancelTitleEdit}
        disabled={titleSaving}
        aria-label="Cancel"
      >
        <IconX size={16} />
      </button>
    </form>
  );

  const titleMetaDisplay = editableTitle && isTitleEditing
    ? renderTitleEditForm('doc-title-edit--inline')
    : (
      <>
        <span className="detail-meta__value">{document?.title}</span>
        {editableTitle ? (
          <button
            type="button"
            className="icon-button"
            onClick={startTitleEdit}
            aria-label="Edit title"
            title="Edit title"
          >
            <EditIcon className="icon-inline" />
          </button>
        ) : null}
      </>
    );

  const issuedDisplay = editableIssued && isIssuedEditing ? (
    <form className="doc-issued-edit" onSubmit={submitIssuedEdit}>
      <input
        type="date"
        value={issuedDraft}
        onChange={(event) => {
          setIssuedDraft(event.target.value);
          if (issuedError) {
            setIssuedError(null);
          }
        }}
        aria-label="Issued on"
        disabled={issuedSaving}
      />
      <button type="submit" className="icon-button icon-button--accent" disabled={issuedSaving} aria-label="Save issued date">
        <CheckIcon size={16} />
      </button>
      <button
        type="button"
        className="icon-button"
        onClick={cancelIssuedEdit}
        disabled={issuedSaving}
        aria-label="Cancel"
      >
        <IconX size={16} />
      </button>
    </form>
  ) : (
    <>
      <span className="detail-meta__value">{issuedDateLabel || 'Not set'}</span>
      {editableIssued ? (
        <button
          type="button"
          className="icon-button"
          onClick={startIssuedEdit}
          aria-label={issuedDateLabel ? 'Edit issued date' : 'Set issued date'}
          title={issuedDateLabel ? 'Edit issued date' : 'Set issued date'}
        >
          <EditIcon className="icon-inline" />
        </button>
      ) : null}
    </>
  );

  const tagsValueContent = (
    <TagSection
      tags={resolvedTags}
      onRemove={
        onTagRemove
          ? (tag) => onTagRemove(document.id, tag.id)
          : undefined
      }
      onAdd={
        onTagAdd
          ? ({ value, option }) => onTagAdd(document, value, { option })
          : undefined
      }
      datalistOptions={tagOptions}
      className="document-summary__tags"
    />
  );

  const correspondentsValueContent = (
    <CorrespondentSection
      entries={resolvedCorrespondents}
      onRemove={
        onCorrespondentRemove
          ? (entry) =>
            onCorrespondentRemove({
              documentId: document.id,
              correspondentId: entry.id,
            })
          : undefined
      }
      onAdd={
        onCorrespondentAdd
          ? ({ name, option }) =>
            onCorrespondentAdd({
              document,
              name,
              option,
            })
          : undefined
      }
      showCount
      datalistOptions={correspondentOptions}
      className="document-summary__correspondents"
    />
  );

  const folderValueContent = (
    <Link
      className="document-summary__folder-link"
      to={folderHref}
      onClick={handleFolderClick}
    >
      {folderName}
    </Link>
  );

  const summaryRowOverrides = {
    title: { valueContent: titleMetaDisplay, error: titleError },
    issued: { valueContent: issuedDisplay, error: issuedError },
    tags: { valueContent: tagsValueContent },
    correspondents: { valueContent: correspondentsValueContent },
    folder: { valueContent: folderValueContent },
  } as Record<string, { valueContent?: React.ReactNode | null; error?: string | null }>;

  const baseRows: MetaItem[] = [...summaryRows, ...extraSummaryRows].map((row) => {
    const overrides = summaryRowOverrides[row.key] || {};
    return {
      key: row.key,
      label: row.label,
      valueContent: overrides.valueContent ?? null,
      fallbackValue: overrides.valueContent ? row.value : row.value,
      error: overrides.error ?? null,
    };
  });

  const allRows = baseRows;
  const summaryClass = `document-summary${isCompactLayout ? ' document-summary--compact' : ''}`;
  const sectionClass = `document-summary__section document-summary__meta${isCompactLayout ? ' document-summary__meta--compact' : ''}`;
  const listClass = `document-summary__details-list${isCompactLayout ? ' document-summary__details-list--meta' : ''}`;

  return (
    <div className={summaryClass}>
      <section className={sectionClass}>
        <dl className={listClass}>
          {allRows.map((item) => (
            <div key={item.key} className="document-summary__details-row">
              <dt>{item.label}</dt>
              <dd>
                {item.valueContent != null && item.valueContent !== ''
                  ? item.valueContent
                  : item.fallbackValue || '—'}
              </dd>
              {item.error ? <div className="status-inline error">{item.error}</div> : null}
            </div>
          ))}
        </dl>
      </section>
    </div>
  );
};

export default DocumentSummarySection;
