"use client";

import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";

import { api } from "@/lib/api/client";
import type { components } from "@/lib/api/schema";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";

type Person = components["schemas"]["PersonRead"];
type Relationship = components["schemas"]["RelationshipRead"];
type Event = components["schemas"]["EventRead"];

function splitName(full: string): { given: string | null; surname: string | null } {
  const t = full.trim().split(/\s+/).filter(Boolean);
  if (t.length === 0) return { given: null, surname: null };
  if (t.length === 1) return { given: t[0], surname: null };
  return { given: t.slice(0, -1).join(" "), surname: t[t.length - 1] };
}

type AddKind = "parent" | "child" | "partner";

export default function FamilyViewPage() {
  const router = useRouter();
  const params = useParams<{ id: string }>();
  const treeId = params.id;

  const [people, setPeople] = useState<Person[]>([]);
  const [rels, setRels] = useState<Relationship[]>([]);
  const [events, setEvents] = useState<Event[]>([]);
  const [ready, setReady] = useState(false);
  const [focusId, setFocusId] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [results, setResults] = useState<Person[] | null>(null); // server fuzzy search
  const [firstName, setFirstName] = useState("");
  // Inline add-relative form: which anchor + kind is open, and the typed name.
  // `key` keeps each empty slot's inline form independent (a person has 2
  // parents, 4 grandparents — many same-kind/anchor slots can coexist).
  const [adding, setAdding] = useState<{ key: string; kind: AddKind; anchor: string } | null>(null);
  const [addName, setAddName] = useState("");

  const load = useCallback(async () => {
    const p = await api.GET("/api/v1/trees/{tree_id}/persons", {
      params: { path: { tree_id: treeId } },
    });
    if (p.response.status === 401) {
      router.push("/login");
      return;
    }
    const [r, e] = await Promise.all([
      api.GET("/api/v1/trees/{tree_id}/relationships", { params: { path: { tree_id: treeId } } }),
      api.GET("/api/v1/trees/{tree_id}/events", { params: { path: { tree_id: treeId } } }),
    ]);
    const ppl = p.data ?? [];
    setPeople(ppl);
    setRels(r.data ?? []);
    setEvents(e.data ?? []);
    setFocusId((cur) => cur ?? ppl[0]?.id ?? null);
    setReady(true);
  }, [router, treeId]);

  useEffect(() => {
    load();
  }, [load]);

  // Debounced server-side fuzzy search (pg_trgm) across the whole tree.
  useEffect(() => {
    const q = search.trim();
    if (!q) {
      setResults(null);
      return;
    }
    const t = setTimeout(async () => {
      const { data } = await api.GET("/api/v1/trees/{tree_id}/persons", {
        params: { path: { tree_id: treeId }, query: { q } },
      });
      setResults(data ?? []);
    }, 250);
    return () => clearTimeout(t);
  }, [search, treeId]);

  const byId = useMemo(() => new Map(people.map((p) => [p.id, p])), [people]);
  const parentsOf = (id: string) =>
    rels.filter((r) => r.type === "parent_child" && r.person_to_id === id).map((r) => r.person_from_id);
  const childrenOf = (id: string) =>
    rels.filter((r) => r.type === "parent_child" && r.person_from_id === id).map((r) => r.person_to_id);
  const partnersOf = (id: string) =>
    rels
      .filter((r) => r.type === "partnership" && (r.person_from_id === id || r.person_to_id === id))
      .map((r) => (r.person_from_id === id ? r.person_to_id : r.person_from_id));

  const years = useMemo(() => {
    const m = new Map<string, string>();
    const yr = (e: Event) => (e.date_start ? e.date_start.slice(0, 4) : e.date_value ?? "");
    for (const p of people) {
      const b = events.find((e) => e.person_id === p.id && e.event_type === "birth");
      const d = events.find((e) => e.person_id === p.id && e.event_type === "death");
      const parts = [b ? yr(b) : "", d ? yr(d) : ""];
      if (parts[0] || parts[1]) m.set(p.id, `${parts[0]}–${parts[1]}`.replace(/^–$/, ""));
    }
    return m;
  }, [people, events]);

  async function addPerson(name: string): Promise<string | null> {
    const { given, surname } = splitName(name);
    const { data } = await api.POST("/api/v1/trees/{tree_id}/persons", {
      params: { path: { tree_id: treeId } },
      body: { given, surname },
    });
    return data?.id ?? null;
  }

  async function createFirst(e: React.FormEvent) {
    e.preventDefault();
    if (!firstName.trim()) return;
    const id = await addPerson(firstName);
    setFirstName("");
    if (id) setFocusId(id);
    load();
  }

  async function postRel(body: components["schemas"]["RelationshipCreate"]) {
    await api.POST("/api/v1/trees/{tree_id}/relationships", {
      params: { path: { tree_id: treeId } },
      body,
    });
  }

  // Create the relationship(s) connecting an (existing or new) person to anchor.
  async function createLink(kind: AddKind, anchor: string, personId: string) {
    if (kind === "parent") {
      await postRel({ type: "parent_child", person_from_id: personId, person_to_id: anchor, qualifier: "biological" });
    } else if (kind === "partner") {
      await postRel({ type: "partnership", person_from_id: anchor, person_to_id: personId });
    } else {
      // child: link to anchor, and to anchor's spouse too (so both parents show)
      await postRel({ type: "parent_child", person_from_id: anchor, person_to_id: personId, qualifier: "biological" });
      const partners = partnersOf(anchor);
      if (partners.length === 1) {
        await postRel({ type: "parent_child", person_from_id: partners[0], person_to_id: personId, qualifier: "biological" });
      }
    }
  }

  async function linkExisting(personId: string) {
    if (!adding) return;
    await createLink(adding.kind, adding.anchor, personId);
    setAdding(null);
    setAddName("");
    load();
  }

  async function submitAdd(e: React.FormEvent) {
    e.preventDefault();
    if (!adding || !addName.trim()) return;
    const newId = await addPerson(addName);
    if (newId) await createLink(adding.kind, adding.anchor, newId);
    setAdding(null);
    setAddName("");
    load();
  }

  if (!ready) return <p className="text-[var(--muted)]">Loading…</p>;

  if (people.length === 0) {
    return (
      <div className="space-y-6">
        <h1 className="text-2xl font-semibold">Start your tree</h1>
        <Card>
          <CardContent className="p-6">
            <form onSubmit={createFirst} className="flex flex-wrap gap-2">
              <Input
                className="w-64"
                placeholder="First person's full name"
                value={firstName}
                onChange={(e) => setFirstName(e.target.value)}
              />
              <Button type="submit">Add person</Button>
            </form>
          </CardContent>
        </Card>
      </div>
    );
  }

  const focus = focusId ? byId.get(focusId) : undefined;
  if (!focus) {
    setFocusId(people[0].id);
    return null;
  }

  const PersonBox = ({
    id,
    muted,
  }: {
    id: string;
    muted?: boolean;
  }) => {
    const p = byId.get(id);
    if (!p) return null;
    const isFocus = id === focusId;
    return (
      <button
        onClick={() => setFocusId(id)}
        className={`w-44 rounded-lg border px-3 py-2 text-left transition-colors ${
          isFocus
            ? "border-bronze bg-bronze/[0.08]"
            : "border-[var(--border)] bg-[var(--surface)] hover:border-bronze/60"
        } ${muted ? "opacity-90" : ""}`}
      >
        <div className="truncate text-sm font-medium">{p.primary_name ?? "Unnamed"}</div>
        <div className="text-xs text-[var(--muted)]">{years.get(id) ?? "—"}</div>
      </button>
    );
  };

  const AddSlot = ({
    formKey,
    kind,
    anchor,
    label,
  }: {
    formKey: string;
    kind: AddKind;
    anchor: string;
    label: string;
  }) =>
    adding?.key === formKey ? (
      <form onSubmit={submitAdd} className="flex w-56 flex-col gap-1">
        <Input
          autoFocus
          className="h-9"
          placeholder="Search existing or type a new name"
          value={addName}
          onChange={(e) => setAddName(e.target.value)}
        />
        {addName.trim() && (
          <div className="overflow-hidden rounded-md border border-[var(--border)] bg-[var(--surface)] text-sm">
            {people
              .filter(
                (p) =>
                  p.id !== anchor &&
                  (p.primary_name ?? "").toLowerCase().includes(addName.trim().toLowerCase()),
              )
              .slice(0, 6)
              .map((p) => (
                <button
                  key={p.id}
                  type="button"
                  onClick={() => linkExisting(p.id)}
                  className="flex w-full items-center justify-between gap-2 px-2 py-1.5 text-left hover:bg-bronze/[0.07]"
                >
                  <span className="truncate">{p.primary_name ?? "Unnamed"}</span>
                  <span className="shrink-0 text-xs text-[var(--muted)]">{years.get(p.id) ?? ""}</span>
                </button>
              ))}
            <button
              type="submit"
              className="flex w-full items-center gap-1 border-t border-[var(--border)] px-2 py-1.5 text-left text-bronze hover:bg-bronze/[0.07]"
            >
              + Create new “{addName.trim()}”
            </button>
          </div>
        )}
        <button type="button" onClick={() => setAdding(null)} className="text-xs text-[var(--muted)]">
          cancel
        </button>
      </form>
    ) : (
      <button
        onClick={() => {
          setAdding({ key: formKey, kind, anchor });
          setAddName("");
        }}
        className="w-44 rounded-lg border border-dashed border-[var(--border)] px-3 py-2 text-left text-sm text-[var(--muted)] hover:border-bronze hover:text-bronze"
      >
        + {label}
      </button>
    );

  // Recursive ancestor chart (grows rightward): a node is its box plus a
  // two-leaf "branch" of its parents, with CSS bracket connectors. Depth 0 =
  // focus, capped at grandparents (depth 2).
  const renderNode = (
    slotPersonId: string | null,
    childId: string,
    keyPrefix: string,
    depth: number,
  ): React.ReactNode => {
    const box = slotPersonId ? (
      <PersonBox id={slotPersonId} muted={depth > 0} />
    ) : (
      <AddSlot formKey={keyPrefix} kind="parent" anchor={childId} label="add parent" />
    );
    if (!slotPersonId || depth >= 2) {
      return <div className="ped-person">{box}</div>;
    }
    const ps = parentsOf(slotPersonId);
    return (
      <div className="ped-person">
        <div className="ped-self">{box}</div>
        <div className="ped-branch">
          <div className="ped-leaf">
            {renderNode(ps[0] ?? null, slotPersonId, `${keyPrefix}-a`, depth + 1)}
          </div>
          <div className="ped-leaf">
            {renderNode(ps[1] ?? null, slotPersonId, `${keyPrefix}-b`, depth + 1)}
          </div>
        </div>
      </div>
    );
  };

  const partners = partnersOf(focus.id);
  const children = childrenOf(focus.id);

  const sorted = [...people].sort((a, b) =>
    (a.primary_name ?? "").localeCompare(b.primary_name ?? ""),
  );
  // Server fuzzy results when searching; otherwise the loaded set.
  const directory = results ?? sorted;
  const shown = directory.slice(0, 200); // cap DOM nodes; refine search to narrow

  return (
    <div className="space-y-8">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <h1 className="text-2xl font-semibold">Family view</h1>
        <Link
          href={`/trees/${treeId}/persons/${focus.id}`}
          className="text-sm text-bronze hover:underline"
        >
          Open {focus.primary_name ?? "person"} →
        </Link>
      </div>

      {/* Pedigree: focus → parents → grandparents, with bracket connectors */}
      <Card>
        <CardContent className="overflow-x-auto p-6">
          <div className="min-w-[44rem]">{renderNode(focus.id, focus.id, "ped", 0)}</div>
        </CardContent>
      </Card>

      {/* Family group: partners + children of the focus */}
      <div className="grid gap-5 sm:grid-cols-2">
        <Card>
          <CardContent className="space-y-3 p-6">
            <h2 className="font-serif text-base font-semibold">Spouses &amp; partners</h2>
            <div className="flex flex-wrap gap-3">
              {partners.map((id) => (
                <PersonBox key={id} id={id} muted />
              ))}
              <AddSlot
                formKey={`partner-${focus.id}`}
                kind="partner"
                anchor={focus.id}
                label="add spouse"
              />
            </div>
          </CardContent>
        </Card>

        <Card>
          <CardContent className="space-y-3 p-6">
            <h2 className="font-serif text-base font-semibold">Children</h2>
            <div className="flex flex-wrap gap-3">
              {children.map((id) => (
                <PersonBox key={id} id={id} muted />
              ))}
              <AddSlot
                formKey={`child-${focus.id}`}
                kind="child"
                anchor={focus.id}
                label="add child"
              />
            </div>
          </CardContent>
        </Card>
      </div>

      {/* Scrollable, searchable people directory (scales to large trees) */}
      <div className="space-y-3">
        <div className="flex flex-wrap items-center justify-between gap-3">
          <h2 className="font-serif text-base font-semibold">People ({people.length})</h2>
          <Input
            className="w-64"
            placeholder="Search by name…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>
        <Card className="overflow-hidden">
          <div className="max-h-96 overflow-y-auto">
            {shown.length === 0 ? (
              <div className="px-4 py-6 text-sm text-[var(--muted)]">No matches.</div>
            ) : (
              shown.map((p, i) => (
                <button
                  key={p.id}
                  onClick={() => setFocusId(p.id)}
                  className={`flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left text-sm transition-colors ${
                    i > 0 ? "border-t border-[var(--border)]" : ""
                  } ${p.id === focusId ? "bg-bronze/[0.08]" : "hover:bg-bronze/[0.05]"}`}
                >
                  <span className="truncate font-medium">{p.primary_name ?? "Unnamed"}</span>
                  <span className="shrink-0 text-xs text-[var(--muted)]">
                    {years.get(p.id) ?? ""}
                  </span>
                </button>
              ))
            )}
          </div>
          {directory.length > shown.length && (
            <div className="border-t border-[var(--border)] bg-[var(--surface)] px-4 py-2 text-xs text-[var(--muted)]">
              Showing {shown.length} of {directory.length} — refine your search to narrow.
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}
