"use client";

import { useDeferredValue, useEffect, useState } from "react";
import { Field } from "@/components/shared/field";
import { Button } from "@/components/ui/button";
import type { Investor, InvestorGroup } from "./investor-model";
import type { FormEvent } from "react";

type Candidate = Pick<Investor, "id" | "name" | "email">;
type CandidateState =
  | { status: "loading"; candidates: Candidate[] }
  | { status: "ready"; candidates: Candidate[] }
  | { status: "error"; candidates: Candidate[]; message: string };

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function parseCandidates(value: unknown): Candidate[] | null {
  if (
    !isRecord(value) ||
    value.ok !== true ||
    !Array.isArray(value.candidates)
  )
    return null;
  const candidates: Candidate[] = [];
  for (const item of value.candidates) {
    if (
      !isRecord(item) ||
      typeof item.id !== "string" ||
      !item.id ||
      typeof item.name !== "string" ||
      !item.name ||
      typeof item.email !== "string" ||
      !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(item.email)
    )
      return null;
    candidates.push({ id: item.id, name: item.name, email: item.email });
  }
  return candidates;
}

async function responseError(response: Response, fallback: string) {
  try {
    const body: unknown = await response.json();
    if (
      isRecord(body) &&
      typeof body.error === "string" &&
      body.error.length <= 300
    )
      return body.error;
  } catch {
    // Non-JSON errors use the safe fallback below.
  }
  return fallback;
}

export function InvestorGroupMemberForm({
  group,
  onCancel,
  onAdded,
  onPendingChange,
}: {
  group: InvestorGroup;
  onCancel: () => void;
  onAdded: (candidate: Candidate) => void;
  onPendingChange: (pending: boolean) => void;
}) {
  const groupId = group.id;
  const [candidateState, setCandidateState] = useState<CandidateState>({
    status: "loading",
    candidates: [],
  });
  const [search, setSearch] = useState("");
  const [selectedId, setSelectedId] = useState("");
  const [pending, setPending] = useState(false);
  const [submitError, setSubmitError] = useState("");
  const [retryVersion, setRetryVersion] = useState(0);
  const deferredSearch = useDeferredValue(search.trim().toLowerCase());

  useEffect(() => {
    let active = true;
    const controller = new AbortController();
    const timeout = window.setTimeout(() => controller.abort(), 50_000);

    void (async () => {
      try {
        const response = await fetch(
          `/api/organization/investor-groups/${encodeURIComponent(groupId)}/users`,
          { signal: controller.signal, cache: "no-store" },
        );
        if (!response.ok)
          throw new Error(
            await responseError(
              response,
              "Eligible investors could not be loaded. Please try again.",
            ),
          );
        const candidates = parseCandidates(await response.json());
        if (!candidates)
          throw new Error(
            "The investor list returned an unsupported response. Please try again.",
          );
        if (active) setCandidateState({ status: "ready", candidates });
      } catch (issue) {
        if (active && !controller.signal.aborted)
          setCandidateState({
            status: "error",
            candidates: [],
            message:
              issue instanceof Error
                ? issue.message
                : "Eligible investors could not be loaded. Please try again.",
          });
      } finally {
        window.clearTimeout(timeout);
      }
    })();

    return () => {
      active = false;
      controller.abort();
      window.clearTimeout(timeout);
    };
  }, [groupId, retryVersion]);

  const candidates = candidateState.candidates;
  const filtered = candidates.filter((candidate) =>
    `${candidate.name} ${candidate.email}`
      .toLowerCase()
      .includes(deferredSearch),
  );
  const selected = candidates.find((candidate) => candidate.id === selectedId);

  async function submit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    if (!selected) {
      setSubmitError("Choose an investor to add to this group.");
      return;
    }
    setSubmitError("");
    setPending(true);
    onPendingChange(true);
    try {
      const response = await fetch(
        `/api/organization/investor-groups/${encodeURIComponent(groupId)}/users`,
        {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ user_id: selected.id }),
          signal: AbortSignal.timeout(50_000),
        },
      );
      if (!response.ok)
        throw new Error(
          await responseError(
            response,
            "The investor could not be added to this group. Please try again.",
          ),
        );
      onAdded(selected);
    } catch (issue) {
      setSubmitError(
        issue instanceof Error
          ? issue.message
          : "The investor could not be added to this group. Please try again.",
      );
    } finally {
      setPending(false);
      onPendingChange(false);
    }
  }

  return (
    <form onSubmit={submit}>
      {candidateState.status === "loading" ? (
        <p
          role="status"
          aria-live="polite"
          className="py-8 text-center text-sm text-muted-foreground"
        >
          Loading eligible investors…
        </p>
      ) : candidateState.status === "error" ? (
        <div className="space-y-3 py-3">
          <p role="alert" className="text-sm text-destructive">
            {candidateState.message}
          </p>
          <Button
            type="button"
            variant="outline"
            onClick={() => {
              setCandidateState({ status: "loading", candidates: [] });
              setRetryVersion((version) => version + 1);
            }}
          >
            Try again
          </Button>
        </div>
      ) : candidates.length === 0 ? (
        <p className="py-8 text-center text-sm text-muted-foreground">
          No eligible investors are available to add to this group.
        </p>
      ) : (
        <div className="space-y-3">
          <Field
            label="Search investors"
            value={search}
            onChange={(event) => {
              setSearch(event.target.value);
              setSelectedId("");
              setSubmitError("");
            }}
            placeholder="Name or email"
            autoComplete="off"
            disabled={pending}
          />
          <fieldset disabled={pending} aria-busy={pending}>
            <legend className="sr-only">Select an investor</legend>
            <div className="max-h-64 overflow-y-auto rounded-lg border">
              {filtered.map((candidate) => (
                <label
                  key={candidate.id}
                  className="flex cursor-pointer items-center gap-3 border-b px-3 py-3 last:border-b-0 hover:bg-muted/50 has-checked:bg-primary/5"
                >
                  <input
                    type="radio"
                    name="investor"
                    value={candidate.id}
                    checked={selectedId === candidate.id}
                    onChange={() => setSelectedId(candidate.id)}
                    className="size-4 accent-primary"
                  />
                  <span className="min-w-0">
                    <span className="block break-words text-sm font-medium">
                      {candidate.name}
                    </span>
                    <span className="block break-all text-xs text-muted-foreground">
                      {candidate.email}
                    </span>
                  </span>
                </label>
              ))}
              {filtered.length === 0 && (
                <div className="space-y-2 px-4 py-6 text-center text-sm text-muted-foreground">
                  <p>No investors match “{search.trim()}”.</p>
                  <Button
                    type="button"
                    variant="link"
                    className="h-auto p-0"
                    onClick={() => setSearch("")}
                  >
                    Clear search
                  </Button>
                </div>
              )}
            </div>
          </fieldset>
          {submitError && (
            <p role="alert" className="text-sm text-destructive">
              {submitError}
            </p>
          )}
        </div>
      )}
      <div className="mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
        <Button
          type="button"
          variant="outline"
          onClick={onCancel}
          disabled={pending}
        >
          Cancel
        </Button>
        {candidateState.status === "ready" && candidates.length > 0 && (
          <Button type="submit" disabled={pending || !selected}>
            {pending ? "Adding…" : "Add investor"}
          </Button>
        )}
      </div>
    </form>
  );
}
