// src/features/admin-llm-jobs/LlmJobDetail.tsx
// Admin: LLM Job detail — diagnostics, payloads, retry/cancel.

'use client';

import { useState, type ReactNode } 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 { Badge } from '@/components/ui/primitives/Badge';
import { Button } from '@/components/ui/primitives/Button';
import { Card } from '@/components/ui/layout/Card/Card';
import type { LlmJobDetail as Job } from '@/server/admin/resources/llm-jobs/queries';
import { ErrorBoundary } from '@/components/ui/feedback/ErrorBoundary';
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 LlmJobDetailProps {
  job: Job;
}

// ─── 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 formatCostUsd(costUsd: string | null, t: (k: string) => string): string {
  if (costUsd === null) return t('cost_unknown');
  const value = parseFloat(costUsd);
  if (isNaN(value)) return t('cost_unknown');
  return `$${value.toFixed(6)}`;
}

function decisionLabel(decision: string, t: (k: string) => string): string {
  switch (decision) {
    case 'APPROVE':
      return t('decision_approve');
    case 'FLAG':
      return t('decision_flag');
    case 'REJECT':
      return t('decision_reject');
    default:
      return decision;
  }
}

function targetAdminPath(targetType: string, targetId: string): string | null {
  switch (targetType) {
    case 'DEAL':
      return `/admin/deals/${targetId}`;
    case 'VENDOR':
      return `/admin/vendors/${targetId}`;
    case 'IMAGE':
      return null;
    default:
      return null;
  }
}

function RawPayload({ label, value }: { label: string; value: unknown }) {
  if (value == null) return null;
  const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
  return (
    <details className="border-border-default rounded-md border">
      <summary className="text-text-primary cursor-pointer px-3 py-2 text-sm font-medium">
        {label}
      </summary>
      <pre className="border-border-default text-text-secondary overflow-auto border-t px-3 py-2 font-mono text-xs whitespace-pre-wrap">
        {text}
      </pre>
    </details>
  );
}

function DlRow({ label, value }: { label: string; value: ReactNode }) {
  return (
    <dl className="flex gap-4 py-2 text-sm">
      <dt className="text-text-secondary w-40 shrink-0 font-medium">{label}</dt>
      <dd className="text-text-primary min-w-0 flex-1 break-all">{value}</dd>
    </dl>
  );
}

function DetailSection({ title, children }: { title: string; children: ReactNode }) {
  return (
    <Card>
      <h2 className="text-text-secondary mb-3 text-sm font-semibold">{title}</h2>
      {children}
    </Card>
  );
}

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

export function LlmJobDetail({ job: initial }: LlmJobDetailProps) {
  const t = useT('admin_llm_jobs');
  const { locale } = useLocale();
  const { toasts, toast, dismiss } = useToast();
  const [job, setJob] = useState(initial);
  const [busy, setBusy] = useState(false);

  const isRetryable = job.status === 'FAILED' || job.status === 'TIMED_OUT';
  const isCancellable = job.status === 'PENDING' || job.status === 'RUNNING';

  const jobTypeLabels: 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'),
  };

  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;
    }
  };

  async function refetch() {
    const res = await fetch(`/api/admin/llm-jobs/${job.id}`);
    if (!res.ok) return;
    const body = (await res.json()) as { job?: Job };
    if (body.job) setJob(body.job);
  }

  async function act(path: string, okMsg: string, failMsg?: string) {
    setBusy(true);
    try {
      const res = await fetch(`/api/admin/llm-jobs/${job.id}/${path}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
      });
      if (!res.ok) {
        toast({ title: failMsg ?? t('toast_retry_fail'), tone: 'danger' });
        return;
      }
      toast({ title: okMsg, tone: 'success' });
      await refetch();
    } catch (err) {
      captureCaught(err, {
        scope: `features.admin-llm-jobs.LlmJobDetail.act.${path}`,
        severity: 'info',
      });
      toast({ title: failMsg ?? t('toast_retry_fail'), tone: 'danger' });
    } finally {
      setBusy(false);
    }
  }

  const adminPath = targetAdminPath(job.targetType, job.targetId);
  const showVerdict = Boolean(job.decision || job.flagReason);

  return (
    <ErrorBoundary>
      <ToastProvider swipeDirection="left">
        <div className="flex flex-col gap-6">
          {/* Header */}
          <div className="flex flex-wrap items-start justify-between gap-4">
            <div className="flex flex-col gap-2">
              <div className="flex flex-wrap items-center gap-2">
                <Badge tone={STATUS_TONE[job.status] ?? 'neutral'} size="sm">
                  {(t as (k: string) => string)(`status_${job.status.toLowerCase()}`) || job.status}
                </Badge>
                <span className="text-text-primary text-sm font-medium">
                  {jobTypeLabels[job.jobType] ?? job.jobType}
                </span>
              </div>
              <p className="text-text-secondary font-mono text-xs">
                {job.modelName
                  ? interpolate(t('field_model'), { name: job.modelName })
                  : t('field_model_unknown')}
              </p>
              <p className="text-text-secondary font-mono text-xs">{job.id}</p>
              <p className="text-text-secondary text-sm">{formatDateTime(job.createdAt, locale)}</p>
            </div>
          </div>

          {/* Action bar */}
          {(isRetryable || isCancellable) && (
            <div className="flex flex-wrap gap-2">
              {isRetryable && (
                <Button
                  variant="primary"
                  size="sm"
                  disabled={busy}
                  onClick={() => {
                    void act('retry', t('toast_retry_ok'));
                  }}
                >
                  {t('action_retry')}
                </Button>
              )}
              {isCancellable && (
                <Button
                  variant="secondary"
                  size="sm"
                  disabled={busy}
                  onClick={() => {
                    void act('cancel', t('toast_cancel_ok'), t('toast_cancel_fail'));
                  }}
                >
                  {t('action_cancel')}
                </Button>
              )}
            </div>
          )}

          {/* Error panel */}
          {job.lastError && (
            <section
              aria-labelledby="llm-job-error-heading"
              className="border-danger-500/30 bg-danger-50 rounded-lg border p-4"
            >
              <h2 id="llm-job-error-heading" className="text-danger-700 mb-2 text-sm font-semibold">
                {t('section_error')}
              </h2>
              <pre className="text-danger-600 overflow-auto font-mono text-xs whitespace-pre-wrap">
                {job.lastError}
              </pre>
            </section>
          )}

          {/* Timeline */}
          <DetailSection title={t('section_timeline')}>
            <DlRow
              label={t('field_started_at')}
              value={job.startedAt ? formatDateTime(job.startedAt, locale) : '—'}
            />
            <DlRow
              label={t('field_completed_at')}
              value={job.completedAt ? formatDateTime(job.completedAt, locale) : '—'}
            />
            <DlRow
              label={t('field_duration')}
              value={formatDuration(job.startedAt, job.completedAt)}
            />
            <DlRow label={t('field_retry_count')} value={formatInteger(job.retryCount, locale)} />
          </DetailSection>

          {/* Usage */}
          <DetailSection title={t('section_usage')}>
            <DlRow
              label={t('field_prompt_tokens')}
              value={
                job.promptTokens !== null
                  ? formatInteger(job.promptTokens, locale)
                  : t('tokens_unknown')
              }
            />
            <DlRow
              label={t('field_completion_tokens')}
              value={
                job.completionTokens !== null
                  ? formatInteger(job.completionTokens, locale)
                  : t('tokens_unknown')
              }
            />
            <DlRow
              label={t('field_total_tokens')}
              value={
                job.totalTokens !== null
                  ? formatInteger(job.totalTokens, locale)
                  : job.status === 'PENDING' || job.status === 'RUNNING'
                    ? t('usage_pending')
                    : t('tokens_unknown')
              }
            />
            <DlRow
              label={t('field_cost_usd')}
              value={
                job.costUsd !== null
                  ? formatCostUsd(job.costUsd, t as (k: string) => string)
                  : job.status === 'PENDING' || job.status === 'RUNNING'
                    ? t('usage_pending')
                    : t('cost_unknown')
              }
            />
          </DetailSection>

          {/* Verdict */}
          {showVerdict && (
            <DetailSection title={t('section_verdict')}>
              <DlRow
                label={t('decision_label')}
                value={job.decision ? decisionLabel(job.decision, t as (k: string) => string) : '—'}
              />
              <DlRow label={t('flag_reason_label')} value={job.flagReason ?? '—'} />
            </DetailSection>
          )}

          {/* Target */}
          <DetailSection title={t('section_target')}>
            {adminPath ? (
              <a
                href={adminPath}
                className="text-text-secondary hover:text-text-primary text-sm transition-colors"
              >
                {t('target_open')} ({targetLabel(job.targetType)})
              </a>
            ) : (
              <p className="text-text-secondary text-sm">
                {job.targetType === 'IMAGE'
                  ? interpolate(t('target_image_hint'), { id: job.targetId })
                  : `${job.targetType}: ${job.targetId}`}
              </p>
            )}
          </DetailSection>

          {/* Raw payloads */}
          <DetailSection title={t('section_payloads')}>
            <div className="flex flex-col gap-3">
              <RawPayload label={t('payload_input')} value={job.inputPayload} />
              <RawPayload label={t('payload_output')} value={job.outputPayload} />
              <RawPayload label={t('payload_prompt')} value={job.promptSnapshot} />
            </div>
          </DetailSection>
        </div>

        <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>
  );
}
