/**
 * Missing precedents — citations the parties brought up but that aren't
 * yet in the corpus.
 *
 * Lifecycle: 'open' → researcher logs gap → chair uploads decision via
 * the dialog → POST /upload routes to internal_decision_upload (ערר/בל"מ)
 * or precedent_library_upload (court rulings), then status flips to
 * 'closed' with linked_case_law_id set.
 *
 * Endpoints touched:
 *   - POST   /api/missing-precedents               create (JSON body)
 *   - GET    /api/missing-precedents?status=open   list (filters)
 *   - GET    /api/missing-precedents/{id}          detail
 *   - PATCH  /api/missing-precedents/{id}          metadata edit
 *   - DELETE /api/missing-precedents/{id}          remove
 *   - POST   /api/missing-precedents/{id}/upload   multipart upload + close
 */

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { ApiError, apiRequest } from "./client";

export type CitedByParty =
  | "appellant"
  | "respondent"
  | "committee"
  | "permit_applicant"
  | "unknown";

export type MissingPrecedentStatus =
  | "open"
  | "uploaded"
  | "closed"
  | "irrelevant";

export type MissingPrecedent = {
  id: string;
  citation: string;
  case_name: string | null;
  cited_in_case_id: string | null;
  cited_in_case_number: string | null; // joined
  cited_in_document_id: string | null;
  cited_by_party: CitedByParty | null;
  cited_by_party_name: string | null;
  legal_topic: string | null;
  legal_issue: string | null;
  claim_quote: string | null;
  status: MissingPrecedentStatus;
  linked_case_law_id: string | null;
  linked_case_law_number: string | null;
  linked_case_law_name: string | null;
  closed_at: string | null;
  created_at: string;
  updated_at: string;
  notes: string | null;
  // provenance of the gap (#148) — how it surfaced, for the "צוטט ע״י" column.
  discovery_source: string | null; // manual | cited_only | digest | court_fetch
  cited_by_precedents: string[] | null; // corpus precedents citing a cited_only stub
  yomon_number: string | null; // digest gap's source yomon number
  // Bridge to the corpus citation graph: committee decisions (and their chairs)
  // that cite this still-missing ruling — fills "צוטט ע״י <יו״ר>" + "תיק".
  cited_by_chairs: string[] | null;
  cited_by_decisions: string[] | null;
};

export type MissingPrecedentListResponse = {
  items: MissingPrecedent[];
  count: number;
  by_status: Partial<Record<MissingPrecedentStatus, number>>;
  total_open: number;
};

export type MissingPrecedentCreateInput = {
  citation: string;
  case_number?: string;
  cited_in_document_id?: string;
  cited_by_party?: CitedByParty;
  cited_by_party_name?: string;
  legal_topic?: string;
  legal_issue?: string;
  claim_quote?: string;
  case_name?: string;
  notes?: string;
};

export type MissingPrecedentPatch = Partial<{
  legal_topic: string;
  legal_issue: string;
  notes: string;
  cited_by_party: CitedByParty;
  cited_by_party_name: string;
  case_name: string;
  status: MissingPrecedentStatus;
  citation: string;
  claim_quote: string;
}>;

export type MissingPrecedentFilters = {
  status?: MissingPrecedentStatus | "";
  caseNumber?: string;
  caseId?: string;
  legalTopic?: string;
  /** Free-text term — matched across the gap's מראה-מקום, case name, and
   *  cited-in appeal number (find a gap by the missing decision's number). */
  q?: string;
  limit?: number;
};

export const missingPrecedentKeys = {
  all: ["missing-precedents"] as const,
  list: (filters: MissingPrecedentFilters) =>
    [...missingPrecedentKeys.all, "list", filters] as const,
  detail: (id: string) => [...missingPrecedentKeys.all, "detail", id] as const,
};

export function useMissingPrecedents(filters: MissingPrecedentFilters = {}) {
  return useQuery({
    queryKey: missingPrecedentKeys.list(filters),
    queryFn: ({ signal }) => {
      const p = new URLSearchParams();
      if (filters.status) p.set("status", filters.status);
      if (filters.caseNumber) p.set("case_number", filters.caseNumber);
      if (filters.caseId) p.set("case_id", filters.caseId);
      if (filters.legalTopic) p.set("legal_topic", filters.legalTopic);
      if (filters.q) p.set("q", filters.q);
      if (filters.limit) p.set("limit", String(filters.limit));
      const qs = p.toString();
      return apiRequest<MissingPrecedentListResponse>(
        `/api/missing-precedents${qs ? `?${qs}` : ""}`,
        { signal },
      );
    },
    staleTime: 15_000,
  });
}

/** Counter for the sidebar / nav badge — open rows only. */
export function useMissingPrecedentsOpenCount() {
  return useQuery({
    queryKey: [...missingPrecedentKeys.all, "open-count"] as const,
    queryFn: ({ signal }) =>
      apiRequest<MissingPrecedentListResponse>(
        "/api/missing-precedents?status=open&limit=1",
        { signal },
      ),
    staleTime: 30_000,
    select: (data) => data.total_open,
  });
}

export function useMissingPrecedent(id: string | null) {
  return useQuery({
    queryKey: missingPrecedentKeys.detail(id ?? ""),
    queryFn: ({ signal }) =>
      apiRequest<MissingPrecedent>(
        `/api/missing-precedents/${encodeURIComponent(id!)}`,
        { signal },
      ),
    enabled: Boolean(id),
    staleTime: 15_000,
  });
}

export function useCreateMissingPrecedent() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (input: MissingPrecedentCreateInput) =>
      apiRequest<MissingPrecedent>("/api/missing-precedents", {
        method: "POST",
        body: input,
      }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.all });
      // APR-6 / ADM-3 (INV-IA2): a new open gap raises the chair-pending
      // aggregate and the /operations missing_precedents counter.
      qc.invalidateQueries({ queryKey: ["chair", "pending"] });
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
  });
}

export function useUpdateMissingPrecedent() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, patch }: { id: string; patch: MissingPrecedentPatch }) =>
      apiRequest<MissingPrecedent>(
        `/api/missing-precedents/${encodeURIComponent(id)}`,
        { method: "PATCH", body: patch },
      ),
    onSuccess: (_, { id }) => {
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.detail(id) });
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.all });
      // APR-6 / ADM-3 (INV-IA2): a status edit (e.g. open→irrelevant) shifts
      // the chair-pending aggregate and the /operations counter.
      qc.invalidateQueries({ queryKey: ["chair", "pending"] });
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
  });
}

export function useDeleteMissingPrecedent() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: string) =>
      apiRequest<{ deleted: boolean }>(
        `/api/missing-precedents/${encodeURIComponent(id)}`,
        { method: "DELETE" },
      ),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.all });
      // APR-6 / ADM-3 (INV-IA2): removing a gap lowers the chair-pending
      // aggregate and the /operations counter.
      qc.invalidateQueries({ queryKey: ["chair", "pending"] });
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
  });
}

export type MissingPrecedentUploadInput = {
  id: string;
  file: File;
  case_number?: string;
  chair_name?: string;
  district?: string;
  case_name?: string;
  court?: string;
  decision_date?: string;
  practice_area?: string;
  appeal_subtype?: string;
  subject_tags?: string[];
  is_binding?: boolean;
  headnote?: string;
  summary?: string;
  precedent_level?: string;
  source_type?: string;
};

export function useUploadMissingPrecedent() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (input: MissingPrecedentUploadInput) => {
      const fd = new FormData();
      fd.append("file", input.file);
      if (input.case_number) fd.append("case_number", input.case_number);
      if (input.chair_name) fd.append("chair_name", input.chair_name);
      if (input.district) fd.append("district", input.district);
      if (input.case_name) fd.append("case_name", input.case_name);
      if (input.court) fd.append("court", input.court);
      if (input.decision_date) fd.append("decision_date", input.decision_date);
      if (input.practice_area) fd.append("practice_area", input.practice_area);
      if (input.appeal_subtype) fd.append("appeal_subtype", input.appeal_subtype);
      if (input.subject_tags && input.subject_tags.length) {
        fd.append("subject_tags", JSON.stringify(input.subject_tags));
      }
      fd.append("is_binding", String(input.is_binding ?? true));
      if (input.headnote) fd.append("headnote", input.headnote);
      if (input.summary) fd.append("summary", input.summary);
      if (input.precedent_level)
        fd.append("precedent_level", input.precedent_level);
      if (input.source_type) fd.append("source_type", input.source_type);

      const res = await fetch(
        `/api/missing-precedents/${encodeURIComponent(input.id)}/upload`,
        { method: "POST", body: fd },
      );
      const parsed = await res.json().catch(() => null);
      if (!res.ok) {
        throw new ApiError(
          `Upload failed with ${res.status}`,
          res.status,
          parsed,
        );
      }
      return parsed as {
        missing_precedent: MissingPrecedent;
        case_law_id: string;
        route: "internal_committee" | "external_upload";
      };
    },
    onSuccess: (_, { id }) => {
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.detail(id) });
      qc.invalidateQueries({ queryKey: missingPrecedentKeys.all });
      qc.invalidateQueries({ queryKey: ["precedent-library"] });
      // APR-6 / ADM-3 (INV-IA2): uploading closes the gap → chair-pending
      // aggregate and the /operations missing_precedents counter both drop.
      qc.invalidateQueries({ queryKey: ["chair", "pending"] });
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
  });
}

/** Hebrew labels for display. */
export const CITED_BY_PARTY_LABELS: Record<CitedByParty, string> = {
  appellant: "עורר",
  respondent: "משיב",
  committee: "ועדה",
  permit_applicant: "מבקש היתר",
  unknown: "לא ידוע",
};

export const STATUS_LABELS: Record<MissingPrecedentStatus, string> = {
  open: "פתוח",
  uploaded: "הועלה",
  closed: "נסגר",
  irrelevant: "לא רלוונטי",
};
