import { useState } from 'react';
import { api } from '../api/client';
import { useAction } from '../lib/hooks';
import { useGateway } from '../lib/gateway';
import { useToast } from '../lib/toast';
import { num, when } from '../lib/format';
import { Banner, Button, Card, Chip, Empty, Field, Grid, PageHead, Tiles } from '../components/ui';

/* Nothing here changes anything. It re-runs the shared weighted scorer over one person's
   prepared pool, after Emby permission and parental-control filtering, and shows every
   component and evidence reason behind the order. */

interface Weighted {
  weight?: number;
  evidence?: number;
}

interface Profile {
  genres?: Record<string, Weighted>;
  studios?: Record<string, Weighted>;
  actors?: Record<string, Weighted>;
  directors?: Record<string, Weighted>;
  franchises?: Record<string, Weighted>;
  runtimeRanges?: Record<string, Weighted>;
  ageRatings?: Record<string, Weighted>;
  communityRatings?: Record<string, Weighted>;
  releasePeriods?: Record<string, Weighted>;
  contentTypes?: Record<string, Weighted>;
}

interface InspectorItem {
  title: string;
  type?: string;
  year?: number;
  runtimeMinutes?: number;
  genres?: string[];
  baseRank?: number;
  baseScore?: number;
  affinityScore?: number;
  compatibilityScore?: number;
  compatibilityLabel?: string;
  preparedReason?: string;
  preparedEvidenceTitle?: string;
  eligibleRows?: string[];
  exposure?: { impressions?: number; focuses?: number; selects?: number };
  explanation?: { total?: number; components?: Record<string, number>; reasonCodes?: string[] };
}

interface InspectorResponse {
  poolCandidates: number;
  permissionEligible: number;
  items: InspectorItem[] | null;
  profile: Profile | null;
  actions: { action: string; title?: string; itemId?: string }[] | null;
  profileMeta?: { sourceEvents?: number; algorithmVersion?: string; poolBuiltAt?: string };
}

const DIMENSIONS: [string, keyof Profile][] = [
  ['Genre', 'genres'],
  ['Studio', 'studios'],
  ['Actor', 'actors'],
  ['Director', 'directors'],
  ['Franchise', 'franchises'],
  ['Runtime', 'runtimeRanges'],
  ['Age rating', 'ageRatings'],
  ['Community rating', 'communityRatings'],
  ['Release period', 'releasePeriods'],
  ['Content type', 'contentTypes'],
];

function affinities(profile: Profile | null) {
  if (!profile) return [];
  return DIMENSIONS.flatMap(([dimension, key]) =>
    Object.entries(profile[key] ?? {}).map(([name, value]) => ({
      dimension,
      name,
      weight: value.weight ?? 0,
      evidence: value.evidence ?? 0,
    })),
  ).sort((a, b) => Math.abs(b.weight) - Math.abs(a.weight));
}

const signed = (value: number) => `${value >= 0 ? '+' : ''}${value.toFixed(3)}`;

export function InspectorPage() {
  const { status } = useGateway();
  const { wrap } = useToast();
  const { busy, run } = useAction();
  const [userId, setUserId] = useState('');
  const [context, setContext] = useState('default');
  const [minutes, setMinutes] = useState('0');
  const [at, setAt] = useState('');
  const [result, setResult] = useState<InspectorResponse | null>(null);
  const [hint, setHint] = useState('Choose a person to inspect their recommendations.');
  const [error, setError] = useState('');

  const users = status?.requestUsers ?? [];

  const runTest = () =>
    run('run', async () => {
      if (!userId) {
        setError('Choose a person to pressure-test.');
        return;
      }
      setError('');
      setHint('Running the permission check and the scorer…');
      const params = new URLSearchParams({ userId, context, minutes: minutes || '0', limit: '100' });
      if (at) params.set('at', new Date(at).toISOString());
      const payload = await wrap(() =>
        api.get<InspectorResponse>(`/admin/api/recommendations?${params.toString()}`),
      );
      if (payload) {
        setResult(payload);
        setHint(`Scored at ${new Date().toLocaleTimeString()}.`);
      } else {
        setHint('Pressure test failed.');
      }
    });

  const top = affinities(result?.profile ?? null).slice(0, 24);
  const actions = result?.actions ?? [];
  const items = result?.items ?? [];
  const meta = result?.profileMeta ?? {};

  return (
    <>
      <PageHead
        title="Score inspector"
        intro="Re-run the ranker for one person and read every component."
      />
      <Banner message={error} />

      <Card
        title="Run a pressure test"
        intro="Nothing is changed by running this. It scores the person's prepared pool as the launcher would, in the context you choose."
        icon="search"
        tone="info"
        footer={
          <>
            <Button variant="primary" busy={busy === 'run'} onClick={() => void runTest()}>
              Run pressure test
            </Button>
            <span className="hint">{hint}</span>
          </>
        }
      >
        <div className="fields">
          <Field label="Person">
            <select value={userId} onChange={(event) => setUserId(event.target.value)}>
              <option value="">Choose a person…</option>
              {users.map((user) => (
                <option key={user.id} value={user.id}>
                  {user.username}
                </option>
              ))}
            </select>
          </Field>
          <Field label="Context">
            <select value={context} onChange={(event) => setContext(event.target.value)}>
              <option value="default">Default</option>
              <option value="bedtime">One episode before bed</option>
              <option value="hidden">Hidden library</option>
              <option value="new-releases">Recent new releases</option>
            </select>
          </Field>
          <Field label="Available minutes">
            <input
              type="number"
              min={0}
              max={360}
              value={minutes}
              onChange={(event) => setMinutes(event.target.value)}
            />
          </Field>
          <Field label="Evaluate at">
            <input type="datetime-local" value={at} onChange={(event) => setAt(event.target.value)} />
          </Field>
        </div>
      </Card>

      {result ? (
        <>
          <Tiles
            tiles={[
              { label: 'prepared pool', value: num(result.poolCandidates), icon: 'database', tone: 'data' },
              { label: 'permission eligible', value: num(result.permissionEligible), icon: 'shield', tone: 'ok' },
              { label: 'ranked result', value: num(items.length), icon: 'sparkle', tone: 'note' },
              { label: 'source events', value: num(meta.sourceEvents ?? 0), icon: 'pulse', tone: 'info' },
              { label: 'algorithm', value: meta.algorithmVersion || '—', small: true, icon: 'chip' },
              { label: 'pool built', value: when(meta.poolBuiltAt), small: true, icon: 'clock' },
            ]}
          />

          <Card
            title="Profile evidence"
            intro="The strongest learned affinities, and every explicit action this person has taken."
            icon="sparkle"
            tone="note"
          >
            {top.length === 0 ? (
              <Empty>No repeated affinity evidence yet; cold-start priors apply.</Empty>
            ) : (
              <div className="chips">
                {top.map((entry) => (
                  <Chip key={`${entry.dimension}:${entry.name}`} tone={entry.weight < 0 ? 'bad' : undefined}>
                    {entry.dimension}: {entry.name} {signed(entry.weight)} · n={num(entry.evidence)}
                  </Chip>
                ))}
              </div>
            )}
            {actions.length === 0 ? (
              <Empty>No explicit recommendation actions.</Empty>
            ) : (
              <div className="chips">
                {actions.map((action, index) => (
                  <Chip key={`${action.action}:${index}`} tone="ok">
                    {action.action}: {action.title || action.itemId}
                  </Chip>
                ))}
              </div>
            )}
          </Card>

          {items.length === 0 ? (
            <Card>
              <Empty>
                No candidates survived this context, the explicit exclusions and the permission filter.
              </Empty>
            </Card>
          ) : (
            <Grid>
              {items.map((item, index) => {
                const explanation = item.explanation ?? {};
                const components = Object.entries(explanation.components ?? {}).sort(
                  (a, b) => Math.abs(b[1]) - Math.abs(a[1]),
                );
                const exposure = item.exposure ?? {};
                const facts = [
                  item.type,
                  item.year,
                  item.runtimeMinutes ? `${item.runtimeMinutes} min` : null,
                  ...(item.genres ?? []),
                ]
                  .filter(Boolean)
                  .join(' · ');
                return (
                  <Card
                    key={`${index}:${item.title}`}
                    title={`#${index + 1} · ${item.title}`}
                    intro={facts}
                    actions={<Chip tone="ok">{Number(explanation.total ?? 0).toFixed(3)}</Chip>}
                  >
                    <p className="hint">
                      {item.preparedReason || 'No legacy prepared explanation'}
                      {item.compatibilityLabel ? ` · ${item.compatibilityLabel}` : ''}
                    </p>
                    <div className="chips">
                      {(explanation.reasonCodes ?? []).map((code) => (
                        <Chip key={code} tone="ok">
                          {code}
                        </Chip>
                      ))}
                      {components.map(([name, value]) => (
                        <Chip key={name} tone={value < 0 ? 'bad' : undefined}>
                          {name}={signed(value)}
                        </Chip>
                      ))}
                    </div>
                    <details>
                      <summary className="muted">Pool, row and exposure detail</summary>
                      <p className="hint">
                        Base rank {num(item.baseRank ?? 0)} · base {Number(item.baseScore ?? 0).toFixed(3)} ·
                        affinity {Number(item.affinityScore ?? 0).toFixed(3)} · compatibility{' '}
                        {Number(item.compatibilityScore ?? 0).toFixed(3)} · impressions{' '}
                        {num(exposure.impressions ?? 0)} · focuses {num(exposure.focuses ?? 0)} · selects{' '}
                        {num(exposure.selects ?? 0)}
                      </p>
                      <div className="chips">
                        {(item.eligibleRows ?? []).map((row) => (
                          <Chip key={row} tone="ok">
                            {row}
                          </Chip>
                        ))}
                      </div>
                      {item.preparedEvidenceTitle ? (
                        <p className="hint">Prepared evidence: {item.preparedEvidenceTitle}</p>
                      ) : null}
                    </details>
                  </Card>
                );
              })}
            </Grid>
          )}
        </>
      ) : null}
    </>
  );
}
