import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';

import { MarkdownField } from '@/components/MarkdownField';
import { MitreTagPicker } from '@/components/MitreTagPicker';
import { Alert } from '@/components/ui/Alert';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import { Modal } from '@/components/ui/Modal';
import { SectionHeader } from '@/components/ui/SectionHeader';
import { Tag } from '@/components/ui/Tag';
import { TextField } from '@/components/ui/TextField';
import {
  ApiError,
  apiDelete,
  apiGet,
  apiPost,
  apiPut,
} from '@/lib/api';
import { type MitreTag, type MitreTagKind } from '@/lib/mitre';
import {
  buildTestQueryString,
  templateKeys,
  type CreateTestTemplatePayload,
  type OpsecLevel,
  type TestTemplate,
  type TestTemplateFilters,
  type TestTemplateListResponse,
} from '@/lib/templates';

const OPSEC_LEVELS: OpsecLevel[] = ['low', 'medium', 'high'];

interface FormState {
  name: string;
  description: string;
  objective: string;
  procedure_md: string;
  prerequisites_md: string;
  expected_result_red_md: string;
  expected_detection_blue_md: string;
  opsec_level: OpsecLevel;
  tags: string;
  expected_iocs: string;
  mitre_tags: MitreTag[];
}

function blankForm(): FormState {
  return {
    name: '',
    description: '',
    objective: '',
    procedure_md: '',
    prerequisites_md: '',
    expected_result_red_md: '',
    expected_detection_blue_md: '',
    opsec_level: 'medium',
    tags: '',
    expected_iocs: '',
    mitre_tags: [],
  };
}

function toForm(t: TestTemplate): FormState {
  return {
    name: t.name,
    description: t.description ?? '',
    objective: t.objective ?? '',
    procedure_md: t.procedure_md ?? '',
    prerequisites_md: t.prerequisites_md ?? '',
    expected_result_red_md: t.expected_result_red_md ?? '',
    expected_detection_blue_md: t.expected_detection_blue_md ?? '',
    opsec_level: t.opsec_level,
    tags: t.tags.join(', '),
    expected_iocs: t.expected_iocs.join(', '),
    mitre_tags: t.mitre_tags.map((tag) => ({
      kind: tag.kind as MitreTagKind,
      id: tag.external_id,
      external_id: tag.external_id,
      name: tag.name,
    })),
  };
}

function csvToList(s: string): string[] {
  return s.split(',').map((x) => x.trim()).filter(Boolean);
}

function toPayload(form: FormState): CreateTestTemplatePayload {
  return {
    name: form.name.trim(),
    description: form.description || null,
    objective: form.objective || null,
    procedure_md: form.procedure_md || null,
    prerequisites_md: form.prerequisites_md || null,
    expected_result_red_md: form.expected_result_red_md || null,
    expected_detection_blue_md: form.expected_detection_blue_md || null,
    opsec_level: form.opsec_level,
    tags: csvToList(form.tags),
    expected_iocs: csvToList(form.expected_iocs),
    mitre_tags: form.mitre_tags.map((t) => ({ kind: t.kind, external_id: t.external_id })),
  };
}

function useTestTemplates(filters: TestTemplateFilters) {
  return useQuery({
    queryKey: templateKeys.tests(filters),
    queryFn: () =>
      apiGet<TestTemplateListResponse>(`/test-templates${buildTestQueryString(filters)}`),
  });
}

export function AdminTestsPage() {
  const qc = useQueryClient();
  const [filters, setFilters] = useState<TestTemplateFilters>({});
  const [editing, setEditing] = useState<TestTemplate | null>(null);
  const [creating, setCreating] = useState(false);
  const [form, setForm] = useState<FormState>(blankForm());
  const [error, setError] = useState<string | null>(null);

  const tests = useTestTemplates(filters);

  const create = useMutation({
    mutationFn: (payload: CreateTestTemplatePayload) =>
      apiPost<TestTemplate>('/test-templates', payload),
    onSuccess: async () => {
      setCreating(false);
      setForm(blankForm());
      setError(null);
      await qc.invalidateQueries({ queryKey: ['templates', 'tests'] });
    },
    onError: (e) => setError(humanError(e)),
  });

  const update = useMutation({
    mutationFn: ({ id, payload }: { id: string; payload: CreateTestTemplatePayload }) =>
      apiPut<TestTemplate>(`/test-templates/${id}`, payload),
    onSuccess: async () => {
      setEditing(null);
      setError(null);
      await qc.invalidateQueries({ queryKey: ['templates', 'tests'] });
    },
    onError: (e) => setError(humanError(e)),
  });

  const remove = useMutation({
    mutationFn: (id: string) => apiDelete<{ ok: boolean }>(`/test-templates/${id}`),
    onSuccess: async () => {
      await qc.invalidateQueries({ queryKey: ['templates', 'tests'] });
    },
  });

  function openCreate() {
    setForm(blankForm());
    setError(null);
    setCreating(true);
  }

  function openEdit(t: TestTemplate) {
    setForm(toForm(t));
    setError(null);
    setEditing(t);
  }

  function submit() {
    const payload = toPayload(form);
    if (!payload.name) {
      setError('Name is required.');
      return;
    }
    if (editing) update.mutate({ id: editing.id, payload });
    else create.mutate(payload);
  }

  const isModalOpen = creating || editing !== null;

  return (
    <>
      <SectionHeader
        prefix="Admin"
        highlight="Tests"
        accent="orange"
        description="Reusable test units. Each test belongs to a scenario at instantiation time, but the catalogue lives independently."
      />

      <div className="mb-6 flex flex-wrap items-end gap-3" data-testid="tests-filters">
        <TextField
          label="Search"
          placeholder="name or description"
          value={filters.q ?? ''}
          onChange={(e) => setFilters((f) => ({ ...f, q: e.target.value || undefined }))}
          data-testid="filter-q"
        />
        <TextField
          label="Tactic external_id"
          placeholder="TA0006"
          value={filters.tactic ?? ''}
          onChange={(e) => setFilters((f) => ({ ...f, tactic: e.target.value || undefined }))}
          data-testid="filter-tactic"
        />
        <label className="block">
          <span className="block font-mono text-3xs font-semibold uppercase tracking-wider2 text-text-dim">
            OPSEC
          </span>
          <select
            value={filters.opsec ?? ''}
            onChange={(e) =>
              setFilters((f) => ({ ...f, opsec: (e.target.value as OpsecLevel | '') || undefined }))
            }
            className="mt-1 rounded-md border border-border bg-bg-card px-3 py-2 font-mono text-xs text-text-bright"
            data-testid="filter-opsec"
          >
            <option value="">— all —</option>
            {OPSEC_LEVELS.map((lv) => (
              <option key={lv} value={lv}>{lv}</option>
            ))}
          </select>
        </label>
        <TextField
          label="Free tag"
          placeholder="phish"
          value={filters.tag ?? ''}
          onChange={(e) => setFilters((f) => ({ ...f, tag: e.target.value || undefined }))}
          data-testid="filter-tag"
        />
        <Button accent="orange" onClick={openCreate} data-testid="create-test" className="ml-auto">
          + New test
        </Button>
      </div>

      {tests.isError && <Alert accent="red">Failed to load tests.</Alert>}

      <div className="grid gap-3" data-testid="tests-list">
        {tests.isLoading && <p className="font-mono text-xs text-text-dim">Loading…</p>}
        {tests.data?.items.map((t) => (
          <Card
            key={t.id}
            accent="orange"
            title={t.name}
            sub={t.description ?? '—'}
            data-testid={`test-row-${t.id}`}
          >
            <div className="flex flex-wrap items-center gap-2">
              <Tag accent={t.opsec_level === 'high' ? 'red' : t.opsec_level === 'low' ? 'green' : 'yellow'}>
                opsec: {t.opsec_level}
              </Tag>
              {t.mitre_tags.map((tag) => (
                <Tag
                  key={`${tag.kind}:${tag.external_id}`}
                  accent={tag.kind === 'tactic' ? 'cyan' : tag.kind === 'technique' ? 'orange' : 'purple'}
                >
                  {tag.external_id}
                </Tag>
              ))}
              {t.tags.map((tg) => (
                <Tag key={tg} accent="cyan">#{tg}</Tag>
              ))}
              <div className="ml-auto flex gap-2">
                <Button accent="orange" onClick={() => openEdit(t)} data-testid={`edit-test-${t.id}`}>
                  Edit
                </Button>
                <Button
                  accent="rose"
                  variant="ghost"
                  onClick={() => {
                    if (window.confirm(`Soft-delete "${t.name}"?`)) remove.mutate(t.id);
                  }}
                  data-testid={`delete-test-${t.id}`}
                >
                  Delete
                </Button>
              </div>
            </div>
          </Card>
        ))}
        {tests.data && tests.data.items.length === 0 && !tests.isLoading && (
          <p className="font-mono text-2xs text-text-dim">No tests match the current filters.</p>
        )}
      </div>

      <Modal
        open={isModalOpen}
        title={editing ? `Test · ${editing.name}` : 'New test template'}
        accent="orange"
        size="7xl"
        onClose={() => {
          setCreating(false);
          setEditing(null);
        }}
        testid="test-template-modal"
      >
        {error && <Alert accent="red" className="mb-3">{error}</Alert>}
        <div className="flex flex-col gap-3 min-w-0">
          <TextField
            label="Name"
            value={form.name}
            onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}
            data-testid="form-name"
          />
          <TextField
            label="Description"
            value={form.description}
            onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}
            data-testid="form-description"
          />
          <TextField
            label="Objective (1-liner)"
            value={form.objective}
            onChange={(e) => setForm((f) => ({ ...f, objective: e.target.value }))}
          />
          <MarkdownField
            label="Procedure"
            value={form.procedure_md}
            onChange={(v) => setForm((f) => ({ ...f, procedure_md: v }))}
            data-testid="form-procedure"
            hint="Step-by-step playbook. Markdown supported."
          />
          <MarkdownField
            label="Prerequisites"
            value={form.prerequisites_md}
            onChange={(v) => setForm((f) => ({ ...f, prerequisites_md: v }))}
          />
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            <MarkdownField
              label="Red — expected result"
              value={form.expected_result_red_md}
              onChange={(v) => setForm((f) => ({ ...f, expected_result_red_md: v }))}
              rows={4}
            />
            <MarkdownField
              label="Blue — expected detection"
              value={form.expected_detection_blue_md}
              onChange={(v) => setForm((f) => ({ ...f, expected_detection_blue_md: v }))}
              rows={4}
            />
          </div>
          <label className="block">
            <span className="block font-mono text-3xs font-semibold uppercase tracking-wider2 text-text-dim">
              OPSEC level
            </span>
            <select
              value={form.opsec_level}
              onChange={(e) => setForm((f) => ({ ...f, opsec_level: e.target.value as OpsecLevel }))}
              className="mt-1 rounded-md border border-border bg-bg-card px-3 py-2 font-mono text-xs text-text-bright"
              data-testid="form-opsec"
            >
              {OPSEC_LEVELS.map((lv) => (
                <option key={lv} value={lv}>{lv}</option>
              ))}
            </select>
          </label>
          <TextField
            label="Free tags (comma-separated)"
            value={form.tags}
            onChange={(e) => setForm((f) => ({ ...f, tags: e.target.value }))}
            data-testid="form-tags"
            hint="e.g. phish, persistence, quick-win"
          />
          <TextField
            label="Expected IOCs (comma-separated)"
            value={form.expected_iocs}
            onChange={(e) => setForm((f) => ({ ...f, expected_iocs: e.target.value }))}
            hint="Indicators the blue team should look for"
          />
          <div>
            <p className="font-mono text-3xs font-semibold uppercase tracking-wider2 text-text-dim mb-1">
              MITRE ATT&amp;CK tags
            </p>
            <MitreTagPicker
              value={form.mitre_tags}
              onChange={(next) => setForm((f) => ({ ...f, mitre_tags: next }))}
            />
          </div>
          <div className="flex justify-end gap-2 pt-2">
            <Button
              variant="ghost"
              onClick={() => {
                setCreating(false);
                setEditing(null);
              }}
            >
              Cancel
            </Button>
            <Button
              accent="orange"
              onClick={submit}
              disabled={create.isPending || update.isPending}
              data-testid="form-submit"
            >
              {editing ? 'Save' : 'Create'}
            </Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

function humanError(e: unknown): string {
  if (e instanceof ApiError) {
    const p = e.payload as { error?: string; message?: string } | null;
    return p?.message ?? p?.error ?? `HTTP ${e.status}`;
  }
  return e instanceof Error ? e.message : 'Unexpected error';
}
