import { FormEvent, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertTriangle, CheckCircle2, Clock3, Eye, GitBranch, Pencil, Play, Plus, Shield, Trash2 } from "lucide-react";

import { api, Policy, Project, ServiceCatalogItem } from "../api/client";
import { DataTable } from "../components/DataTable";
import { buttonClass, Field, iconButtonClass, inputClass, selectClass } from "../components/FormControls";
import { LoadingOverlay } from "../components/LoadingOverlay";
import { Modal } from "../components/Modal";
import { PageHeader } from "../components/PageHeader";

function policyValue(policy: Policy, key: string) {
  return String(policy.definition?.[key] ?? "");
}

function policyService(policy: Policy) {
  const service = policy.definition?.service;
  if (!service || typeof service !== "object") {
    return "";
  }
  const value = service as { protocol?: unknown; ports?: unknown };
  return `${String(value.protocol ?? "")}/${String(value.ports ?? "")}`;
}

function policyStatus(policy: Policy) {
  const status = policy.deployment_status ?? {};
  return {
    state: String(status.state ?? "unknown"),
    label: String(status.label ?? "Unknown"),
    expectedRules: Number(status.expected_rules ?? 0),
    activeRules: Number(status.active_rules ?? 0),
    staleRules: Number(status.stale_rules ?? 0),
  };
}

function statusClass(state: string) {
  if (state === "active") {
    return "border-accent/40 bg-accent/10 text-accent";
  }
  if (state === "audit") {
    return "border-amber-400/40 bg-amber-400/10 text-amber-300";
  }
  if (["stale", "partial", "unresolved"].includes(state)) {
    return "border-danger/40 bg-danger/10 text-danger";
  }
  return "border-border bg-canvas text-slate-500";
}

function StatusIcon({ state }: { state: string }) {
  if (state === "active") {
    return <CheckCircle2 size={15} />;
  }
  if (state === "audit") {
    return <Shield size={15} />;
  }
  if (["stale", "partial", "unresolved"].includes(state)) {
    return <AlertTriangle size={15} />;
  }
  return <Clock3 size={15} />;
}

function PolicyDeploymentStatus({ policy }: { policy: Policy }) {
  const status = policyStatus(policy);
  const detail =
    status.state === "audit"
      ? "No live firewall write"
      : `${status.activeRules}/${status.expectedRules} active${status.staleRules ? ` · ${status.staleRules} stale` : ""}`;
  return (
    <div className="flex flex-col gap-1">
      <span className={`inline-flex w-fit items-center gap-1.5 rounded-md border px-2 py-1 text-xs ${statusClass(status.state)}`}>
        <StatusIcon state={status.state} />
        {status.label}
      </span>
      <span className="text-xs text-slate-500">{detail}</span>
    </div>
  );
}

const defaultPolicyForm = {
  project_id: "",
  name: "Web to DB",
  source: "sg:Web Tier",
  destination: "sg:Database",
  service_id: "",
  protocol: "tcp",
  ports: "5432",
  action: "allow",
  direction: "egress",
  enforcement_mode: "enforced",
  logging: true,
  description: "Allow application database traffic",
};

export function Policies() {
  const queryClient = useQueryClient();
  const policies = useQuery({ queryKey: ["policies"], queryFn: () => api<Policy[]>("/policies") });
  const projects = useQuery({ queryKey: ["projects"], queryFn: () => api<Project[]>("/projects") });
  const services = useQuery({ queryKey: ["service-catalog"], queryFn: () => api<ServiceCatalogItem[]>("/service-catalog") });
  const [preview, setPreview] = useState("");
  const [open, setOpen] = useState(false);
  const [editing, setEditing] = useState<Policy | null>(null);
  const [form, setForm] = useState(defaultPolicyForm);
  const [busyMessage, setBusyMessage] = useState("");

  const save = useMutation({
    mutationFn: () => {
      const service = services.data?.find((item) => item.id === form.service_id);
      return api<Policy>(editing ? `/policies/${editing.id}` : "/policies", {
        method: editing ? "PATCH" : "POST",
        body: JSON.stringify({
          project_id: form.project_id || null,
          name: form.name,
          enabled: true,
          definition: {
            source: form.source,
            destination: form.destination,
            service: { protocol: service?.protocol ?? form.protocol, ports: service?.ports ?? form.ports },
            action: form.action,
            direction: form.direction,
            enforcement_mode: form.enforcement_mode,
            logging: form.logging,
            description: form.description,
          },
        }),
      });
    },
    onSuccess: () => {
      setOpen(false);
      setEditing(null);
      queryClient.invalidateQueries({ queryKey: ["policies"] });
    },
  });
  const remove = useMutation({
    mutationFn: (policy: Policy) => api(`/policies/${policy.id}`, { method: "DELETE" }),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ["policies"] }),
  });

  async function submit(event: FormEvent) {
    event.preventDefault();
    await save.mutateAsync();
  }

  async function compile(policy: Policy) {
    setBusyMessage(`Compiling ${policy.name}...`);
    try {
      const data = await api<Policy>(`/policies/${policy.id}/compile`, { method: "POST" });
      setPreview(JSON.stringify(data.last_compiled, null, 2));
      await queryClient.invalidateQueries({ queryKey: ["policies"] });
    } finally {
      setBusyMessage("");
    }
  }

  async function firewallPreview(policy: Policy) {
    setBusyMessage(`Generating preview for ${policy.name}...`);
    try {
      const data = await api<Record<string, unknown>>(`/firewall/preview/${policy.id}`, { method: "POST" });
      setPreview(JSON.stringify(data, null, 2));
    } finally {
      setBusyMessage("");
    }
  }

  function addPolicy() {
    setEditing(null);
    setForm(defaultPolicyForm);
    setOpen(true);
  }

  function editPolicy(policy: Policy) {
    const service = policy.definition?.service as { protocol?: string; ports?: string } | undefined;
    setEditing(policy);
    setForm({
      project_id: policy.project_id ?? "",
      name: policy.name,
      source: policyValue(policy, "source") || "any",
      destination: policyValue(policy, "destination") || "any",
      service_id: "",
      protocol: service?.protocol ?? "tcp",
      ports: service?.ports ?? "",
      action: policyValue(policy, "action") || "allow",
      direction: policyValue(policy, "direction") || "ingress",
      enforcement_mode: policy.enforcement_mode || "enforced",
      logging: Boolean(policy.definition?.logging),
      description: policyValue(policy, "description"),
    });
    setOpen(true);
  }

  function chooseService(serviceId: string) {
    const service = services.data?.find((item) => item.id === serviceId);
    setForm({
      ...form,
      service_id: serviceId,
      protocol: service?.protocol ?? form.protocol,
      ports: service?.ports ?? form.ports,
    });
  }

  function deletePolicy(policy: Policy) {
    if (window.confirm(`Delete policy "${policy.name}"?`)) {
      remove.mutate(policy);
    }
  }

  return (
    <>
      <PageHeader title="Policies" subtitle="Create versioned microsegmentation policies and compile firewall previews." />
      <LoadingOverlay open={Boolean(busyMessage)} message={busyMessage} />
      <div className="space-y-4">
        <button className={buttonClass} onClick={addPolicy}><Plus size={16} /> Add Policy</button>
        <Modal title={editing ? "Edit Policy" : "Add Policy"} open={open} onClose={() => setOpen(false)}>
          <form onSubmit={submit}>
          <div className="mb-4 flex items-center gap-2 font-medium"><GitBranch size={18} /> {editing ? "Edit Policy" : "Add Policy"}</div>
          <div className="grid gap-3">
            <Field label="Project"><select className={selectClass} value={form.project_id} onChange={(event) => setForm({ ...form, project_id: event.target.value })}><option value="">Global</option>{(projects.data ?? []).map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}</select></Field>
            <Field label="Name"><input className={inputClass} value={form.name} onChange={(event) => setForm({ ...form, name: event.target.value })} /></Field>
            <div className="grid grid-cols-2 gap-3">
              <Field label="Source"><input className={inputClass} value={form.source} onChange={(event) => setForm({ ...form, source: event.target.value })} /></Field>
              <Field label="Destination"><input className={inputClass} value={form.destination} onChange={(event) => setForm({ ...form, destination: event.target.value })} /></Field>
            </div>
            <Field label="Service"><select className={selectClass} value={form.service_id} onChange={(event) => chooseService(event.target.value)}><option value="">Custom</option>{(services.data ?? []).map((service) => <option key={service.id} value={service.id}>{service.name} {service.ports}</option>)}</select></Field>
            <div className="grid grid-cols-2 gap-3">
              <Field label="Protocol">
                <select className={selectClass} value={form.protocol} onChange={(event) => setForm({ ...form, protocol: event.target.value })}>
                  <option value="tcp">tcp</option>
                  <option value="udp">udp</option>
                  <option value="tcp/udp">tcp & udp</option>
                </select>
              </Field>
              <Field label="Ports"><input className={inputClass} value={form.ports} onChange={(event) => setForm({ ...form, ports: event.target.value })} /></Field>
            </div>
            <div className="grid grid-cols-2 gap-3">
              <Field label="Action"><select className={selectClass} value={form.action} onChange={(event) => setForm({ ...form, action: event.target.value })}><option>allow</option><option>deny</option><option>reject</option></select></Field>
              <Field label="Direction"><select className={selectClass} value={form.direction} onChange={(event) => setForm({ ...form, direction: event.target.value })}><option>ingress</option><option>egress</option></select></Field>
            </div>
            <Field label="Mode">
              <select className={selectClass} value={form.enforcement_mode} onChange={(event) => setForm({ ...form, enforcement_mode: event.target.value })}>
                <option value="enforced">enforced</option>
                <option value="audit">audit</option>
              </select>
            </Field>
            <Field label="Description"><input className={inputClass} value={form.description} onChange={(event) => setForm({ ...form, description: event.target.value })} /></Field>
            <button className={buttonClass}><Plus size={16} /> {editing ? "Update Policy" : "Save Policy"}</button>
          </div>
          </form>
        </Modal>
        <section className="space-y-4">
          <DataTable
            rows={(policies.data ?? []) as unknown as Record<string, unknown>[]}
            columns={[
              { key: "name", label: "Policy" },
              { key: "source", label: "Source", render: (row) => policyValue(row as unknown as Policy, "source") },
              { key: "destination", label: "Destination", render: (row) => policyValue(row as unknown as Policy, "destination") },
              { key: "service", label: "Service", render: (row) => policyService(row as unknown as Policy) },
              { key: "enforcement_mode", label: "Mode" },
              { key: "deployment_status", label: "Status", render: (row) => <PolicyDeploymentStatus policy={row as unknown as Policy} /> },
              { key: "version", label: "Version" },
              {
                key: "actions",
                label: "Actions",
                render: (row) => {
                  const policy = row as unknown as Policy;
                  return (
                    <div className="flex justify-end gap-2">
                      <button className={iconButtonClass} title="Compile policy" aria-label={`Compile ${policy.name}`} onClick={() => compile(policy)}><Play size={16} /></button>
                      <button className={iconButtonClass} title="Preview policy" aria-label={`Preview ${policy.name}`} onClick={() => firewallPreview(policy)}><Eye size={16} /></button>
                      <button className={iconButtonClass} title="Edit policy" aria-label={`Edit ${policy.name}`} onClick={() => editPolicy(policy)}><Pencil size={16} /></button>
                      <button className={iconButtonClass} title="Delete policy" aria-label={`Delete ${policy.name}`} disabled={remove.isPending} onClick={() => deletePolicy(policy)}><Trash2 size={16} /></button>
                    </div>
                  );
                },
              },
            ]}
          />
          <pre className="min-h-40 overflow-auto rounded-md border border-border bg-panel p-3 text-xs">{preview || "No policy output yet."}</pre>
        </section>
      </div>
    </>
  );
}
