import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { CheckCircle2, ChevronDown, RotateCcw, Trash2 } from 'lucide-react';

import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { Separator } from '@/components/ui/separator';
import { Textarea } from '@/components/ui/textarea';
import {
  clearFailureSupplementDraft,
  clearFailureSupplementDrafts,
  getStoredFailureSupplementDrafts,
  hasFailureSupplementDraftValues,
  saveFailureSupplementDraft,
  type FailureSupplementDraftRecord,
} from '@/lib/standardizationFailureSupplementDraftStorage';
import { cn } from '@/lib/utils';
import {
  FAILURE_SUPPLEMENT_FIELDS,
  getRequiredSupplementFields,
  type FailureSupplementFieldKey,
  type FailureSupplementFields,
  type StandardizationFailureItem,
} from '@/services/standardizedService';

export interface FailureResolutionSubmission {
  failureId: string;
  item: StandardizationFailureItem;
  supplement: FailureSupplementFields;
}

export interface FailureResolutionActionResult {
  failedIds?: string[];
}

export interface FailureResolutionDialogProps {
  open: boolean;
  items: StandardizationFailureItem[];
  onOpenChange: (open: boolean) => void;
  onConfirmSuccessPool: (submissions: FailureResolutionSubmission[]) => void | FailureResolutionActionResult | Promise<void | FailureResolutionActionResult>;
  onRetryAiStandardization: (submissions: FailureResolutionSubmission[]) => void | FailureResolutionActionResult | Promise<void | FailureResolutionActionResult>;
  submitting?: boolean;
  retrying?: boolean;
}

interface FailureSupplementFormProps {
  item: StandardizationFailureItem;
  draft: FailureSupplementFields;
  restoredDraft?: FailureSupplementDraftRecord;
  errors?: string[];
  onDraftChange: (field: FailureSupplementFieldKey, value: string) => void;
}

function normalizeDraft(draft: FailureSupplementFields): FailureSupplementFields {
  return Object.fromEntries(
    Object.entries(draft).map(([key, value]) => [key, value?.trim() ?? '']),
  ) as FailureSupplementFields;
}

function getRequiredMissingLabels(item: StandardizationFailureItem, draft: FailureSupplementFields) {
  return getRequiredSupplementFields({
    missing_required_fields: item.missing_required_fields,
    failure_reason: item.failure_reason,
    human_fix_hint: item.human_fix_hint,
  })
    .filter((field) => field.required && !(draft[field.key] ?? '').trim())
    .map((field) => field.label);
}

function getDraftCompleteness(item: StandardizationFailureItem, draft: FailureSupplementFields) {
  const requiredFields = getRequiredSupplementFields({
    missing_required_fields: item.missing_required_fields,
    failure_reason: item.failure_reason,
    human_fix_hint: item.human_fix_hint,
  }).filter((field) => field.required);

  const total = requiredFields.length;
  const complete = requiredFields.filter((field) => Boolean((draft[field.key] ?? '').trim())).length;
  return { complete, total };
}

function formatQuantity(item: StandardizationFailureItem) {
  return `${item.quantity || 0} ${item.unit || ''}`.trim() || '-';
}

function getSuggestionText(item: StandardizationFailureItem) {
  return item.human_fix_hint || item.failure_reason || '暂无 AI 补充建议';
}

export function FailureSupplementForm({
  item,
  draft,
  restoredDraft,
  errors = [],
  onDraftChange,
}: FailureSupplementFormProps) {
  const requiredFields = getRequiredSupplementFields({
    missing_required_fields: item.missing_required_fields,
    failure_reason: item.failure_reason,
    human_fix_hint: item.human_fix_hint,
  });
  const requiredFieldMap = new Map(requiredFields.map((field) => [field.key, field]));

  return (
    <div className="flex min-h-0 flex-col gap-4">
      <div className="rounded-md border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900">
        <div className="mb-1 text-xs font-medium text-amber-800">AI 补充建议</div>
        <p className="whitespace-pre-wrap">{getSuggestionText(item)}</p>
      </div>

      {errors.length > 0 ? (
        <div className="rounded-md border border-destructive/30 bg-destructive/10 px-4 py-2 text-sm text-destructive">
          缺少：{errors.join('、')}
        </div>
      ) : null}

      {restoredDraft ? (
        <div className="rounded-md border border-sky-200 bg-sky-50 px-4 py-2 text-xs text-sky-800">
          {Date.parse(item.updated_at) > Date.parse(restoredDraft.savedAt)
            ? '失败记录已更新，已保留本地草稿'
            : '已恢复本地草稿'}
        </div>
      ) : null}

      <Collapsible>
        <CollapsibleTrigger asChild>
          <Button variant="ghost" size="sm" className="w-fit px-0 text-sm text-muted-foreground hover:bg-transparent">
            原始信息 <ChevronDown className="ml-1 h-3.5 w-3.5" />
          </Button>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <div data-testid="failure-source-details" className="grid gap-3 rounded-md border bg-muted/30 p-3 text-sm md:grid-cols-2">
            <div>
              <span className="text-muted-foreground">原始规格</span>
              <p className="mt-0.5 break-all">{item.original_spec || '-'}</p>
            </div>
            <div>
              <span className="text-muted-foreground">数量</span>
              <p className="mt-0.5">{formatQuantity(item)}</p>
            </div>
            <div>
              <span className="text-muted-foreground">发票种类</span>
              <p className="mt-0.5">{item.invoice_type || '-'}</p>
            </div>
            <div>
              <span className="text-muted-foreground">收货地址</span>
              <p className="mt-0.5 break-all">{item.source_snapshot.delivery_address || '-'}</p>
            </div>
            <div className="md:col-span-2">
              <span className="text-muted-foreground">来源备注</span>
              <p className="mt-0.5 whitespace-pre-wrap">{item.source_snapshot.remarks || '-'}</p>
            </div>
          </div>
        </CollapsibleContent>
      </Collapsible>

      <div className="grid gap-3 md:grid-cols-2">
        {FAILURE_SUPPLEMENT_FIELDS.map((field) => {
          const requiredField = requiredFieldMap.get(field.key);
          const label = requiredField?.displayLabel ?? field.label;
          return (
            <div key={field.key} className="space-y-1.5">
              <Label htmlFor={`failure-supplement-${item.id}-${field.key}`} className="text-xs">
                {label}
              </Label>
              <Textarea
                id={`failure-supplement-${item.id}-${field.key}`}
                value={draft[field.key] ?? ''}
                onChange={(event) => onDraftChange(field.key, event.target.value)}
                className="min-h-20 resize-y"
              />
            </div>
          );
        })}
      </div>
    </div>
  );
}

export function FailureResolutionDialog({
  open,
  items,
  onOpenChange,
  onConfirmSuccessPool,
  onRetryAiStandardization,
  submitting = false,
  retrying = false,
}: FailureResolutionDialogProps) {
  const [activeId, setActiveId] = useState<string>('');
  const [drafts, setDrafts] = useState<Record<string, FailureSupplementFields>>({});
  const [restoredDrafts, setRestoredDrafts] = useState<Record<string, FailureSupplementDraftRecord>>({});
  const [dirtyDraftIds, setDirtyDraftIds] = useState<Set<string>>(new Set());
  const [saveStatusById, setSaveStatusById] = useState<Record<string, 'idle' | 'saving' | 'saved' | 'failed'>>({});
  const [remainingIds, setRemainingIds] = useState<string[]>([]);
  const [errorsById, setErrorsById] = useState<Record<string, string[]>>({});
  const [localSubmitting, setLocalSubmitting] = useState(false);
  const clearedDraftIdsRef = useRef(new Set<string>());
  const isBusy = submitting || retrying || localSubmitting;

  useEffect(() => {
    if (!open) {
      return;
    }

    const nextIds = items.map((item) => item.id);
    const storedDrafts = getStoredFailureSupplementDrafts(items);
    setRemainingIds(nextIds);
    setActiveId((current) => (current && nextIds.includes(current) ? current : nextIds[0] ?? ''));
    setErrorsById({});
    setRestoredDrafts(storedDrafts);
    setDirtyDraftIds(new Set());
    setSaveStatusById({});
    clearedDraftIdsRef.current = new Set();
    setDrafts(() => {
      const next: Record<string, FailureSupplementFields> = {};
      for (const item of items) {
        next[item.id] = storedDrafts[item.id]?.values ?? {};
      }
      return next;
    });
  }, [items, open]);

  const remainingItems = useMemo(() => {
    const itemMap = new Map(items.map((item) => [item.id, item]));
    return remainingIds.map((id) => itemMap.get(id)).filter((item): item is StandardizationFailureItem => Boolean(item));
  }, [items, remainingIds]);

  const activeItem = remainingItems.find((item) => item.id === activeId) ?? remainingItems[0] ?? null;

  const persistDraft = useCallback((failureId: string, draft: FailureSupplementFields) => {
    const item = items.find((candidate) => candidate.id === failureId);
    if (!item) {
      return;
    }

    const record = saveFailureSupplementDraft(item, draft);
    setRestoredDrafts((current) => {
      const next = { ...current };
      if (record) {
        next[failureId] = record;
      } else {
        delete next[failureId];
      }
      return next;
    });
  }, [items]);

  function flushDraft(failureId: string) {
    const draft = drafts[failureId] ?? {};
    if (!hasFailureSupplementDraftValues(draft)) {
      return;
    }

    try {
      persistDraft(failureId, draft);
      setSaveStatusById((current) => ({ ...current, [failureId]: 'saved' }));
      setDirtyDraftIds((current) => {
        const next = new Set(current);
        next.delete(failureId);
        return next;
      });
    } catch {
      setSaveStatusById((current) => ({ ...current, [failureId]: 'failed' }));
    }
  }

  useEffect(() => {
    if (!open || dirtyDraftIds.size === 0) {
      return;
    }

    const timeoutId = window.setTimeout(() => {
      const failedIds = new Set<string>();

      for (const failureId of dirtyDraftIds) {
        try {
          persistDraft(failureId, drafts[failureId] ?? {});
          setSaveStatusById((current) => ({ ...current, [failureId]: 'saved' }));
        } catch {
          failedIds.add(failureId);
          setSaveStatusById((current) => ({ ...current, [failureId]: 'failed' }));
        }
      }

      setDirtyDraftIds((current) => {
        const next = new Set<string>();
        for (const failureId of current) {
          if (failedIds.has(failureId)) {
            next.add(failureId);
          }
        }
        return next;
      });
    }, 500);

    return () => window.clearTimeout(timeoutId);
  }, [dirtyDraftIds, drafts, open, persistDraft]);

  useEffect(() => {
    if (!open) {
      return;
    }

    const flushAll = () => {
      for (const item of remainingItems) {
        const draft = drafts[item.id] ?? {};
        if (!clearedDraftIdsRef.current.has(item.id) && hasFailureSupplementDraftValues(draft)) {
          saveFailureSupplementDraft(item, draft);
        }
      }
    };

    const handleVisibilityChange = () => {
      if (document.visibilityState === 'hidden') {
        flushAll();
      }
    };

    window.addEventListener('pagehide', flushAll);
    window.addEventListener('beforeunload', flushAll);
    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      flushAll();
      window.removeEventListener('pagehide', flushAll);
      window.removeEventListener('beforeunload', flushAll);
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, [drafts, open, remainingItems]);

  function updateDraft(failureId: string, field: FailureSupplementFieldKey, value: string) {
    setDrafts((current) => ({
      ...current,
      [failureId]: {
        ...(current[failureId] ?? {}),
        [field]: value,
      },
    }));
    setRestoredDrafts((current) => {
      if (!current[failureId]) {
        return current;
      }

      const next = { ...current };
      delete next[failureId];
      return next;
    });
    setDirtyDraftIds((current) => new Set(current).add(failureId));
    setSaveStatusById((current) => ({ ...current, [failureId]: 'saving' }));
    clearedDraftIdsRef.current.delete(failureId);
    setErrorsById((current) => {
      if (!current[failureId]?.length) {
        return current;
      }

      const item = items.find((candidate) => candidate.id === failureId);
      if (!item) {
        return current;
      }

      const nextMissing = getRequiredMissingLabels(item, {
        ...(drafts[failureId] ?? {}),
        [field]: value,
      });
      return { ...current, [failureId]: nextMissing };
    });
  }

  function selectActiveItem(failureId: string) {
    if (activeId) {
      flushDraft(activeId);
    }
    setActiveId(failureId);
  }

  function clearDraft(item: StandardizationFailureItem) {
    clearedDraftIdsRef.current.add(item.id);
    clearFailureSupplementDraft(item);
    setDrafts((current) => ({ ...current, [item.id]: {} }));
    setRestoredDrafts((current) => {
      const next = { ...current };
      delete next[item.id];
      return next;
    });
    setDirtyDraftIds((current) => {
      const next = new Set(current);
      next.delete(item.id);
      return next;
    });
    setSaveStatusById((current) => ({ ...current, [item.id]: 'idle' }));
    setErrorsById((current) => {
      const next = { ...current };
      delete next[item.id];
      return next;
    });
  }

  async function submit(action: 'success' | 'retry') {
    const valid: FailureResolutionSubmission[] = [];
    const invalid: Record<string, string[]> = {};

    for (const item of remainingItems) {
      const draft = normalizeDraft(drafts[item.id] ?? {});
      const missingLabels = getRequiredMissingLabels(item, draft);

      if (missingLabels.length > 0) {
        invalid[item.id] = missingLabels;
      } else {
        valid.push({
          failureId: item.id,
          item,
          supplement: draft,
        });
      }
    }

    setErrorsById(invalid);

    if (valid.length > 0) {
      setLocalSubmitting(true);
      let actionResult: void | FailureResolutionActionResult;
      try {
        if (action === 'success') {
          actionResult = await onConfirmSuccessPool(valid);
        } else {
          actionResult = await onRetryAiStandardization(valid);
        }
      } finally {
        setLocalSubmitting(false);
      }

      for (const failedId of actionResult?.failedIds ?? []) {
        invalid[failedId] = invalid[failedId] ?? ['提交失败'];
      }

      const failedIds = new Set(actionResult?.failedIds ?? []);
      const successfulItems = valid
        .filter((submission) => !failedIds.has(submission.failureId))
        .map((submission) => submission.item);
      for (const item of successfulItems) {
        clearedDraftIdsRef.current.add(item.id);
      }
      clearFailureSupplementDrafts(successfulItems);
      setDrafts((current) => {
        const next = { ...current };
        for (const item of successfulItems) {
          next[item.id] = {};
        }
        return next;
      });
      setDirtyDraftIds((current) => {
        const next = new Set(current);
        for (const item of successfulItems) {
          next.delete(item.id);
        }
        return next;
      });
      setRestoredDrafts((current) => {
        const next = { ...current };
        for (const item of successfulItems) {
          delete next[item.id];
        }
        return next;
      });
    }

    const invalidIds = Object.keys(invalid);
    if (invalidIds.length > 0) {
      setRemainingIds(invalidIds);
      setActiveId(invalidIds[0]);
      return;
    }

    onOpenChange(false);
  }

  return (
    <Dialog open={open} onOpenChange={(nextOpen) => !isBusy && onOpenChange(nextOpen)}>
      <DialogContent className="flex max-h-[86vh] max-w-6xl flex-col gap-0 overflow-hidden p-0">
        <DialogHeader className="border-b px-5 py-4">
          <DialogTitle className="text-primary">标准化失败人工补充</DialogTitle>
          <DialogDescription>待处理 {remainingItems.length} 条</DialogDescription>
        </DialogHeader>

        <div className="grid min-h-0 flex-1 grid-cols-1 lg:grid-cols-[18rem_minmax(0,1fr)]">
          <aside className="min-h-0 border-b bg-muted/20 lg:border-b-0 lg:border-r">
            <div className="max-h-48 space-y-2 overflow-y-auto p-3 lg:max-h-[60vh]">
              {remainingItems.map((item) => {
                const completeness = getDraftCompleteness(item, drafts[item.id] ?? {});
                const missing = errorsById[item.id] ?? [];
                return (
                  <button
                    key={item.id}
                    type="button"
                    onClick={() => selectActiveItem(item.id)}
                    className={cn(
                      'w-full rounded-md border bg-background p-3 text-left text-sm transition-colors hover:border-primary/50',
                      item.id === activeItem?.id ? 'border-primary shadow-sm' : 'border-border',
                    )}
                  >
                    <div className="flex items-start justify-between gap-2">
                      <span className="font-medium text-foreground">{item.original_name || item.standardized_name || item.id}</span>
                      <Badge variant={missing.length > 0 ? 'destructive' : 'outline'} className="shrink-0">
                        {missing.length > 0 ? '待补充' : `${completeness.complete}/${completeness.total}`}
                      </Badge>
                    </div>
                    {(restoredDrafts[item.id] || hasFailureSupplementDraftValues(drafts[item.id] ?? {})) ? (
                      <Badge variant="outline" className="mt-2 border-sky-200 bg-sky-50 text-sky-700">
                        有草稿
                      </Badge>
                    ) : null}
                    <p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
                      {item.missing_required_fields.length > 0 ? item.missing_required_fields.join('、') : item.failure_reason || '-'}
                    </p>
                    {missing.length > 0 ? <p className="mt-2 text-xs text-destructive">缺少：{missing.join('、')}</p> : null}
                  </button>
                );
              })}
            </div>
          </aside>

          <section className="min-h-0 overflow-y-auto p-5">
            {activeItem ? (
              <div className="space-y-4">
                <div className="flex items-start justify-between gap-3">
                  <div>
                    <h2 className="text-xl font-semibold text-foreground">{activeItem.original_name || activeItem.standardized_name || activeItem.id}</h2>
                    <div className="mt-1 flex flex-wrap items-center gap-2">
                      <p className="font-mono text-xs text-muted-foreground">{activeItem.source_material_id || activeItem.id}</p>
                      {saveStatusById[activeItem.id] === 'saving' ? <span className="text-xs text-muted-foreground">保存中</span> : null}
                      {saveStatusById[activeItem.id] === 'saved' ? <span className="text-xs text-emerald-700">已自动保存</span> : null}
                      {saveStatusById[activeItem.id] === 'failed' ? <span className="text-xs text-destructive">保存失败</span> : null}
                    </div>
                  </div>
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    className="shrink-0 text-xs text-muted-foreground"
                    disabled={!hasFailureSupplementDraftValues(drafts[activeItem.id] ?? {}) && !restoredDrafts[activeItem.id]}
                    onClick={() => clearDraft(activeItem)}
                  >
                    <Trash2 className="mr-1 h-3.5 w-3.5" />清空草稿
                  </Button>
                </div>
                <FailureSupplementForm
                  item={activeItem}
                  draft={drafts[activeItem.id] ?? {}}
                  restoredDraft={restoredDrafts[activeItem.id]}
                  errors={errorsById[activeItem.id]}
                  onDraftChange={(field, value) => updateDraft(activeItem.id, field, value)}
                />
              </div>
            ) : (
              <div className="flex min-h-64 items-center justify-center text-sm text-muted-foreground">没有可处理的失败记录</div>
            )}
          </section>
        </div>

        <Separator />
        <DialogFooter className="gap-2 px-5 py-4">
          <Button variant="outline" onClick={() => onOpenChange(false)} disabled={isBusy}>
            取消
          </Button>
          <Button variant="outline" onClick={() => void submit('retry')} disabled={remainingItems.length === 0 || isBusy}>
            <RotateCcw className="mr-1 h-3.5 w-3.5" />重新 AI 标准化
          </Button>
          <Button onClick={() => void submit('success')} disabled={remainingItems.length === 0 || isBusy}>
            <CheckCircle2 className="mr-1 h-3.5 w-3.5" />确认入成功池
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
