import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { FormEvent } from 'react';
import { EditIcon, IconX, PlusIcon } from '../ui/icons';
import SelectionAssignmentMenu, { SelectionAssignmentMenuItem } from './SelectionAssignmentMenu';
import { getTagColorStyle } from '../utils/colors';
import {
  formatDate,
  toDateInputValue,
  toIssuedTimestamp,
} from '../utils/date';
import { describeDocumentSummary } from './documentSummary';
import { isPlainObject } from '../utils/typeGuards';

type Identifier = string | number;

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

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

interface DocumentLike {
  id?: Identifier;
  title?: string;
  issued_at?: string | null;
  current_version?: { version_number?: number } | null;
  tags?: TagEntry[];
  correspondents?: CorrespondentEntry[];
  [key: string]: unknown;
}

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?: DocumentLike | null;
  tagLookupById?: Map<Identifier, TagEntry>;
  tagOptions?: SelectionAssignmentMenuItem[];
  onTagAdd?: (doc: DocumentLike, value: string, context?: { option?: unknown }) => void;
  onTagRemove?: (docId: Identifier | undefined, tagId: Identifier | undefined) => void;
  correspondents?: CorrespondentEntry[];
  correspondentOptions?: SelectionAssignmentMenuItem[];
  onCorrespondentAdd?: (payload: { document: DocumentLike; 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;
  layout?: 'default' | 'compact';
  detailItems?: Array<{ label?: string; value?: string }>;
}

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 (isPlainObject(source)) {
    const raw = source.name ?? source.label ?? '';
    return `${raw}`.trim();
  }
  return `${source}`.trim();
};

const normalizeQuickAddOption = (option: QuickAddOption | string | null | undefined): QuickAddEntry | null => {
  if (option == null) {
    return null;
  }
  const label = (() => {
    if (isPlainObject(option)) {
      const sourceLabel = option.label ?? option.name ?? '';
      return `${sourceLabel}`.trim();
    }
    return `${option}`.trim();
  })();
  if (!label) {
    return null;
  }
  return {
    id: isPlainObject(option) && 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: SelectionAssignmentMenuItem | null) => {
      if (!item) {
        return;
      }
      const payload = item.payload ?? { label: item.label };
      handleSelect(payload);
    },
    [handleSelect],
  );

  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={(
            <span className="quick-add__chip-label">
              <PlusIcon className="icon-inline" aria-hidden="true" /> Add tag
            </span>
          )}
        />
      ) : 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: SelectionAssignmentMenuItem | null) => {
      if (!onAdd || !item) {
        return;
      }
      const source = (item.payload ?? item) as QuickAddOption | string | null;
      const resolvedName = resolveOptionName(source);
      if (!resolvedName) {
        return;
      }
      const payload = isPlainObject(source)
        ? { ...source, name: resolvedName }
        : { id: null, name: resolvedName };
      onAdd({ name: resolvedName, option: payload, input: null });
    },
    [onAdd],
  );

  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={(
            <span className="quick-add__chip-label">
              <PlusIcon className="icon-inline" aria-hidden="true" /> Add correspondent
            </span>
          )}
        />
      ) : null}
    </div>
  );
};

const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
  document,
  tagLookupById = new Map(),
  tagOptions = [],
  onTagAdd,
  onTagRemove,
  correspondents,
  correspondentOptions = [],
  onCorrespondentAdd,
  onCorrespondentRemove,
  onUpdateTitle,
  onUpdateIssued,
  layout = 'default',
  detailItems = [],
}) => {
  const isCompactLayout = layout === 'compact';
  const summary = useMemo(() => {
    if (!document) {
      return {
        title: '',
        originalName: '',
        sizeLabel: '—',
        pageCount: null,
      };
    }
    return 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,
    }));
  }, [document?.tags, tagLookupById]);

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

  const metaRows = useMemo(() => {
    const rows: { key: string; label: string; value: string | null }[] = [];
    const currentVersionNumber = document?.current_version?.version_number;
    if (Number.isFinite(currentVersionNumber)) {
      rows.push({
        key: 'current-version',
        label: 'Current version',
        value: `#${currentVersionNumber}`,
      });
    }

    if (summary.sizeLabel && summary.sizeLabel !== '—') {
      rows.push({ key: 'size', label: 'Size', value: summary.sizeLabel });
    }

    if (Number.isFinite(summary.pageCount)) {
      rows.push({ key: 'pages', label: 'Pages', value: String(summary.pageCount) });
    }

    return rows;
  }, [document?.current_version?.version_number, summary]);

  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 TitleSection = () => (
    editableTitle && isTitleEditing ? (
      <form className="doc-title-edit" 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" disabled={titleSaving}>
          Save
        </button>
        <button
          type="button"
          className="secondary"
          onClick={cancelTitleEdit}
          disabled={titleSaving}
        >
          Cancel
        </button>
      </form>
    ) : (
      <>
        <h3 className="doc-title-row__title">{summary.title}</h3>
        {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" disabled={issuedSaving}>
        Save
      </button>
      <button
        type="button"
        className="secondary"
        onClick={cancelIssuedEdit}
        disabled={issuedSaving}
      >
        Cancel
      </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 metaItems = [
    {
      key: 'issued',
      label: 'Issued',
      valueContent: issuedDisplay,
      error: issuedError,
    },
    ...metaRows.map((row) => ({
      key: row.key,
      label: row.label,
      fallbackValue: row.value,
    })),
  ];

  const detailRows = Array.isArray(detailItems)
    ? detailItems.map((item, index) => ({
        key: `detail-${item?.label || index}`,
        label: item?.label || '—',
        fallbackValue: item?.value,
      }))
    : [];

  const compactRows = [...metaItems, ...detailRows];

  const renderTags = () => (
    <section className="document-summary__section document-summary__section--tags">
      <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"
      />
    </section>
  );

  const renderCorrespondents = () => (
    <section className="document-summary__section document-summary__section--correspondents">
      <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"
      />
    </section>
  );

  if (isCompactLayout) {
    return (
      <div className="document-summary document-summary--compact">
        <section className="document-summary__section document-summary__title-row">
          <TitleSection />
        </section>
        {titleError ? <div className="status-inline error">{titleError}</div> : null}
        {renderTags()}
        {renderCorrespondents()}
        {compactRows.length ? (
          <section className="document-summary__section document-summary__meta document-summary__meta--compact">
            <dl className="document-summary__details-list document-summary__details-list--meta">
              {compactRows.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>
        ) : null}
      </div>
    );
  }

  return (
    <div className="document-summary">
      <div className="doc-title-row">
        <div className="doc-title-row__primary">
          {editableTitle && isTitleEditing ? (
            <form className="doc-title-edit" 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" disabled={titleSaving}>
                Save
              </button>
              <button
                type="button"
                className="secondary"
                onClick={cancelTitleEdit}
                disabled={titleSaving}
              >
                Cancel
              </button>
            </form>
          ) : (
            <>
              <h3 className="doc-title-row__title">{summary.title}</h3>
              {editableTitle ? (
                <button
                  type="button"
                  className="icon-button"
                  onClick={startTitleEdit}
                  aria-label="Edit title"
                  title="Edit title"
                >
                  <EditIcon className="icon-inline" />
                </button>
              ) : null}
            </>
          )}
        </div>
      </div>

      {titleError ? <div className="status-inline error">{titleError}</div> : null}

      <div className="detail-meta">
        <div className="detail-meta__row">
          <span className="detail-meta__label">Issued:</span>
          {issuedDisplay}
        </div>
        {issuedError ? <div className="status-inline error">{issuedError}</div> : null}

        {metaRows.map((row) => (
          <div key={row.key} className="detail-meta__row">
            <span className="detail-meta__label">{row.label}:</span>
            <span className="detail-meta__value">{row.value}</span>
          </div>
        ))}
      </div>

      {renderTags()}
      {renderCorrespondents()}
    </div>
  );
};

export default DocumentSummarySection;
