// src/features/admin-approval/ApprovalInbox.tsx
// Admin: Unified approval inbox — deals/images/reviews/reports/jobs.
// Type filter chips, multi-select bulk actions, ApprovalRow per item.

'use client';

import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { X } from 'lucide-react';
import { getCsrfToken } from '@/lib/csrf';
import { formatBytes, formatDateTime } from '@/lib/format';
import { useT, useLocale } from '@/lib/i18n/react';
import { interpolate } from '@/lib/i18n/interpolate';
import { dirForLocale } from '@/lib/i18n';
import { Stack } from '@/components/ui/layout/Stack';
import { Button } from '@/components/ui/primitives/Button';
import { EmptyState } from '@/components/ui/feedback/EmptyState';
import { AdminPanel } from '@/components/ui/domain/admin/AdminPanel';
import { ApprovalRow } from '@/components/ui/domain/admin/ApprovalRow';
import type { ApprovalItemType as RowItemType } from '@/components/ui/domain/admin/ApprovalRow';
import type { UnifiedApprovalItem, ApprovalItemType } from '@/server/admin/_shared/approval-queue';
import { ErrorBoundary } from '@/components/ui/feedback/ErrorBoundary';
import { Image } from '@/components/ui/primitives/Image';
import { AiVerdictPanel } from '@/components/ui/domain/admin/AiVerdictPanel';
import { CascadePreviewPanel } from '@/components/ui/domain/admin/CascadePreviewPanel';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/overlays/Dialog';
import { Checkbox } from '@/components/ui/primitives/Checkbox';
import { Textarea } from '@/components/ui/primitives/Textarea';
import { createOptimisticMutation } from '@/lib/query/optimistic';
import { Skeleton } from '@/components/ui/feedback/Skeleton';
import { QueryBoundary } from '@platform-modules/ui-primitives';
import type { DashboardPrefetchDescriptor } from '@/lib/query/prefetch-registry';

/** Map server item type to ApprovalRow type (job → report fallback with label). */
function toRowType(type: ApprovalItemType): RowItemType {
  if (type === 'job') return 'report'; // closest visual variant; label comes from subtitle
  return type as RowItemType;
}

// ─── Props ────────────────────────────────────────────────────────────────────

export interface ApprovalInboxProps {
  initialItems: UnifiedApprovalItem[];
  initialTotal: number;
}

// ─── Type filter config ────────────────────────────────────────────────────────

const ALL_TYPES: (ApprovalItemType | 'all')[] = ['all', 'deal', 'image', 'review', 'report', 'job'];
const approvalQueryKey = (type: ApprovalItemType | 'all') => ['admin-approval', type] as const;

type ApprovalInboxData = {
  items: UnifiedApprovalItem[];
  total: number;
};

function ApprovalInboxSkeleton() {
  return (
    <div aria-hidden="true" className="space-y-3 py-1">
      {Array.from({ length: 5 }).map((_, index) => (
        <div key={index} className="flex items-start gap-3 py-2">
          <Skeleton className="mt-1 h-4 w-4 rounded-sm" />
          <div className="min-w-0 flex-1 space-y-2">
            <Skeleton className="h-4 w-48" />
            <Skeleton className="h-3 w-72 max-w-full" />
          </div>
          <div className="flex gap-2">
            <Skeleton className="h-8 w-20" />
            <Skeleton className="h-8 w-20" />
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Approve/Reject endpoint map ──────────────────────────────────────────────

function approveEndpoint(item: UnifiedApprovalItem): string | null {
  switch (item.type) {
    case 'deal':
      return `/api/admin/deals/${item.id}/approve`;
    case 'image':
      return `/api/admin/images/${item.id}/approve`;
    case 'review':
      return `/api/admin/review-removals/${item.id}/approve`;
    default:
      return null;
  }
}

function rejectEndpoint(item: UnifiedApprovalItem): string | null {
  switch (item.type) {
    case 'deal':
      return `/api/admin/deals/${item.id}/reject`;
    case 'image':
      return `/api/admin/images/${item.id}/reject`;
    case 'review':
      return `/api/admin/review-removals/${item.id}/reject`;
    default:
      return null;
  }
}

async function fetchApprovalItems(type: ApprovalItemType | 'all'): Promise<ApprovalInboxData> {
  const params = new URLSearchParams();
  if (type !== 'all') params.set('type', type);
  const query = params.toString();
  const res = await fetch(`/api/admin/approval${query ? `?${query}` : ''}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const json = (await res.json()) as {
    ok: boolean;
    data?: ApprovalInboxData;
  };
  return json.data ?? { items: [], total: 0 };
}

export const ADMIN_VENDOR_APPROVAL_PREFETCH_DESCRIPTOR: DashboardPrefetchDescriptor = {
  href: '/admin/vendors/approval',
  queryKey: approvalQueryKey('all'),
  queryFn: () => fetchApprovalItems('all'),
};

function removeItem(prev: ApprovalInboxData | undefined, id: string): ApprovalInboxData {
  const items = (prev?.items ?? []).filter((item) => item.id !== id);
  return { items, total: Math.max(0, (prev?.total ?? items.length) - 1) };
}

// ─── Component ────────────────────────────────────────────────────────────────

export function ApprovalInbox({ initialItems, initialTotal }: ApprovalInboxProps) {
  const t = useT('admin_approval');
  const tList = useT('admin_list');
  const tRow = useT('approval_row');
  const { locale } = useLocale();
  const dir = dirForLocale(locale);

  const [activeType, setActiveType] = useState<ApprovalItemType | 'all'>('all');
  const [error, setError] = useState<string | null>(null);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [_actionLoading, setActionLoading] = useState<string | null>(null);
  const [previewItem, setPreviewItem] = useState<UnifiedApprovalItem | null>(null);
  const [rejectReason, setRejectReason] = useState<string>('');

  const approvalQuery = useQuery({
    queryKey: approvalQueryKey(activeType),
    queryFn: () => fetchApprovalItems(activeType),
    initialData: activeType === 'all' ? { items: initialItems, total: initialTotal } : undefined,
  });

  const items = approvalQuery.data?.items ?? [];
  const total = approvalQuery.data?.total ?? 0;
  const useApproveMutation = createOptimisticMutation<void, UnifiedApprovalItem, ApprovalInboxData>(
    {
      mutationFn: async (item) => {
        const url = approveEndpoint(item);
        if (!url) throw new Error('Unsupported approval action');
        const res = await fetch(url, {
          method: 'POST',
          headers: { 'x-csrf-token': getCsrfToken(), 'Content-Type': 'application/json' },
        });
        if (!res.ok) throw new Error(await res.text());
      },
      queryKey: approvalQueryKey(activeType),
      optimisticUpdate: (prev, item) => removeItem(prev, item.id),
      errorToast: (err) => (err as Error).message,
      afterSuccess: () => setError(null),
      afterError: ({ err }) => setError((err as Error).message),
      broadcast: true,
    },
  );

  const useRejectMutation = createOptimisticMutation<
    void,
    { item: UnifiedApprovalItem; reason?: string },
    ApprovalInboxData
  >({
    mutationFn: async ({ item, reason }) => {
      const url = rejectEndpoint(item);
      if (!url) throw new Error('Unsupported rejection action');
      const body: Record<string, unknown> = {};
      if (reason && reason.trim().length > 0) body.reason = reason.trim();
      const res = await fetch(url, {
        method: 'POST',
        headers: { 'x-csrf-token': getCsrfToken(), 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!res.ok) throw new Error(await res.text());
    },
    queryKey: approvalQueryKey(activeType),
    optimisticUpdate: (prev, { item }) => removeItem(prev, item.id),
    errorToast: (err) => (err as Error).message,
    afterSuccess: () => setError(null),
    afterError: ({ err }) => setError((err as Error).message),
    broadcast: true,
  });

  const approveMutation = useApproveMutation();
  const rejectMutation = useRejectMutation();

  function handleTypeChange(type: ApprovalItemType | 'all') {
    setActiveType(type);
    setError(null);
    setSelected(new Set());
  }

  // ── Selection ─────────────────────────────────────────────────────────────
  function toggleSelect(id: string) {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  function selectAll() {
    setSelected(new Set(items.map((i) => i.id)));
  }

  function clearSelection() {
    setSelected(new Set());
  }

  // ── Single approve/reject ─────────────────────────────────────────────────
  async function handleApprove(item: UnifiedApprovalItem) {
    setActionLoading(item.id);
    try {
      await approveMutation.mutateAsync(item);
    } finally {
      setActionLoading(null);
    }
  }

  async function handleReject(item: UnifiedApprovalItem, reason?: string) {
    setActionLoading(item.id);
    try {
      await rejectMutation.mutateAsync({ item, reason });
    } finally {
      setActionLoading(null);
    }
  }

  // ── Bulk actions ──────────────────────────────────────────────────────────
  async function handleBulkApprove() {
    const targets = items.filter((i) => selected.has(i.id) && approveEndpoint(i) !== null);
    for (const item of targets) await handleApprove(item);
    clearSelection();
  }

  async function handleBulkReject() {
    const targets = items.filter((i) => selected.has(i.id) && rejectEndpoint(i) !== null);
    for (const item of targets) await handleReject(item);
    clearSelection();
  }

  const typeLabelKey: Record<ApprovalItemType | 'all', string> = {
    all: 'filter_all',
    deal: 'filter_deals',
    image: 'filter_images',
    review: 'filter_reviews',
    report: 'filter_reports',
    job: 'filter_jobs',
  };

  const typeLabel: Record<ApprovalItemType, string> = {
    deal: t('type_deal'),
    image: t('type_image'),
    review: t('type_review'),
    report: t('type_report'),
    job: t('type_job'),
  };

  // Decode encoded titles/subtitles. Server emits raw enum tokens like
  // `target:VENDOR` or `reason:FACTUAL_ERROR` so client can translate per locale.
  const tx = t as (k: string) => string;
  function decodeTitle(item: UnifiedApprovalItem): string {
    if (item.type === 'report' && item.title.startsWith('target:')) {
      const target = item.title.slice('target:'.length);
      return `${tx('report_title')}: ${tx(`target_${target}`) || ''}`.trim();
    }
    return item.title;
  }
  function decodeSubtitle(item: UnifiedApprovalItem): string {
    if (item.type === 'report' && item.subtitle.startsWith('reason:')) {
      const reason = item.subtitle.slice('reason:'.length);
      return tx(`reason_${reason}`) || '';
    }
    return item.subtitle;
  }

  const rangeFrom = items.length > 0 ? 1 : 0;
  const rangeTo = items.length;
  const rangeLabel =
    total > 0
      ? interpolate(tList('showing_x_of_y'), { from: rangeFrom, to: rangeTo, total })
      : null;

  return (
    <ErrorBoundary>
      <div>
        <Stack gap="6">
          <div>
            <p className="text-text-secondary text-sm">{t('page_subtitle')}</p>
            {rangeLabel ? (
              <p className="text-text-secondary mt-1 text-xs" aria-live="polite">
                {rangeLabel}
              </p>
            ) : null}
          </div>
          <AdminPanel
            toolbar={
              <div className="flex flex-wrap items-center gap-2">
                {/* Type filter chips */}
                <div role="group" aria-label={t('page_title')} className="flex flex-wrap gap-1">
                  {ALL_TYPES.map((type) => (
                    <Button
                      key={type}
                      variant={activeType === type ? 'primary' : 'ghost'}
                      size="sm"
                      aria-pressed={activeType === type}
                      onClick={() => handleTypeChange(type)}
                    >
                      {(t as (k: string) => string)(typeLabelKey[type])}
                    </Button>
                  ))}
                </div>

                {/* Bulk toolbar */}
                {selected.size > 0 && (
                  <div className="ms-auto flex items-center gap-2">
                    <span className="text-text-secondary text-sm">
                      {t('selected').replace('{n}', String(selected.size))}
                    </span>
                    <Button size="sm" variant="primary" onClick={handleBulkApprove}>
                      {t('bulk_approve')}
                    </Button>
                    <Button size="sm" variant="danger" onClick={handleBulkReject}>
                      {t('bulk_reject')}
                    </Button>
                    <Button
                      size="sm"
                      variant="ghost"
                      onClick={clearSelection}
                      aria-label={t('clear_selection')}
                      title={t('clear_selection')}
                    >
                      <X width={14} height={14} aria-hidden="true" />
                    </Button>
                  </div>
                )}
                {selected.size === 0 && items.length > 0 && (
                  <Button size="sm" variant="ghost" className="ms-auto" onClick={selectAll}>
                    {t('select_all')}
                  </Button>
                )}
              </div>
            }
          >
            {error && (
              <p role="alert" className="bg-danger-50 text-danger-700 rounded-md p-3 text-sm">
                {t('error')}: {error}
              </p>
            )}

            <QueryBoundary query={approvalQuery} skeleton={<ApprovalInboxSkeleton />}>
              {(resolved) => {
                const resolvedItems = resolved.items ?? [];
                if (resolvedItems.length === 0) {
                  return (
                    <EmptyState
                      title={t('empty_title')}
                      description={
                        activeType === 'all'
                          ? t('empty_desc')
                          : t('empty_desc_filtered').replace(
                              '{type}',
                              (t as (k: string) => string)(typeLabelKey[activeType]),
                            )
                      }
                    />
                  );
                }

                return (
                  <ul className="divide-border-default divide-y" aria-label={t('page_title')}>
                    {resolvedItems.map((item) => {
                      const isImage = item.type === 'image';
                      const uploader =
                        item.vendorName ??
                        item.uploaderName ??
                        (t as (k: string) => string)('image_uploader_unknown');
                      const uploaderLabel = item.vendorName
                        ? (t as (k: string) => string)('image_uploader_vendor')
                        : item.uploaderName
                          ? (t as (k: string) => string)('image_uploader_user')
                          : null;
                      const imageMetaParts: string[] = [];
                      if (uploaderLabel) imageMetaParts.push(`${uploaderLabel}: ${uploader}`);
                      if (item.sizeBytes != null) imageMetaParts.push(formatBytes(item.sizeBytes));
                      if (item.mime) imageMetaParts.push(item.mime);
                      const baseSubtitle = decodeSubtitle(item);
                      const subtitleText = isImage
                        ? `${typeLabel[item.type]} · ${imageMetaParts.join(' · ')}`
                        : baseSubtitle
                          ? `${typeLabel[item.type]} · ${baseSubtitle}`
                          : typeLabel[item.type];
                      return (
                        <li key={item.id} className="flex items-start gap-3 py-2">
                          {/* Checkbox */}
                          <Checkbox
                            className="mt-1"
                            aria-label={item.title}
                            checked={selected.has(item.id)}
                            onCheckedChange={() => toggleSelect(item.id)}
                          />
                          <div className="min-w-0 flex-1">
                            <ApprovalRow
                              id={item.id}
                              title={decodeTitle(item)}
                              subtitle={subtitleText}
                              type={toRowType(item.type)}
                              moderationHref={item.moderationHref}
                              onOpen={
                                isImage
                                  ? () => {
                                      setPreviewItem(item);
                                      setRejectReason('');
                                    }
                                  : undefined
                              }
                              preview={
                                isImage && item.previewKey ? (
                                  <Image
                                    src={item.previewKey}
                                    alt={decodeTitle(item)}
                                    variant="thumb"
                                    width={48}
                                    height={48}
                                    className="h-12 w-12 rounded-md object-cover"
                                  />
                                ) : undefined
                              }
                              aiBadge={
                                isImage && item.aiDecision ? (
                                  <span className="inline-flex items-center gap-1.5 text-xs">
                                    <span
                                      className={
                                        item.aiDecision === 'FLAG'
                                          ? 'bg-warning-50 text-warning-700 ring-warning-600/20 inline-flex items-center rounded-md px-2 py-0.5 font-medium ring-1'
                                          : 'text-text-muted inline-flex items-center rounded-md bg-neutral-100 px-2 py-0.5 font-medium ring-1 ring-neutral-500/20'
                                      }
                                    >
                                      {(t as (k: string) => string)(
                                        `ai_decision_${item.aiDecision.toLowerCase()}`,
                                      )}
                                    </span>
                                    {item.aiScore != null && (
                                      <span className="text-text-muted">
                                        {Math.round(item.aiScore * 100)}%
                                      </span>
                                    )}
                                  </span>
                                ) : undefined
                              }
                              onApprove={
                                approveEndpoint(item) ? () => handleApprove(item) : undefined
                              }
                              onReject={rejectEndpoint(item) ? () => handleReject(item) : undefined}
                            />
                          </div>
                        </li>
                      );
                    })}
                  </ul>
                );
              }}
            </QueryBoundary>
          </AdminPanel>
        </Stack>

        {/* Image preview dialog */}
        <Dialog
          open={previewItem !== null}
          onOpenChange={(open) => {
            if (!open) {
              setPreviewItem(null);
              setRejectReason('');
            }
          }}
        >
          <DialogContent className="max-w-2xl" dir={dir} aria-describedby={undefined}>
            <DialogHeader>
              <DialogTitle>
                {previewItem ? decodeTitle(previewItem) : t('image_preview_title')}
              </DialogTitle>
            </DialogHeader>
            {previewItem && previewItem.previewKey && (
              <div className="flex flex-col gap-4">
                {/* ── Hero image ─────────────────────────────────────────── */}
                <div className="bg-surface-raised flex items-center justify-center rounded-lg p-2">
                  <Image
                    src={previewItem.previewKey}
                    alt={decodeTitle(previewItem)}
                    variant="hero"
                    width={800}
                    height={600}
                    loading="eager"
                    className="max-h-[60vh] w-auto rounded-md object-contain"
                  />
                </div>

                {/* ── Metadata grid ──────────────────────────────────────── */}
                <dl className="grid grid-cols-1 gap-x-4 gap-y-2 text-sm sm:grid-cols-2">
                  <div>
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_uploader')}
                    </dt>
                    <dd className="text-text-primary">
                      {previewItem.vendorName
                        ? `${t('image_uploader_vendor')}: ${previewItem.vendorName}`
                        : previewItem.uploaderName
                          ? `${t('image_uploader_user')}: ${previewItem.uploaderName}`
                          : t('image_uploader_unknown')}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_size')}
                    </dt>
                    <dd className="text-text-primary">{formatBytes(previewItem.sizeBytes)}</dd>
                  </div>
                  <div>
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_mime')}
                    </dt>
                    <dd className="text-text-primary">{previewItem.mime ?? '—'}</dd>
                  </div>
                  <div>
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_scan_status')}
                    </dt>
                    <dd className="text-text-primary" title={t('scan_status_tooltip')}>
                      {previewItem.scanStatus
                        ? (t as (k: string) => string)(`scan_${previewItem.scanStatus}`) ||
                          previewItem.scanStatus
                        : '—'}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_uploaded_at')}
                    </dt>
                    <dd className="text-text-primary">
                      {formatDateTime(previewItem.createdAt, locale)}
                    </dd>
                  </div>
                  {previewItem.entityRefHref && (
                    <div>
                      <dt className="text-text-secondary text-xs font-semibold uppercase">
                        {previewItem.purpose
                          ? (t as (k: string) => string)(`purpose_${previewItem.purpose}`)
                          : ''}
                      </dt>
                      <dd className="text-text-primary text-xs">
                        <a
                          href={previewItem.entityRefHref}
                          className="text-brand-primary-700 hover:text-brand-primary-900 underline"
                          aria-label={t('entity_link_aria')}
                        >
                          {previewItem.entityRefName ?? previewItem.entityRefHref}
                        </a>
                      </dd>
                    </div>
                  )}
                  <div className="sm:col-span-2">
                    <dt className="text-text-secondary text-xs font-semibold uppercase">
                      {t('image_r2_key')}
                    </dt>
                    <dd className="text-text-primary font-mono text-xs break-all">
                      {previewItem.previewKey}
                    </dd>
                  </div>
                </dl>

                {/* ── AI Verdict Panel ───────────────────────────────────── */}
                {previewItem.aiDecision && (
                  <AiVerdictPanel
                    decision={previewItem.aiDecision}
                    score={previewItem.aiScore ?? null}
                    model={previewItem.aiModel ?? null}
                    checkedAt={previewItem.aiCheckedAt ?? null}
                    reason={previewItem.aiReason ?? null}
                    rawPayload={previewItem.aiRawPayload}
                  />
                )}

                {/* ── Cascade Preview Panel ──────────────────────────────── */}
                {previewItem.cascadePreview && (
                  <CascadePreviewPanel preview={previewItem.cascadePreview} />
                )}

                {/* ── Reject reason override textarea ────────────────────── */}
                {rejectEndpoint(previewItem) && (
                  <div className="flex flex-col gap-1">
                    <label
                      htmlFor="reject-reason-input"
                      className="text-text-secondary text-xs font-semibold uppercase"
                    >
                      {t('reject_reason_override_label')}
                    </label>
                    <Textarea
                      id="reject-reason-input"
                      value={rejectReason}
                      onChange={(e) => setRejectReason(e.target.value)}
                      placeholder={t('reject_reason_override_placeholder')}
                      rows={3}
                    />
                  </div>
                )}

                {/* ── Action buttons ─────────────────────────────────────── */}
                <div className="flex flex-row-reverse gap-2">
                  {rejectEndpoint(previewItem) && (
                    <Button
                      variant="danger"
                      size="sm"
                      onClick={async () => {
                        const item = previewItem;
                        const reason = rejectReason;
                        setPreviewItem(null);
                        setRejectReason('');
                        await handleReject(item, reason);
                      }}
                    >
                      {tRow('reject')}
                    </Button>
                  )}
                  {approveEndpoint(previewItem) && (
                    <Button
                      variant="primary"
                      size="sm"
                      onClick={async () => {
                        const item = previewItem;
                        setPreviewItem(null);
                        await handleApprove(item);
                      }}
                    >
                      {tRow('approve')}
                    </Button>
                  )}
                </div>
              </div>
            )}
          </DialogContent>
        </Dialog>
      </div>
    </ErrorBoundary>
  );
}
