"use client";

import { useActionState, useEffect, useState } from "react";
import { useFormStatus } from "react-dom";
import { useRouter } from "next/navigation";
import { FlaskConical, Pencil, Plus, Trash2 } from "lucide-react";
import { deleteRuleAction, saveRuleAction, testRuleAction, type RuleTestResult } from "@/app/actions/curricula";
import type { ActionResult } from "@/app/actions/shared";
import {
  Badge,
  buttonClass,
  Callout,
  Card,
  CardHeader,
  EmptyState,
  Field,
  inputClass,
  selectClass,
  TableShell,
  Td,
  Th,
} from "@/components/ui";
import { useToast } from "@/components/toast";
import { RULE_TYPES, type RuleType } from "@/types";

export interface RuleView {
  id: string;
  name: string;
  curriculum_id: string;
  rule_type: RuleType;
  prefix: string | null;
  regex_pattern: string | null;
  exact_student_number: string | null;
  numeric_start: number | null;
  numeric_end: number | null;
  priority: number;
  active: boolean;
  description: string | null;
}

const RULE_TYPE_LABELS: Record<RuleType, string> = {
  PREFIX: "Prefix",
  REGEX: "Regular expression",
  EXACT: "Exact student number",
  NUMERIC_RANGE: "Numeric range",
};

const initial: ActionResult = { ok: false, message: null };
const initialTest: ActionResult<RuleTestResult> = { ok: false, message: null };

function SaveButton({ editing }: { editing: boolean }) {
  const { pending } = useFormStatus();
  return (
    <button type="submit" className={buttonClass("primary", "sm")} disabled={pending}>
      {pending ? "Saving…" : editing ? "Save rule" : "Create rule"}
    </button>
  );
}

function ruleSummary(rule: RuleView): string {
  switch (rule.rule_type) {
    case "PREFIX":
      return `starts with "${rule.prefix ?? ""}"`;
    case "REGEX":
      return `matches /${rule.regex_pattern ?? ""}/`;
    case "EXACT":
      return `equals "${rule.exact_student_number ?? ""}"`;
    case "NUMERIC_RANGE":
      return `between ${rule.numeric_start ?? "?"} and ${rule.numeric_end ?? "?"}`;
    default:
      return "";
  }
}

export function RulesManager({
  rules,
  curricula,
  canManage,
}: {
  rules: RuleView[];
  curricula: { id: string; code: string; name: string; status: string }[];
  canManage: boolean;
}) {
  const router = useRouter();
  const { toast } = useToast();
  const [saveState, saveAction] = useActionState<ActionResult, FormData>(saveRuleAction, initial);
  const [deleteState, deleteAction] = useActionState<ActionResult, FormData>(deleteRuleAction, initial);
  const [testState, testAction] = useActionState<ActionResult<RuleTestResult>, FormData>(testRuleAction, initialTest);

  const [editing, setEditing] = useState<RuleView | null>(null);
  const [showForm, setShowForm] = useState(false);
  const [ruleType, setRuleType] = useState<RuleType>("PREFIX");
  const [confirmDelete, setConfirmDelete] = useState<string | null>(null);

  useEffect(() => {
    if (!saveState.message) return;
    toast({ tone: saveState.ok ? "success" : "error", title: saveState.message });
    if (saveState.ok) {
      setShowForm(false);
      setEditing(null);
      router.refresh();
    }
  }, [saveState, toast, router]);

  useEffect(() => {
    if (!deleteState.message) return;
    toast({ tone: deleteState.ok ? "success" : "error", title: deleteState.message });
    if (deleteState.ok) {
      setConfirmDelete(null);
      router.refresh();
    }
  }, [deleteState, toast, router]);

  const openCreate = () => {
    setEditing(null);
    setRuleType("PREFIX");
    setShowForm(true);
  };
  const openEdit = (rule: RuleView) => {
    setEditing(rule);
    setRuleType(rule.rule_type);
    setShowForm(true);
  };

  // Highlight equal-priority rules that resolve to different curricula.
  const conflictKeys = new Set<string>();
  const byPriority = new Map<number, RuleView[]>();
  for (const rule of rules.filter((r) => r.active)) {
    const bucket = byPriority.get(rule.priority) ?? [];
    bucket.push(rule);
    byPriority.set(rule.priority, bucket);
  }
  for (const [, bucket] of byPriority) {
    const distinct = new Set(bucket.map((r) => r.curriculum_id));
    if (bucket.length > 1 && distinct.size > 1) for (const rule of bucket) conflictKeys.add(rule.id);
  }

  const test = testState.ok ? testState.data : null;

  return (
    <div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_22rem]">
      <div className="space-y-5">
        <Card>
          <CardHeader
            title="Assignment rules"
            description={`${rules.length} rule(s). Higher priority wins.`}
            actions={
              canManage ? (
                <button type="button" onClick={openCreate} className={buttonClass("primary", "sm")}>
                  <Plus className="h-3.5 w-3.5" aria-hidden />
                  Add rule
                </button>
              ) : null
            }
          />
          {rules.length === 0 ? (
            <EmptyState
              title="No assignment rules configured"
              description="Without rules, every student number resolves as CURRICULUM_UNRESOLVED and must be assigned manually."
              action={
                canManage ? (
                  <button type="button" onClick={openCreate} className={buttonClass("primary")}>
                    <Plus className="h-4 w-4" aria-hidden />
                    Add the first rule
                  </button>
                ) : null
              }
            />
          ) : (
            <TableShell>
              <thead>
                <tr>
                  <Th align="right">Priority</Th>
                  <Th>Rule</Th>
                  <Th>Type</Th>
                  <Th>Matches</Th>
                  <Th>Curriculum</Th>
                  <Th>Status</Th>
                  {canManage ? <Th>Actions</Th> : null}
                </tr>
              </thead>
              <tbody>
                {rules.map((rule) => {
                  const curriculum = curricula.find((c) => c.id === rule.curriculum_id);
                  return (
                    <tr key={rule.id} className={conflictKeys.has(rule.id) ? "bg-rose-50/60" : "hover:bg-ink-50"}>
                      <Td align="right" className="tabular font-semibold">
                        {rule.priority}
                      </Td>
                      <Td>
                        <span className="font-medium text-ink-900">{rule.name}</span>
                        {rule.description ? (
                          <span className="block max-w-sm text-[11px] leading-snug text-ink-500">{rule.description}</span>
                        ) : null}
                        {conflictKeys.has(rule.id) ? (
                          <Badge tone="danger" className="mt-1">
                            Conflicts at this priority
                          </Badge>
                        ) : null}
                      </Td>
                      <Td className="text-[12px]">{RULE_TYPE_LABELS[rule.rule_type]}</Td>
                      <Td className="font-mono text-[12px] text-ink-600">{ruleSummary(rule)}</Td>
                      <Td className="text-[13px]">{curriculum?.code ?? "—"}</Td>
                      <Td>
                        {rule.active ? <Badge tone="success">Active</Badge> : <Badge tone="neutral">Inactive</Badge>}
                      </Td>
                      {canManage ? (
                        <Td>
                          <div className="flex gap-1">
                            <button
                              type="button"
                              onClick={() => openEdit(rule)}
                              className={buttonClass("ghost", "icon")}
                              aria-label={`Edit ${rule.name}`}
                            >
                              <Pencil className="h-3.5 w-3.5" aria-hidden />
                            </button>
                            {confirmDelete === rule.id ? (
                              <form action={deleteAction} className="flex items-center gap-1">
                                <input type="hidden" name="rule_id" value={rule.id} />
                                <button type="submit" className={buttonClass("danger", "sm")}>
                                  Confirm
                                </button>
                                <button
                                  type="button"
                                  onClick={() => setConfirmDelete(null)}
                                  className={buttonClass("ghost", "sm")}
                                >
                                  Cancel
                                </button>
                              </form>
                            ) : (
                              <button
                                type="button"
                                onClick={() => setConfirmDelete(rule.id)}
                                className={buttonClass("ghost", "icon")}
                                aria-label={`Delete ${rule.name}`}
                              >
                                <Trash2 className="h-3.5 w-3.5 text-rose-600" aria-hidden />
                              </button>
                            )}
                          </div>
                        </Td>
                      ) : null}
                    </tr>
                  );
                })}
              </tbody>
            </TableShell>
          )}
        </Card>

        {conflictKeys.size > 0 ? (
          <Callout tone="danger" title="Equal-priority rules point at different curricula">
            Student numbers matching more than one of these rules will resolve as CURRICULUM_RULE_CONFLICT and must be
            assigned manually. Give one rule a higher priority to resolve this.
          </Callout>
        ) : null}

        {showForm && canManage ? (
          <Card>
            <CardHeader title={editing ? `Edit rule — ${editing.name}` : "New assignment rule"} />
            <form action={saveAction} className="space-y-4 p-5">
              {editing ? <input type="hidden" name="rule_id" value={editing.id} /> : null}

              <div className="grid gap-4 sm:grid-cols-2">
                <Field label="Rule name" htmlFor="name" required error={saveState.fieldErrors?.name}>
                  <input
                    id="name"
                    name="name"
                    defaultValue={editing?.name ?? ""}
                    className={inputClass}
                    required
                    placeholder="e.g. 2024 intake prefix"
                  />
                </Field>
                <Field
                  label="Assign this curriculum"
                  htmlFor="curriculum_id"
                  required
                  error={saveState.fieldErrors?.curriculum_id}
                >
                  <select
                    id="curriculum_id"
                    name="curriculum_id"
                    defaultValue={editing?.curriculum_id ?? ""}
                    className={selectClass}
                    required
                  >
                    <option value="">Select a curriculum…</option>
                    {curricula.map((curriculum) => (
                      <option key={curriculum.id} value={curriculum.id}>
                        {curriculum.code} — {curriculum.name.slice(0, 60)}
                      </option>
                    ))}
                  </select>
                </Field>

                <Field label="Rule type" htmlFor="rule_type" required>
                  <select
                    id="rule_type"
                    name="rule_type"
                    value={ruleType}
                    onChange={(event) => setRuleType(event.target.value as RuleType)}
                    className={selectClass}
                  >
                    {RULE_TYPES.map((type) => (
                      <option key={type} value={type}>
                        {RULE_TYPE_LABELS[type]}
                      </option>
                    ))}
                  </select>
                </Field>

                <Field
                  label="Priority"
                  htmlFor="priority"
                  required
                  hint="Higher wins. Use distinct priorities to avoid conflicts."
                  error={saveState.fieldErrors?.priority}
                >
                  <input
                    id="priority"
                    name="priority"
                    type="number"
                    min={0}
                    max={10000}
                    defaultValue={editing?.priority ?? 100}
                    className={`${inputClass} tabular`}
                    required
                  />
                </Field>
              </div>

              {ruleType === "PREFIX" ? (
                <Field
                  label="Prefix"
                  htmlFor="prefix"
                  required
                  error={saveState.fieldErrors?.prefix}
                  hint="Compared against the normalised student number."
                >
                  <input
                    id="prefix"
                    name="prefix"
                    defaultValue={editing?.prefix ?? ""}
                    className={`${inputClass} font-mono`}
                    placeholder="2024"
                  />
                </Field>
              ) : null}

              {ruleType === "REGEX" ? (
                <Field
                  label="Regular expression"
                  htmlFor="regex_pattern"
                  required
                  error={saveState.fieldErrors?.regex_pattern}
                  hint="Anchor it yourself, e.g. ^2025\\d{4}$. Applied to the normalised student number."
                >
                  <input
                    id="regex_pattern"
                    name="regex_pattern"
                    defaultValue={editing?.regex_pattern ?? ""}
                    className={`${inputClass} font-mono`}
                    placeholder="^2025\\d{4}$"
                  />
                </Field>
              ) : null}

              {ruleType === "EXACT" ? (
                <Field
                  label="Exact student number"
                  htmlFor="exact_student_number"
                  required
                  error={saveState.fieldErrors?.exact_student_number}
                >
                  <input
                    id="exact_student_number"
                    name="exact_student_number"
                    defaultValue={editing?.exact_student_number ?? ""}
                    className={`${inputClass} font-mono`}
                  />
                </Field>
              ) : null}

              {ruleType === "NUMERIC_RANGE" ? (
                <div className="grid gap-4 sm:grid-cols-2">
                  <Field label="Range start" htmlFor="numeric_start" required error={saveState.fieldErrors?.numeric_start}>
                    <input
                      id="numeric_start"
                      name="numeric_start"
                      defaultValue={editing?.numeric_start ?? ""}
                      className={`${inputClass} tabular`}
                      inputMode="numeric"
                    />
                  </Field>
                  <Field label="Range end" htmlFor="numeric_end" required error={saveState.fieldErrors?.numeric_end}>
                    <input
                      id="numeric_end"
                      name="numeric_end"
                      defaultValue={editing?.numeric_end ?? ""}
                      className={`${inputClass} tabular`}
                      inputMode="numeric"
                    />
                  </Field>
                  <p className="text-[12px] text-ink-500 sm:col-span-2">
                    Only applied when the normalised student number is entirely numeric, so alphanumeric conventions are
                    never silently coerced.
                  </p>
                </div>
              ) : null}

              <Field label="Description" htmlFor="description">
                <input id="description" name="description" defaultValue={editing?.description ?? ""} className={inputClass} />
              </Field>

              <label className="flex items-center gap-2 text-[13px] text-ink-700">
                <input
                  type="checkbox"
                  name="active"
                  defaultChecked={editing ? editing.active : true}
                  className="h-4 w-4 rounded border-ink-300 text-brand-600 focus:ring-brand-500"
                />
                Rule is active
              </label>

              <div className="flex gap-2 border-t border-ink-200 pt-4">
                <SaveButton editing={!!editing} />
                <button
                  type="button"
                  onClick={() => {
                    setShowForm(false);
                    setEditing(null);
                  }}
                  className={buttonClass("ghost", "sm")}
                >
                  Cancel
                </button>
              </div>
            </form>
          </Card>
        ) : null}
      </div>

      {/* Rule tester */}
      <Card className="h-fit">
        <CardHeader title="Test a student number" description="Runs the saved active rules exactly as the system will." />
        <form action={testAction} className="space-y-3 p-5">
          <Field label="Sample student number" htmlFor="student_number">
            <input
              id="student_number"
              name="student_number"
              className={`${inputClass} tabular`}
              placeholder="e.g. 2024-0001"
              required
            />
          </Field>
          <button type="submit" className={buttonClass("secondary", "sm", "w-full")}>
            <FlaskConical className="h-3.5 w-3.5" aria-hidden />
            Test resolution
          </button>

          {test ? (
            <div className="space-y-2 pt-2">
              <p className="text-[12px] text-ink-500">
                Normalised: <span className="tabular font-mono text-ink-800">{test.normalized}</span>
              </p>
              {test.outcome === "MATCHED" ? (
                <Callout tone="success" title={`Resolves to ${test.curriculumCode ?? "—"}`}>
                  {test.message}
                </Callout>
              ) : test.outcome === "CONFLICT" ? (
                <Callout tone="danger" title="CURRICULUM_RULE_CONFLICT">
                  {test.message}
                </Callout>
              ) : (
                <Callout tone="warning" title="CURRICULUM_UNRESOLVED">
                  {test.message}
                </Callout>
              )}
              {test.matchedRuleNames.length > 0 ? (
                <p className="text-[12px] text-ink-500">Matched: {test.matchedRuleNames.join(", ")}</p>
              ) : null}
            </div>
          ) : testState.message && !testState.ok ? (
            <Callout tone="danger">{testState.message}</Callout>
          ) : null}
        </form>
      </Card>
    </div>
  );
}
