// src/features/admin-llm-jobs/LlmJobs.tsx
// Admin: LLM Jobs list — cost + token tracking per AI job.

'use client';

import { useState } from 'react';
import { getCsrfToken } from '@/lib/csrf';
import { useT, useLocale } from '@/lib/i18n/react';
import { formatDateTime, formatInteger, fmtDuration } from '@/lib/format';
import { interpolate } from '@/lib/i18n/interpolate';
import { usePaginatedQuery } from '@/lib/hooks/usePaginatedQuery';
import { AdminListPage } from '@/components/ui/domain/admin/AdminListPage';
import { Badge } from '@/components/ui/primitives/Badge';
import { Button } from '@/components/ui/primitives/Button';
import {
  AdminTable,
  type ActionDef,
  type ColumnDef,
} from '@/components/ui/domain/admin/AdminTable';
import { AdminFilterBar, type FilterChip } from '@/components/ui/domain/admin/AdminFilterBar';
import { Pagination } from '@/components/ui/primitives/Pagination';
import { useAdminFilters } from '@/components/ui/domain/admin/AdminFilterBar/useAdminFilters';
import type { LlmJobRow } from '@/server/admin/resources/llm-jobs/queries';
import { ErrorBoundary } from '@/components/ui/feedback/ErrorBoundary';
import {
  AlertDialog,
  AlertDialogContent,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogAction,
  AlertDialogCancel,
} from '@/components/ui/overlays/AlertDialog';
import { useToast } from '@/components/ui/overlays/Toast/useToast';
import { ToastProvider, ToastViewport, Toast, ToastTitle } from '@/components/ui/overlays/Toast';
import { captureCaught } from '@/lib/observability';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface LlmJobsProps {
  initialJobs: LlmJobRow[];
  initialTotal: number;
  initialPage?: number;
}

// ─── Filter param keys ────────────────────────────────────────────────────────

const FILTER_KEYS = ['status', 'jobType'];

const RETRYABLE_STATUSES = new Set(['FAILED', 'TIMED_OUT']);

// ─── Status tone map ──────────────────────────────────────────────────────────

const STATUS_TONE: Record<string, 'neutral' | 'warning' | 'success' | 'danger'> = {
  PENDING: 'neutral',
  RUNNING: 'warning',
  COMPLETED: 'success',
  FAILED: 'danger',
  TIMED_OUT: 'danger',
};

function formatDuration(startedAt: string | null, completedAt: string | null): string {
  if (!startedAt || !completedAt) return '—';
  const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
  if (!Number.isFinite(ms) || ms < 0) return '—';
  return fmtDuration(ms);
}

function toPending(r: LlmJobRow): LlmJobRow {
  return {
    ...r,
    status: 'PENDING',
    retryCount: r.retryCount + 1,
    lastError: null,
    startedAt: null,
    completedAt: null,
  };
}

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

export function LlmJobs({ initialJobs, initialTotal, initialPage }: LlmJobsProps) {
  const t = useT('admin_llm_jobs');
  const tCommon = useT('common');
  const { locale } = useLocale();
  const { toasts, toast, dismiss } = useToast();
  const [retryAllOpen, setRetryAllOpen] = useState(false);

  // ── Read active filter values from URL params ─────────────────────────────
  const { filters } = useAdminFilters(FILTER_KEYS);
  const statusFilter = filters['status'] ?? '';
  const jobTypeFilter = filters['jobType'] ?? '';

  const params: Record<string, string> = {};
  if (statusFilter) params.status = statusFilter;
  if (jobTypeFilter) params.jobType = jobTypeFilter;

  // ── Paginated data ─────────────────────────────────────────────────────────
  const { data, totalPages, page, isInitialLoading, isRefetching, error, setPage, mutate } =
    usePaginatedQuery<LlmJobRow>({
      endpoint: '/api/admin/llm-jobs',
      params,
      limit: 25,
      initialData: initialJobs,
      initialTotal,
      initialPage,
      dataKey: 'jobs',
    });

  async function retryRow(id: string) {
    const snapshot = data;
    mutate((rows) => rows.map((r) => (r.id === id ? toPending(r) : r)));
    try {
      const res = await fetch(`/api/admin/llm-jobs/${id}/retry`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
      });
      const body = (await res.json()) as { ok?: boolean; data?: { retried?: boolean } };
      if (!res.ok || body.ok === false || body.data?.retried === false) {
        mutate(() => snapshot);
        toast({
          title: body.data?.retried === false ? t('toast_retry_capped') : t('toast_retry_fail'),
          tone: 'danger',
        });
        return;
      }
      toast({ title: t('toast_retry_ok'), tone: 'success' });
    } catch (err) {
      mutate(() => snapshot);
      captureCaught(err, { scope: 'features.admin-llm-jobs.LlmJobs.retryRow', severity: 'info' });
      toast({ title: t('toast_retry_fail'), tone: 'danger' });
    }
  }

  async function retryAllFailed() {
    setRetryAllOpen(false);
    const snapshot = data;
    mutate((rows) => rows.map((r) => (RETRYABLE_STATUSES.has(r.status) ? toPending(r) : r)));
    try {
      const res = await fetch('/api/admin/llm-jobs/retry-failed', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
      });
      if (!res.ok) {
        mutate(() => snapshot);
        toast({ title: t('toast_retry_fail'), tone: 'danger' });
        return;
      }
      const body = (await res.json()) as { data?: { retried?: number } };
      toast({
        title: interpolate(t('toast_retry_all_ok'), { count: body.data?.retried ?? 0 }),
        tone: 'success',
      });
    } catch (err) {
      mutate(() => snapshot);
      captureCaught(err, {
        scope: 'features.admin-llm-jobs.LlmJobs.retryAllFailed',
        severity: 'info',
      });
      toast({ title: t('toast_retry_fail'), tone: 'danger' });
    }
  }

  // ── Status chips ──────────────────────────────────────────────────────────
  const statusChips: FilterChip[] = [
    {
      key: 'status',
      value: 'PENDING',
      label: t('status_pending'),
      groupLabel: `${t('filter_group_status')}`,
    },
    {
      key: 'status',
      value: 'RUNNING',
      label: t('status_running'),
      groupLabel: `${t('filter_group_status')}`,
    },
    {
      key: 'status',
      value: 'COMPLETED',
      label: t('status_completed'),
      groupLabel: `${t('filter_group_status')}`,
    },
    {
      key: 'status',
      value: 'FAILED',
      label: t('status_failed'),
      groupLabel: `${t('filter_group_status')}`,
    },
    {
      key: 'status',
      value: 'TIMED_OUT',
      label: t('status_timed_out'),
      groupLabel: `${t('filter_group_status')}`,
    },
  ];

  // ── Job type chips ────────────────────────────────────────────────────────
  const jobTypeChips: FilterChip[] = [
    {
      key: 'jobType',
      value: 'DEAL_MODERATION',
      label: t('job_type_deal_moderation'),
      groupLabel: `${t('filter_group_job_type')}`,
    },
    {
      key: 'jobType',
      value: 'IMAGE_APPROVAL',
      label: t('job_type_image_approval'),
      groupLabel: `${t('filter_group_job_type')}`,
    },
    {
      key: 'jobType',
      value: 'REVIEW_PRESCORING',
      label: t('job_type_review_prescoring'),
      groupLabel: `${t('filter_group_job_type')}`,
    },
    {
      key: 'jobType',
      value: 'VENDOR_VIOLATION',
      label: t('job_type_vendor_violation'),
      groupLabel: `${t('filter_group_job_type')}`,
    },
    {
      key: 'jobType',
      value: 'TRANSLATION',
      label: t('job_type_translation'),
      groupLabel: `${t('filter_group_job_type')}`,
    },
  ];

  // Combine into one chips array for AdminFilterBar.
  // AdminFilterBar groups by key, so status and jobType appear as separate groups.
  const allChips: FilterChip[] = [...statusChips, ...jobTypeChips];

  // ── Column definitions ────────────────────────────────────────────────────
  const columns: ColumnDef<LlmJobRow>[] = [
    {
      key: 'createdAt',
      label: t('col_created_at'),
      render: (r) => formatDateTime(r.createdAt, locale),
    },
    {
      key: 'jobType',
      label: t('col_job_type'),
      render: (r) => {
        const labels: Record<string, string> = {
          DEAL_MODERATION: t('job_type_deal_moderation'),
          IMAGE_APPROVAL: t('job_type_image_approval'),
          REVIEW_PRESCORING: t('job_type_review_prescoring'),
          VENDOR_VIOLATION: t('job_type_vendor_violation'),
          TRANSLATION: t('job_type_translation'),
        };
        return labels[r.jobType] ?? r.jobType;
      },
    },
    {
      key: 'modelName',
      label: t('col_model_name'),
      render: (r) => (
        <span className="text-text-secondary font-mono text-xs">
          {r.modelName ?? t('model_unknown')}
        </span>
      ),
    },
    {
      key: 'status',
      label: t('col_status'),
      align: 'center',
      render: (r) => {
        const cancelled = r.status === 'FAILED' && r.lastError === 'Cancelled by admin';
        const tone = cancelled ? 'neutral' : (STATUS_TONE[r.status] ?? 'neutral');
        const label = cancelled
          ? t('status_cancelled')
          : (t as (k: string) => string)(`status_${r.status.toLowerCase()}`) || r.status;
        return (
          <Badge tone={tone} size="sm">
            {label}
          </Badge>
        );
      },
    },
    {
      key: 'retryCount',
      label: t('col_attempts'),
      align: 'end',
      render: (r) => formatInteger(r.retryCount, locale),
    },
    {
      key: 'duration',
      label: t('col_duration'),
      align: 'end',
      render: (r) => formatDuration(r.startedAt, r.completedAt),
    },
    {
      key: 'error',
      label: t('col_error'),
      render: (r) => {
        if (!RETRYABLE_STATUSES.has(r.status)) return '—';
        if (!r.lastError) return '—';
        const text = r.lastError.length > 60 ? r.lastError.slice(0, 60) + '…' : r.lastError;
        return (
          <span className="text-text-secondary font-mono text-xs" title={r.lastError}>
            {text}
          </span>
        );
      },
    },
    {
      key: 'totalTokens',
      label: t('col_total_tokens'),
      align: 'end',
      render: (r) =>
        r.totalTokens !== null ? formatInteger(r.totalTokens, locale) : t('tokens_unknown'),
    },
    {
      key: 'costUsd',
      label: t('col_cost_usd'),
      align: 'end',
      render: (r) => {
        if (r.costUsd === null) return t('cost_unknown');
        const value = parseFloat(r.costUsd);
        if (isNaN(value)) return t('cost_unknown');
        // Show 6 decimal places to preserve micro-cost precision
        return `$${value.toFixed(6)}`;
      },
    },
    {
      key: 'targetType',
      label: t('col_target_type'),
      render: (r) => {
        const targetLabel = (k: string) => {
          switch (k) {
            case 'VENDOR':
              return t('target_vendor');
            case 'DEAL':
              return t('target_deal');
            case 'IMAGE':
              return t('target_image');
            default:
              return k;
          }
        };
        return <span className="text-text-secondary text-xs">{targetLabel(r.targetType)}</span>;
      },
    },
  ];

  const actions: ActionDef<LlmJobRow>[] = [
    {
      key: 'retry',
      label: t('action_retry'),
      hidden: (r) => !RETRYABLE_STATUSES.has(r.status),
      onAction: (r) => {
        void retryRow(r.id);
      },
    },
  ];

  // ── Render ────────────────────────────────────────────────────────────────
  return (
    <ErrorBoundary>
      <ToastProvider swipeDirection="left">
        <AdminListPage
          status={error ? 'error' : isInitialLoading ? 'loading' : 'idle'}
          busy={isRefetching}
          onRetry={() => setPage(page)}
          filterBar={
            <div className="flex flex-wrap items-center justify-between gap-3">
              <AdminFilterBar chips={allChips} />
              <Button variant="secondary" size="sm" onClick={() => setRetryAllOpen(true)}>
                {t('action_retry_all')}
              </Button>
            </div>
          }
          table={
            <AdminTable<LlmJobRow>
              columns={columns}
              rows={data}
              loading={isInitialLoading}
              rowHref={(r) => `/admin/moderation/jobs/${r.id}`}
              actions={actions}
            />
          }
          pagination={
            totalPages > 1 ? (
              <Pagination page={page} totalPages={totalPages} onPageChange={setPage} />
            ) : undefined
          }
        />

        <AlertDialog open={retryAllOpen} onOpenChange={setRetryAllOpen}>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>{t('action_retry_all')}</AlertDialogTitle>
              <AlertDialogDescription>{t('confirm_retry_all')}</AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel>{tCommon('cancel')}</AlertDialogCancel>
              <AlertDialogAction onClick={() => void retryAllFailed()}>
                {t('action_retry_all')}
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>

        <ToastViewport />
        {toasts.map((item) => (
          <Toast
            key={item.id}
            tone={item.tone}
            duration={item.duration}
            open
            onOpenChange={() => dismiss(item.id)}
          >
            <ToastTitle>{item.title}</ToastTitle>
          </Toast>
        ))}
      </ToastProvider>
    </ErrorBoundary>
  );
}
