import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';

import { MarkdownField } from '@/components/MarkdownField';
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 { useAuth } from '@/lib/auth';
import {
  MISSION_STATUS_ACCENT,
  MISSION_STATUS_LABEL,
  missionKeys,
  type AddScenariosPayload,
  type MemberPayload,
  type Mission,
  type MissionRoleHint,
  type MissionStatus,
  type SetMembersPayload,
  type TransitionPayload,
  type UpdateMissionPayload,
} from '@/lib/missions';
import type {
  ScenarioTemplate,
  ScenarioTemplateListResponse,
} from '@/lib/templates';
import { templateKeys } from '@/lib/templates';

const TABS = ['tests', 'members', 'synthesis', 'export'] as const;
type Tab = (typeof TABS)[number];

const ALLOWED_TRANSITIONS: Record<MissionStatus, MissionStatus[]> = {
  draft: ['in_progress', 'archived'],
  in_progress: ['completed', 'archived'],
  completed: ['archived'],
  archived: [],
};

const TRANSITION_BUTTON_ACCENT: Record<MissionStatus, 'cyan' | 'orange' | 'green' | 'teal'> = {
  draft: 'cyan',
  in_progress: 'orange',
  completed: 'green',
  archived: 'teal',
};

interface RosterUser {
  id: string;
  email: string;
  display_name: string | null;
}

interface RosterResponse {
  items: RosterUser[];
}

interface MemberSelection {
  user_id: string;
  role_hint: MissionRoleHint;
}

function useMission(id: string) {
  return useQuery({
    queryKey: missionKeys.detail(id),
    queryFn: () => apiGet<Mission>(`/missions/${id}`),
    enabled: !!id,
  });
}

function useScenarioCatalogue(enabled: boolean) {
  return useQuery({
    queryKey: templateKeys.scenarios(''),
    queryFn: () =>
      apiGet<ScenarioTemplateListResponse>('/scenario-templates?limit=500'),
    enabled,
  });
}

function useRoster(enabled: boolean) {
  return useQuery({
    queryKey: ['users', 'roster'],
    queryFn: () => apiGet<RosterResponse>('/users/roster'),
    enabled,
  });
}

function formatDateRange(start: string | null, end: string | null): string {
  if (!start && !end) return 'No dates set';
  if (start && end) return `${start} → ${end}`;
  return start ?? end ?? '';
}

// --------------------------------------------------------------------------- //
// Metadata edit modal                                                         //
// --------------------------------------------------------------------------- //

interface MetaEditModalProps {
  mission: Mission;
  open: boolean;
  onClose: () => void;
}

function MetaEditModal({ mission, open, onClose }: MetaEditModalProps) {
  const qc = useQueryClient();
  const [name, setName] = useState(mission.name);
  const [client, setClient] = useState(mission.client_target ?? '');
  const [dateStart, setDateStart] = useState(mission.date_start ?? '');
  const [dateEnd, setDateEnd] = useState(mission.date_end ?? '');
  const [description, setDescription] = useState(mission.description_md ?? '');

  // Reset form whenever the modal opens with a (potentially newer) mission.
  useEffect(() => {
    if (!open) return;
    setName(mission.name);
    setClient(mission.client_target ?? '');
    setDateStart(mission.date_start ?? '');
    setDateEnd(mission.date_end ?? '');
    setDescription(mission.description_md ?? '');
  }, [open, mission]);

  const update = useMutation({
    mutationFn: (body: UpdateMissionPayload) =>
      apiPut<Mission>(`/missions/${mission.id}`, body),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missionKeys.detail(mission.id) });
      qc.invalidateQueries({ queryKey: missionKeys.listPrefix() });
      onClose();
    },
  });

  const apiErr = update.error instanceof ApiError ? update.error : null;
  const nameInvalid = name.trim().length === 0;
  const datesInvalid = dateStart && dateEnd && dateEnd < dateStart;

  function submit() {
    update.mutate({
      name: name.trim(),
      client_target: client.trim() || null,
      date_start: dateStart || null,
      date_end: dateEnd || null,
      description_md: description.trim() || null,
    });
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title={mission.name}
      accent="cyan"
      size="3xl"
      testid="mission-edit-meta-modal"
    >
      <div className="flex flex-col gap-3 min-w-0">
        {apiErr && <Alert accent="red">{apiErr.message}</Alert>}
        <div className="grid grid-cols-1 gap-3 md:grid-cols-2">
          <TextField
            label="Name"
            required
            value={name}
            onChange={(e) => setName(e.target.value)}
            data-testid="meta-edit-name"
          />
          <TextField
            label="Client / target"
            value={client}
            onChange={(e) => setClient(e.target.value)}
            data-testid="meta-edit-client"
          />
          <TextField
            label="Start date"
            type="date"
            value={dateStart}
            onChange={(e) => setDateStart(e.target.value)}
            data-testid="meta-edit-date-start"
          />
          <TextField
            label="End date"
            type="date"
            value={dateEnd}
            onChange={(e) => setDateEnd(e.target.value)}
            data-testid="meta-edit-date-end"
          />
        </div>
        <MarkdownField
          label="ROE / Description"
          value={description}
          onChange={setDescription}
          data-testid="meta-edit-description"
        />
        {datesInvalid && (
          <p className="font-mono text-2xs text-red" data-testid="meta-edit-date-error">
            End date must be on or after start date.
          </p>
        )}
        <div className="flex items-center justify-end gap-2 pt-2">
          <Button accent="teal" variant="ghost" onClick={onClose}>
            Cancel
          </Button>
          <Button
            accent="green"
            onClick={submit}
            disabled={nameInvalid || !!datesInvalid || update.isPending}
            data-testid="meta-edit-save"
          >
            {update.isPending ? 'Saving…' : 'Save'}
          </Button>
        </div>
      </div>
    </Modal>
  );
}

// --------------------------------------------------------------------------- //
// Add-scenarios modal                                                         //
// --------------------------------------------------------------------------- //

interface AddScenariosModalProps {
  mission: Mission;
  open: boolean;
  onClose: () => void;
}

function AddScenariosModal({ mission, open, onClose }: AddScenariosModalProps) {
  const qc = useQueryClient();
  const [selected, setSelected] = useState<string[]>([]);
  const catalogue = useScenarioCatalogue(open);

  useEffect(() => {
    if (open) setSelected([]);
  }, [open]);

  const add = useMutation({
    mutationFn: (body: AddScenariosPayload) =>
      apiPost<Mission>(`/missions/${mission.id}/scenarios`, body),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missionKeys.detail(mission.id) });
      qc.invalidateQueries({ queryKey: missionKeys.listPrefix() });
      onClose();
    },
  });

  const apiErr = add.error instanceof ApiError ? add.error : null;

  function toggle(id: string) {
    setSelected((prev) =>
      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
    );
  }

  function submit() {
    add.mutate({ scenario_template_ids: selected });
  }

  const totalTestsToAdd = useMemo(() => {
    if (!catalogue.data) return 0;
    const by_id = new Map<string, ScenarioTemplate>(
      catalogue.data.items.map((sc) => [sc.id, sc] as const),
    );
    return selected.reduce((acc, id) => acc + (by_id.get(id)?.tests_count ?? 0), 0);
  }, [selected, catalogue.data]);

  return (
    <Modal
      open={open}
      onClose={onClose}
      title={`Add scenarios to ${mission.name}`}
      accent="cyan"
      size="3xl"
      testid="mission-add-scenarios-modal"
    >
      <div className="flex flex-col gap-3 min-w-0">
        {apiErr && <Alert accent="red">{apiErr.message}</Alert>}
        {catalogue.isError && <Alert accent="red">Failed to load scenarios.</Alert>}
        {catalogue.isLoading && (
          <p className="font-mono text-xs text-text-dim">Loading…</p>
        )}
        <p className="font-mono text-2xs text-text-dim">
          {selected.length} scenario{selected.length === 1 ? '' : 's'} ·{' '}
          {totalTestsToAdd} test{totalTestsToAdd === 1 ? '' : 's'} will be appended
          after the current {mission.scenarios_count}.
        </p>
        <ul
          className="grid grid-cols-1 gap-2 md:grid-cols-2"
          data-testid="add-scenarios-picker"
        >
          {catalogue.data?.items.map((sc) => {
            const isSelected = selected.includes(sc.id);
            return (
              <li key={sc.id}>
                <button
                  type="button"
                  className={`w-full rounded-md border ${
                    isSelected ? 'border-cyan text-cyan' : 'border-border text-text'
                  } bg-bg-card p-3 text-left font-mono text-xs hover:border-cyan`}
                  onClick={() => toggle(sc.id)}
                  data-testid={`add-scenario-toggle-${sc.id}`}
                  aria-pressed={isSelected}
                >
                  <div className="flex items-center justify-between">
                    <span className="text-text-bright">{sc.name}</span>
                    <Tag accent="purple">{sc.tests_count} tests</Tag>
                  </div>
                  {sc.description && (
                    <p className="mt-1 text-text-dim">{sc.description}</p>
                  )}
                </button>
              </li>
            );
          })}
        </ul>
        {catalogue.data && catalogue.data.items.length === 0 && (
          <p className="font-mono text-xs text-text-dim">
            No scenarios in the catalogue yet.
          </p>
        )}
        <div className="flex items-center justify-end gap-2 pt-2">
          <Button accent="teal" variant="ghost" onClick={onClose}>
            Cancel
          </Button>
          <Button
            accent="green"
            onClick={submit}
            disabled={selected.length === 0 || add.isPending}
            data-testid="add-scenarios-save"
          >
            {add.isPending ? 'Adding…' : `Add ${selected.length} scenario${selected.length === 1 ? '' : 's'}`}
          </Button>
        </div>
      </div>
    </Modal>
  );
}

// --------------------------------------------------------------------------- //
// Edit-members modal                                                          //
// --------------------------------------------------------------------------- //

interface EditMembersModalProps {
  mission: Mission;
  open: boolean;
  onClose: () => void;
}

function EditMembersModal({ mission, open, onClose }: EditMembersModalProps) {
  const qc = useQueryClient();
  const roster = useRoster(open);
  const [members, setMembers] = useState<MemberSelection[]>([]);

  useEffect(() => {
    if (!open) return;
    setMembers(
      mission.members.map((m) => ({
        user_id: m.user_id,
        role_hint: m.role_hint,
      })),
    );
  }, [open, mission]);

  const save = useMutation({
    mutationFn: (body: SetMembersPayload) =>
      apiPut<Mission>(`/missions/${mission.id}/members`, body),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missionKeys.detail(mission.id) });
      qc.invalidateQueries({ queryKey: missionKeys.listPrefix() });
      onClose();
    },
  });

  const apiErr = save.error instanceof ApiError ? save.error : null;

  function setRole(user_id: string, role_hint: MissionRoleHint) {
    setMembers((prev) =>
      prev.some((m) => m.user_id === user_id)
        ? prev.map((m) => (m.user_id === user_id ? { ...m, role_hint } : m))
        : [...prev, { user_id, role_hint }],
    );
  }

  function remove(user_id: string) {
    setMembers((prev) => prev.filter((m) => m.user_id !== user_id));
  }

  function submit() {
    const payload: SetMembersPayload = {
      members: members.map(
        (m): MemberPayload => ({ user_id: m.user_id, role_hint: m.role_hint }),
      ),
    };
    save.mutate(payload);
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title={`Members of ${mission.name}`}
      accent="cyan"
      size="3xl"
      testid="mission-edit-members-modal"
    >
      <div className="flex flex-col gap-3 min-w-0">
        {apiErr && <Alert accent="red">{apiErr.message}</Alert>}
        {roster.isError && <Alert accent="red">Failed to load roster.</Alert>}
        {roster.isLoading && (
          <p className="font-mono text-xs text-text-dim">Loading users…</p>
        )}
        <ul className="flex flex-col gap-2" data-testid="edit-members-picker">
          {roster.data?.items.map((u) => {
            const selected = members.find((m) => m.user_id === u.id);
            return (
              <li
                key={u.id}
                className="flex flex-wrap items-center justify-between gap-2 rounded-md border border-border bg-bg-card p-3"
                data-testid={`edit-member-row-${u.id}`}
              >
                <div>
                  <p className="font-mono text-xs text-text-bright">
                    {u.display_name ?? u.email}
                  </p>
                  {u.display_name && (
                    <p className="font-mono text-2xs text-text-dim">{u.email}</p>
                  )}
                </div>
                <div className="flex items-center gap-2">
                  <Button
                    accent="red"
                    variant={selected?.role_hint === 'red' ? 'solid' : 'outline'}
                    onClick={() => setRole(u.id, 'red')}
                    data-testid={`edit-member-${u.id}-red`}
                  >
                    Red
                  </Button>
                  <Button
                    accent="cyan"
                    variant={selected?.role_hint === 'blue' ? 'solid' : 'outline'}
                    onClick={() => setRole(u.id, 'blue')}
                    data-testid={`edit-member-${u.id}-blue`}
                  >
                    Blue
                  </Button>
                  {selected && (
                    <Button
                      accent="rose"
                      variant="ghost"
                      onClick={() => remove(u.id)}
                      data-testid={`edit-member-${u.id}-clear`}
                    >
                      ✕
                    </Button>
                  )}
                </div>
              </li>
            );
          })}
        </ul>
        <div className="flex items-center justify-end gap-2 pt-2">
          <Button accent="teal" variant="ghost" onClick={onClose}>
            Cancel
          </Button>
          <Button
            accent="green"
            onClick={submit}
            disabled={save.isPending}
            data-testid="edit-members-save"
          >
            {save.isPending ? 'Saving…' : 'Save members'}
          </Button>
        </div>
      </div>
    </Modal>
  );
}

// --------------------------------------------------------------------------- //
// Main page                                                                   //
// --------------------------------------------------------------------------- //

export function MissionDetailPage() {
  const params = useParams();
  const missionId = params.id ?? '';
  const navigate = useNavigate();
  const qc = useQueryClient();
  const { state } = useAuth();

  const canEdit =
    state.user?.is_admin ||
    state.user?.permissions.includes('mission.update') ||
    false;

  const [tab, setTab] = useState<Tab>('tests');
  const [editMeta, setEditMeta] = useState(false);
  const [addScenarios, setAddScenarios] = useState(false);
  const [editMembers, setEditMembers] = useState(false);
  const detail = useMission(missionId);

  const transition = useMutation({
    mutationFn: (body: TransitionPayload) =>
      apiPost<Mission>(`/missions/${missionId}/transition`, body),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missionKeys.detail(missionId) });
      qc.invalidateQueries({ queryKey: missionKeys.listPrefix() });
    },
  });

  const remove = useMutation({
    mutationFn: () => apiDelete<{ ok: true }>(`/missions/${missionId}`),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missionKeys.listPrefix() });
      navigate('/missions');
    },
  });

  const apiErr = detail.error instanceof ApiError ? detail.error : null;
  const m = detail.data;

  if (apiErr) {
    return (
      <section>
        <SectionHeader prefix="Mission" highlight="Not found" accent="rose" />
        <Alert accent="rose">{apiErr.message}</Alert>
      </section>
    );
  }
  if (!m) {
    return <p className="font-mono text-xs text-text-dim">Loading mission…</p>;
  }

  const accent = MISSION_STATUS_ACCENT[m.status];
  const allowedNext = ALLOWED_TRANSITIONS[m.status];

  return (
    <section data-testid={`mission-detail-${m.id}`}>
      <div className="flex items-baseline justify-between flex-wrap gap-3">
        <SectionHeader prefix="Mission" highlight={m.name} accent={accent} />
        <div className="flex items-center gap-2">
          <Tag accent={accent}>{MISSION_STATUS_LABEL[m.status]}</Tag>
          {canEdit && (
            <Button
              accent="cyan"
              variant="outline"
              onClick={() => setEditMeta(true)}
              data-testid="mission-edit-meta"
            >
              Edit
            </Button>
          )}
          {allowedNext.map((target) => (
            <Button
              key={target}
              accent={TRANSITION_BUTTON_ACCENT[target]}
              onClick={() => transition.mutate({ status: target })}
              data-testid={`mission-transition-${target}`}
              disabled={transition.isPending}
            >
              → {MISSION_STATUS_LABEL[target]}
            </Button>
          ))}
          <Button
            accent="rose"
            variant="ghost"
            onClick={() => {
              if (
                window.confirm(
                  `Soft-delete mission "${m.name}"? An admin can restore from the trash.`,
                )
              ) {
                remove.mutate();
              }
            }}
            data-testid="mission-delete"
            disabled={remove.isPending}
          >
            Delete
          </Button>
        </div>
      </div>

      <Card className="mb-4">
        <dl className="grid grid-cols-2 gap-3 md:grid-cols-4 font-mono text-2xs">
          <div>
            <dt className="text-text-dim uppercase tracking-wider2">Client</dt>
            <dd className="text-text-bright">{m.client_target ?? '—'}</dd>
          </div>
          <div>
            <dt className="text-text-dim uppercase tracking-wider2">Dates</dt>
            <dd className="text-text-bright">
              {formatDateRange(m.date_start, m.date_end)}
            </dd>
          </div>
          <div>
            <dt className="text-text-dim uppercase tracking-wider2">Scenarios</dt>
            <dd className="text-text-bright">{m.scenarios_count}</dd>
          </div>
          <div>
            <dt className="text-text-dim uppercase tracking-wider2">Tests</dt>
            <dd className="text-text-bright">{m.tests_count}</dd>
          </div>
        </dl>
        {m.description_md && (
          <pre className="mt-3 whitespace-pre-wrap font-mono text-xs text-text">{m.description_md}</pre>
        )}
      </Card>

      <nav className="flex gap-1 border-b border-border mb-4" aria-label="Mission tabs">
        {TABS.map((t) => (
          <button
            key={t}
            type="button"
            onClick={() => setTab(t)}
            data-testid={`mission-tab-${t}`}
            className={`px-3 py-2 font-mono text-2xs uppercase tracking-wider2 ${
              tab === t
                ? 'text-cyan border-b-2 border-cyan -mb-px'
                : 'text-text-dim hover:text-text-bright'
            }`}
          >
            {t}
          </button>
        ))}
      </nav>

      {tab === 'tests' && (
        <Card>
          <div className="flex items-baseline justify-between flex-wrap gap-2 mb-3">
            <p className="font-mono text-2xs text-text-dim">
              Snapshots are frozen at append time — editing a source template
              does not propagate.
            </p>
            {canEdit && (
              <Button
                accent="cyan"
                onClick={() => setAddScenarios(true)}
                data-testid="mission-add-scenarios"
              >
                + Add scenarios
              </Button>
            )}
          </div>
          {m.scenarios.length === 0 ? (
            <p className="font-mono text-xs text-text-dim">
              No scenarios snapshotted yet.
              {canEdit && ' Click "Add scenarios" to append one.'}
            </p>
          ) : (
            <div className="flex flex-col gap-4" data-testid="mission-scenarios">
              {m.scenarios.map((sc) => (
                <div
                  key={sc.id}
                  className="rounded-md border border-border bg-bg-card p-3"
                  data-testid={`mission-scenario-${sc.id}`}
                >
                  <div className="flex items-center gap-2 mb-2">
                    <Tag accent="cyan">#{sc.position + 1}</Tag>
                    <p className="font-mono text-xs text-text-bright">
                      {sc.snapshot_name}
                    </p>
                  </div>
                  {sc.snapshot_description && (
                    <p className="mb-2 font-mono text-2xs text-text-dim">
                      {sc.snapshot_description}
                    </p>
                  )}
                  <table className="w-full font-mono text-2xs">
                    <thead>
                      <tr className="text-text-dim uppercase tracking-wider2">
                        <th className="text-left py-1">#</th>
                        <th className="text-left py-1">Test</th>
                        <th className="text-left py-1">MITRE</th>
                        <th className="text-left py-1">OPSEC</th>
                        <th className="text-left py-1">State</th>
                      </tr>
                    </thead>
                    <tbody>
                      {sc.tests.map((t) => (
                        <tr
                          key={t.id}
                          className="border-t border-border/40"
                          data-testid={`mission-test-${t.id}`}
                        >
                          <td className="py-1 text-text-dim">{t.position + 1}</td>
                          <td className="py-1 text-text-bright">{t.snapshot_name}</td>
                          <td className="py-1">
                            <div className="flex flex-wrap gap-1">
                              {t.mitre_tags.map((tag) => (
                                <Tag
                                  accent="cyan"
                                  key={`${tag.kind}-${tag.external_id}`}
                                >
                                  {tag.external_id}
                                </Tag>
                              ))}
                            </div>
                          </td>
                          <td className="py-1 text-text">
                            {t.snapshot_opsec_level}
                          </td>
                          <td className="py-1">
                            <Tag
                              accent={
                                t.state === 'pending'
                                  ? 'teal'
                                  : t.state === 'executed'
                                    ? 'orange'
                                    : t.state === 'reviewed_by_blue'
                                      ? 'green'
                                      : 'rose'
                              }
                            >
                              {t.state}
                            </Tag>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              ))}
            </div>
          )}
        </Card>
      )}

      {tab === 'members' && (
        <Card>
          <div className="flex items-baseline justify-between flex-wrap gap-2 mb-3">
            <p className="font-mono text-2xs text-text-dim">
              Members see this mission and (for reds) can author red-side fields
              on its tests in M7+.
            </p>
            {canEdit && (
              <Button
                accent="cyan"
                onClick={() => setEditMembers(true)}
                data-testid="mission-edit-members"
              >
                Edit members
              </Button>
            )}
          </div>
          {m.members.length === 0 ? (
            <p className="font-mono text-xs text-text-dim">
              No members assigned.
              {canEdit && ' Click "Edit members" to add some.'}
            </p>
          ) : (
            <ul className="flex flex-col gap-2" data-testid="mission-members">
              {m.members.map((mb) => (
                <li
                  key={mb.user_id}
                  className="flex items-center justify-between rounded-md border border-border bg-bg-card p-3"
                  data-testid={`mission-member-${mb.user_id}`}
                >
                  <div>
                    <p className="font-mono text-xs text-text-bright">
                      {mb.user_display_name ?? mb.user_email}
                    </p>
                    {mb.user_display_name && (
                      <p className="font-mono text-2xs text-text-dim">
                        {mb.user_email}
                      </p>
                    )}
                  </div>
                  <Tag accent={mb.role_hint === 'red' ? 'red' : 'cyan'}>
                    {mb.role_hint}
                  </Tag>
                </li>
              ))}
            </ul>
          )}
        </Card>
      )}

      {tab === 'synthesis' && (
        <Card>
          <p className="font-mono text-xs text-text-dim">
            Reveal.js slide synthesis lands in M10.
          </p>
        </Card>
      )}

      {tab === 'export' && (
        <Card>
          <p className="font-mono text-xs text-text-dim">
            JSON / CSV exports land in M11.
          </p>
        </Card>
      )}

      <MetaEditModal mission={m} open={editMeta} onClose={() => setEditMeta(false)} />
      <AddScenariosModal
        mission={m}
        open={addScenarios}
        onClose={() => setAddScenarios(false)}
      />
      <EditMembersModal
        mission={m}
        open={editMembers}
        onClose={() => setEditMembers(false)}
      />
    </section>
  );
}
