import { useEffect, useState } from 'react';
import { Button, Image, Input, Modal, Space, Tag, Typography, message } from 'antd';
import type {
  StorePackageAuditDetailDto,
  StorePackageItemDto,
  StorePackageViewDto,
} from '@dukang/shared-types';
import { normalizeStorePackageImageUrls } from '@dukang/shared-types';
import { request } from '../lib/api';
import { notifyPackageAuditChanged } from '../lib/admin-events';
import { fmtTime } from '../lib/constants';

const HQ_PACKAGE_STATUS_LABELS: Record<string, string> = {
  PENDING: '待审核',
  APPROVED: '已通过',
  REJECTED: '已驳回',
};

function packageKey(pkg: StorePackageItemDto | StorePackageViewDto, index: number) {
  const name = String(pkg.name ?? '').trim();
  return name ? `name:${name}` : `idx:${index}`;
}

function imageSignature(pkg: StorePackageItemDto | StorePackageViewDto) {
  return normalizeStorePackageImageUrls(pkg).join('|');
}

type FieldChange = { label: string; old: string; now: string; kind: 'text' | 'value' };

function diffText(a: string, b: string): Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> {
  const m = a.length;
  const n = b.length;
  const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
  for (let i = m - 1; i >= 0; i--) {
    for (let j = n - 1; j >= 0; j--) {
      dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
    }
  }
  const raw: Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> = [];
  let i = 0;
  let j = 0;
  while (i < m && j < n) {
    if (a[i] === b[j]) {
      raw.push({ type: 'equal', text: a[i] });
      i++;
      j++;
    } else if (dp[i + 1][j] >= dp[i][j + 1]) {
      raw.push({ type: 'delete', text: a[i] });
      i++;
    } else {
      raw.push({ type: 'insert', text: b[j] });
      j++;
    }
  }
  while (i < m) raw.push({ type: 'delete', text: a[i++] });
  while (j < n) raw.push({ type: 'insert', text: b[j++] });
  const merged: Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> = [];
  for (const s of raw) {
    const last = merged[merged.length - 1];
    if (last && last.type === s.type) last.text += s.text;
    else merged.push({ ...s });
  }
  return merged;
}

function fieldChanges(
  live: StorePackageItemDto | StorePackageViewDto,
  proposed: StorePackageItemDto | StorePackageViewDto,
): FieldChange[] {
  const changes: FieldChange[] = [];
  const text = (v: string | number | null | undefined) => (v ?? '').toString().trim();
  const pushText = (label: string, oldV: string, newV: string) => {
    if (oldV !== newV) changes.push({ label, old: oldV, now: newV, kind: 'text' });
  };
  const pushValue = (label: string, oldV: string, newV: string) => {
    if (oldV !== newV) changes.push({ label, old: oldV || '（空）', now: newV || '（空）', kind: 'value' });
  };
  pushValue('价格', `¥${text(live.price)}`, `¥${text(proposed.price)}`);
  pushText('套餐名称', text(live.name), text(proposed.name));
  pushText('菜品内容', text(live.dishes), text(proposed.dishes));
  pushText('可用时间', text(live.usableTime), text(proposed.usableTime));
  pushText('其他说明', text(live.otherNotes), text(proposed.otherNotes));
  const liveImgs = normalizeStorePackageImageUrls(live);
  const proposedImgs = normalizeStorePackageImageUrls(proposed);
  if (imageSignature(live) !== imageSignature(proposed)) {
    changes.push({ label: '图片', old: `${liveImgs.length} 张`, now: `${proposedImgs.length} 张`, kind: 'value' });
  }
  return changes;
}

function diffPackages(live: StorePackageViewDto[], proposed: StorePackageItemDto[]) {
  const liveMap = new Map(live.map((p, i) => [packageKey(p, i), p]));
  const proposedMap = new Map(proposed.map((p, i) => [packageKey(p, i), p]));
  const keys = new Set([...liveMap.keys(), ...proposedMap.keys()]);
  const rows: Array<{
    key: string;
    change: 'added' | 'removed' | 'changed' | 'unchanged';
    live?: StorePackageViewDto;
    proposed?: StorePackageItemDto;
    changes?: FieldChange[];
  }> = [];

  for (const key of keys) {
    const l = liveMap.get(key);
    const p = proposedMap.get(key);
    if (l && !p) {
      rows.push({ key, change: 'removed', live: l });
    } else if (!l && p) {
      rows.push({ key, change: 'added', proposed: p });
    } else if (l && p) {
      const changed =
        l.price !== p.price ||
        l.dishes !== p.dishes ||
        (l.usableTime ?? '') !== (p.usableTime ?? '') ||
        (l.otherNotes ?? '') !== (p.otherNotes ?? '') ||
        imageSignature(l) !== imageSignature(p);
      rows.push({
        key,
        change: changed ? 'changed' : 'unchanged',
        live: l,
        proposed: p,
        changes: changed ? fieldChanges(l, p) : undefined,
      });
    }
  }
  return rows;
}

const CHANGE_LABELS = {
  added: { text: '新增', color: 'green' },
  removed: { text: '删除', color: 'red' },
  changed: { text: '变更', color: 'orange' },
  unchanged: { text: '未变', color: 'default' },
} as const;

function TextDiff({ oldText, newText }: { oldText: string; newText: string }) {
  const segs = diffText(oldText, newText);
  return (
    <div style={{ marginTop: 2 }}>
      <div style={{ lineHeight: 1.6 }}>
        <Typography.Text type="secondary">原：</Typography.Text>
        {segs
          .filter((s) => s.type !== 'insert')
          .map((s, idx) =>
            s.type === 'delete' ? (
              <Typography.Text key={idx} delete style={{ color: '#cf1322' }}>
                {s.text || '（空）'}
              </Typography.Text>
            ) : (
              <Typography.Text key={idx}>{s.text}</Typography.Text>
            ),
          )}
      </div>
      <div style={{ lineHeight: 1.6 }}>
        <Typography.Text type="secondary">新：</Typography.Text>
        {segs
          .filter((s) => s.type !== 'delete')
          .map((s, idx) =>
            s.type === 'insert' ? (
              <Typography.Text key={idx} style={{ color: '#389e0d' }}>
                {s.text || '（空）'}
              </Typography.Text>
            ) : (
              <Typography.Text key={idx}>{s.text}</Typography.Text>
            ),
          )}
      </div>
    </div>
  );
}

function PackageDetailCard({
  title,
  pkg,
  change,
  changes,
  imageSize = 72,
}: {
  title?: string;
  pkg: StorePackageItemDto | StorePackageViewDto;
  change?: keyof typeof CHANGE_LABELS;
  changes?: FieldChange[];
  imageSize?: number;
}) {
  const images = normalizeStorePackageImageUrls(pkg);
  const meta = change ? CHANGE_LABELS[change] : null;
  return (
    <div
      style={{
        marginBottom: 12,
        padding: 12,
        border: '1px solid #f0f0f0',
        borderRadius: 8,
        background: '#fafafa',
      }}
    >
      <Space style={{ marginBottom: 8 }} wrap>
        {title ? <Typography.Text type="secondary">{title}</Typography.Text> : null}
        {meta ? <Tag color={meta.color}>{meta.text}</Tag> : null}
      </Space>
      <div style={{ marginBottom: 8 }}>
        <strong>{pkg.name}</strong>
        <span style={{ marginLeft: 8 }}>¥{pkg.price}</span>
      </div>
      <Typography.Paragraph className="admin-package-audit-text" style={{ marginBottom: 8 }}>
        {pkg.dishes || '—'}
      </Typography.Paragraph>
      {pkg.usableTime ? (
        <Typography.Paragraph type="secondary" className="admin-package-audit-text" style={{ marginBottom: 4 }}>
          可用时间：{pkg.usableTime}
        </Typography.Paragraph>
      ) : null}
      {pkg.otherNotes ? (
        <Typography.Paragraph type="secondary" className="admin-package-audit-text" style={{ marginBottom: 8 }}>
          其他说明：{pkg.otherNotes}
        </Typography.Paragraph>
      ) : null}
      {images.length ? (
        <Image.PreviewGroup>
          <Space wrap size={8}>
            {images.map((url) => (
              <Image key={url} src={url} width={imageSize} height={imageSize} style={{ objectFit: 'cover', borderRadius: 4 }} />
            ))}
          </Space>
        </Image.PreviewGroup>
      ) : (
        <Typography.Text type="secondary">无套餐图片</Typography.Text>
      )}
      {changes && changes.length ? (
        <div
          style={{
            marginTop: 8,
            padding: 8,
            background: '#fff7e6',
            border: '1px solid #ffe7ba',
            borderRadius: 6,
          }}
        >
          <Typography.Text strong style={{ fontSize: 12 }}>
            变更明细
          </Typography.Text>
          <ul style={{ margin: '6px 0 0', paddingLeft: 18 }}>
            {changes.map((c) => (
              <li key={c.label} style={{ marginBottom: 6 }}>
                <Typography.Text type="secondary">{c.label}：</Typography.Text>
                {c.kind === 'text' ? (
                  <TextDiff oldText={c.old} newText={c.now} />
                ) : (
                  <>
                    <Typography.Text delete type="secondary">
                      {c.old}
                    </Typography.Text>
                    <Typography.Text type="secondary"> → </Typography.Text>
                    <Typography.Text strong>{c.now}</Typography.Text>
                  </>
                )}
              </li>
            ))}
          </ul>
        </div>
      ) : null}
    </div>
  );
}

export type StorePackageAuditPanelProps = {
  requestId: string;
  /** 是否在面板顶部显示通过/驳回（抽屉 extra 另有按钮时可关） */
  showActions?: boolean;
  fullscreen?: boolean;
  onAudited?: () => void;
  onDetailLoaded?: (detail: StorePackageAuditDetailDto | null) => void;
};

/** 套餐变更对比 + 通过/驳回（审核通知页与门店详情抽屉共用） */
export default function StorePackageAuditPanel({
  requestId,
  showActions = true,
  fullscreen = false,
  onAudited,
  onDetailLoaded,
}: StorePackageAuditPanelProps) {
  const [loading, setLoading] = useState(false);
  const [detail, setDetail] = useState<StorePackageAuditDetailDto | null>(null);
  const [rejectOpen, setRejectOpen] = useState(false);
  const [rejectReason, setRejectReason] = useState('');
  const [auditing, setAuditing] = useState(false);

  async function load() {
    setLoading(true);
    try {
      const data = await request<StorePackageAuditDetailDto>(`/admin/store-package-audits/${requestId}`);
      setDetail(data);
      onDetailLoaded?.(data);
    } catch (e) {
      message.error(e instanceof Error ? e.message : '加载套餐审核详情失败');
      setDetail(null);
      onDetailLoaded?.(null);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void load();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [requestId]);

  async function audit(action: 'APPROVE' | 'REJECT', reason?: string) {
    if (!detail) return;
    setAuditing(true);
    try {
      await request(`/admin/store-package-audits/${detail.id}/audit`, {
        method: 'PUT',
        body: JSON.stringify(action === 'REJECT' ? { action, rejectReason: reason } : { action }),
      });
      message.success(action === 'APPROVE' ? '套餐已通过' : '套餐已驳回');
      notifyPackageAuditChanged();
      onAudited?.();
      await load();
    } catch (e) {
      message.error(e instanceof Error ? e.message : '操作失败');
    } finally {
      setAuditing(false);
    }
  }

  if (loading && !detail) {
    return <Typography.Text type="secondary">加载套餐变更…</Typography.Text>;
  }
  if (!detail) {
    return <Typography.Text type="secondary">暂无套餐审核详情</Typography.Text>;
  }

  const diffRows = diffPackages(detail.livePackages ?? [], detail.packages ?? []);
  const changeByKey = new Map(diffRows.map((row) => [row.key, row.change]));
  const changesByKey = new Map(diffRows.map((row) => [row.key, row.changes]));
  const addedCount = diffRows.filter((r) => r.change === 'added').length;
  const removedCount = diffRows.filter((r) => r.change === 'removed').length;
  const changedCount = diffRows.filter((r) => r.change === 'changed').length;
  const unchangedCount = diffRows.filter((r) => r.change === 'unchanged').length;

  return (
    <div>
      <Space style={{ marginBottom: 12, width: '100%', justifyContent: 'space-between' }} wrap>
        <Space wrap>
          <Tag>{HQ_PACKAGE_STATUS_LABELS[detail.status] ?? detail.status}</Tag>
          <Typography.Text type="secondary">
            提交方：{detail.submitterType === 'PARTNER' ? '合伙人' : '门店'} · {fmtTime(detail.createdAt)}
          </Typography.Text>
        </Space>
        {showActions && detail.status === 'PENDING' ? (
          <Space>
            <Button type="primary" loading={auditing} onClick={() => void audit('APPROVE')}>
              通过套餐
            </Button>
            <Button
              danger
              loading={auditing}
              onClick={() => {
                setRejectReason('');
                setRejectOpen(true);
              }}
            >
              驳回套餐
            </Button>
          </Space>
        ) : null}
      </Space>
      {detail.rejectReason ? (
        <Typography.Paragraph type="danger">驳回原因：{detail.rejectReason}</Typography.Paragraph>
      ) : null}
      <Space direction="vertical" size={4} style={{ marginBottom: 12 }}>
        <Typography.Text type="secondary">
          线上已审核 {detail.livePackages?.length ?? 0} 条 · 待审核 {detail.packages?.length ?? 0} 条
        </Typography.Text>
        <Space wrap>
          <Tag color="green">新增 {addedCount}</Tag>
          <Tag color="red">删除 {removedCount}</Tag>
          <Tag color="orange">变更 {changedCount}</Tag>
          {unchangedCount ? <Tag>未变 {unchangedCount}</Tag> : null}
        </Space>
      </Space>
      <div className="admin-package-audit-cols">
        <div className="admin-package-audit-col">
          <Typography.Title level={5} style={{ marginTop: 0 }}>
            线上已审核套餐
          </Typography.Title>
          {(detail.livePackages ?? []).length ? (
            (detail.livePackages ?? []).map((pkg, index) => (
              <PackageDetailCard
                key={`live-${packageKey(pkg, index)}`}
                title={`套餐 ${index + 1}`}
                pkg={pkg}
                change={changeByKey.get(packageKey(pkg, index))}
                imageSize={fullscreen ? 160 : 72}
              />
            ))
          ) : (
            <Typography.Text type="secondary">暂无线上套餐</Typography.Text>
          )}
        </div>
        <div className="admin-package-audit-col">
          <Typography.Title level={5} style={{ marginTop: 0 }}>
            待审核套餐
          </Typography.Title>
          {(detail.packages ?? []).length ? (
            (detail.packages ?? []).map((pkg, index) => (
              <PackageDetailCard
                key={`pending-${packageKey(pkg, index)}`}
                title={`套餐 ${index + 1}`}
                pkg={pkg}
                change={changeByKey.get(packageKey(pkg, index))}
                changes={changesByKey.get(packageKey(pkg, index))}
                imageSize={fullscreen ? 160 : 72}
              />
            ))
          ) : (
            <Typography.Text type="secondary">暂无待审核套餐</Typography.Text>
          )}
        </div>
      </div>

      <Modal
        title="驳回套餐变更"
        open={rejectOpen}
        confirmLoading={auditing}
        onCancel={() => setRejectOpen(false)}
        onOk={() => {
          if (!rejectReason.trim()) {
            message.warning('请填写驳回原因');
            return;
          }
          void audit('REJECT', rejectReason.trim()).then(() => setRejectOpen(false));
        }}
      >
        <Input.TextArea
          rows={3}
          value={rejectReason}
          placeholder="驳回原因"
          onChange={(e) => setRejectReason(e.target.value)}
        />
      </Modal>
    </div>
  );
}

/** 供门店详情抽屉 extra 调用 */
export async function auditStorePackageRequest(
  requestId: string,
  action: 'APPROVE' | 'REJECT',
  rejectReason?: string,
) {
  await request(`/admin/store-package-audits/${requestId}/audit`, {
    method: 'PUT',
    body: JSON.stringify(action === 'REJECT' ? { action, rejectReason } : { action }),
  });
  notifyPackageAuditChanged();
}
