"use client";

import { useCallback, useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

import {
  createWorkingGroupIssue,
  deleteWorkingGroupIssue,
  getMyWorkingGroupIssuePrimaryGovernment,
  getWorkingGroupIssueById,
  getWorkingGroupIssues,
  getWorkingGroupCategories,
  getWorkingGroupIssueGovernmentAgencies,
  getWorkingGroupIssueStatuses,
  getWorkingGroupIssueSummary,
  updateWorkingGroupIssue,
  type CreateWorkingGroupIssuePayload,
  type UpdateWorkingGroupIssuePayload,
  type WorkingGroupIssueAgency,
  type WorkingGroupIssueLookup,
} from "@/features/pswg/working-group-issues/service/working-group-issues-service";
import type { IssueRow } from "@/features/pswg/working-group-issues/wg-issues-data";

// Centralized query keys so mutations can invalidate the exact caches they
// affect (e.g. update mutation refreshes both the list and the edited detail).
const wgIssueQueryKeys = {
  all: ["working-group-issues"] as const,
  list: () => [...wgIssueQueryKeys.all, "list"] as const,
  detail: (issueId: number) =>
    [...wgIssueQueryKeys.all, "detail", issueId] as const,
  primaryGovernment: () =>
    [...wgIssueQueryKeys.all, "primary-government"] as const,
  summary: () => [...wgIssueQueryKeys.all, "summary"] as const,
  statuses: () => [...wgIssueQueryKeys.all, "statuses"] as const,
  categories: () => [...wgIssueQueryKeys.all, "categories"] as const,
  agencies: () => [...wgIssueQueryKeys.all, "agencies"] as const,
};

// Lookups (statuses, categories, agencies) rarely change, so we cache them
// for 5 minutes to avoid refetching every time the screen mounts.
const LOOKUP_STALE_TIME_MS = 5 * 60 * 1000;

const EMPTY_ROWS: IssueRow[] = [];
const EMPTY_LOOKUP: WorkingGroupIssueLookup[] = [];
const EMPTY_AGENCIES: WorkingGroupIssueAgency[] = [];

function findLookupIdByName(
  list: WorkingGroupIssueLookup[],
  name: string,
): number | null {
  const item = list.find(
    (entry) => entry.name.toLowerCase() === name.toLowerCase(),
  );

  return item?.id ?? null;
}

// Codes are stable identifiers ("DRAFT", "NEW_SUBMISSION") that don't drift
// if an admin renames the status, so prefer this when detecting defaults.
function findLookupIdByCode(
  list: WorkingGroupIssueLookup[],
  code: string,
): number | null {
  const item = list.find(
    (entry) => entry.code?.toLowerCase() === code.toLowerCase(),
  );

  return item?.id ?? null;
}

function getErrorMessage(error: unknown, fallback: string) {
  return error instanceof Error ? error.message : fallback;
}

// --- Lookup queries ----------------------------------------------------
// Both the filter bar and the create/edit dialog read these lists. Sharing
// query keys means the dialog hits the cache instead of refetching.

function useWgIssueStatuses() {
  return useQuery({
    queryKey: wgIssueQueryKeys.statuses(),
    queryFn: getWorkingGroupIssueStatuses,
    staleTime: LOOKUP_STALE_TIME_MS,
  });
}

export function useWgCategories() {
  return useQuery({
    queryKey: wgIssueQueryKeys.categories(),
    queryFn: getWorkingGroupCategories,
    staleTime: LOOKUP_STALE_TIME_MS,
  });
}

function useWgIssueAgencies() {
  return useQuery({
    queryKey: wgIssueQueryKeys.agencies(),
    queryFn: getWorkingGroupIssueGovernmentAgencies,
    staleTime: LOOKUP_STALE_TIME_MS,
  });
}

export function useWgIssuePrimaryGovernment(enabled = true) {
  return useQuery({
    queryKey: wgIssueQueryKeys.primaryGovernment(),
    queryFn: getMyWorkingGroupIssuePrimaryGovernment,
    enabled,
    staleTime: LOOKUP_STALE_TIME_MS,
  });
}

// Combined lookups used by the filter bar.
export function useWgIssuesFilterOptions() {
  const statusesQuery = useWgIssueStatuses();
  const categoriesQuery = useWgCategories();
  const agenciesQuery = useWgIssueAgencies();

  return {
    agencies: agenciesQuery.data ?? EMPTY_AGENCIES,
    categories: categoriesQuery.data ?? EMPTY_LOOKUP,
    statuses: statusesQuery.data ?? EMPTY_LOOKUP,
    isLoading:
      statusesQuery.isLoading ||
      categoriesQuery.isLoading ||
      agenciesQuery.isLoading,
    error:
      (statusesQuery.error as Error | null)?.message ??
      (categoriesQuery.error as Error | null)?.message ??
      (agenciesQuery.error as Error | null)?.message ??
      null,
  };
}

// --- Summary stats -----------------------------------------------------

export function useWgIssueSummary() {
  return useQuery({
    queryKey: wgIssueQueryKeys.summary(),
    queryFn: getWorkingGroupIssueSummary,
  });
}

// --- Issue list --------------------------------------------------------

export function useWorkingGroupIssues() {
  const query = useQuery({
    queryKey: wgIssueQueryKeys.list(),
    queryFn: getWorkingGroupIssues,
  });

  return {
    rows: query.data ?? EMPTY_ROWS,
    isLoading: query.isLoading,
    error: query.error
      ? getErrorMessage(query.error, "Unable to load working group issues.")
      : null,
    refetch: query.refetch,
  };
}

// --- Issue detail ------------------------------------------------------

export function useWorkingGroupIssueDetail(
  issueId: number,
  fallbackErrorMessage: string,
  enabled = true,
) {
  const query = useQuery({
    queryKey: wgIssueQueryKeys.detail(issueId),
    queryFn: () => getWorkingGroupIssueById(issueId),
    enabled: enabled && Number.isFinite(issueId),
  });

  return {
    detail: query.data ?? null,
    isLoading: query.isLoading,
    error: query.error
      ? getErrorMessage(query.error, fallbackErrorMessage)
      : null,
  };
}

// --- Create + update form ---------------------------------------------

type WorkingGroupIssueFormFeedback = {
  error: string | null;
  successMessage: string;
};

export function useWorkingGroupIssueForm() {
  const queryClient = useQueryClient();
  const statusesQuery = useWgIssueStatuses();
  const agenciesQuery = useWgIssueAgencies();

  const [feedback, setFeedback] = useState<WorkingGroupIssueFormFeedback>({
    error: null,
    successMessage: "",
  });

  const invalidateList = useCallback(() => {
    void queryClient.invalidateQueries({ queryKey: wgIssueQueryKeys.list() });
    void queryClient.invalidateQueries({
      queryKey: wgIssueQueryKeys.summary(),
    });
  }, [queryClient]);

  const createMutation = useMutation({
    mutationFn: createWorkingGroupIssue,
    onSuccess: (message) => {
      setFeedback({ error: null, successMessage: message });
      invalidateList();
    },
    onError: (error) => {
      setFeedback({
        error: getErrorMessage(
          error,
          "Unable to create working group issue.",
        ),
        successMessage: "",
      });
    },
  });

  const updateMutation = useMutation({
    mutationFn: ({
      issueId,
      payload,
    }: {
      issueId: number;
      payload: UpdateWorkingGroupIssuePayload;
    }) => updateWorkingGroupIssue(issueId, payload),
    onSuccess: (message, variables) => {
      setFeedback({ error: null, successMessage: message });
      invalidateList();
      void queryClient.invalidateQueries({
        queryKey: wgIssueQueryKeys.detail(variables.issueId),
      });
    },
    onError: (error) => {
      setFeedback({
        error: getErrorMessage(
          error,
          "Unable to update working group issue.",
        ),
        successMessage: "",
      });
    },
  });

  // Wraps the create mutation in a boolean-returning API so the dialog can
  // do `if (saved) { ...close form... }` without learning about mutations.
  const createIssue = useCallback(
    async (payload: CreateWorkingGroupIssuePayload) => {
      setFeedback({ error: null, successMessage: "" });

      try {
        await createMutation.mutateAsync(payload);
        return true;
      } catch {
        return false;
      }
    },
    [createMutation],
  );

  const updateIssue = useCallback(
    async (issueId: number, payload: UpdateWorkingGroupIssuePayload) => {
      setFeedback({ error: null, successMessage: "" });

      try {
        await updateMutation.mutateAsync({ issueId, payload });
        return true;
      } catch {
        return false;
      }
    },
    [updateMutation],
  );

  const statuses = statusesQuery.data ?? EMPTY_LOOKUP;

  // Look up by stable code first, fall back to display name so the form
  // still works against older API responses that don't return `code`.
  const defaultDraftStatusId = useMemo(
    () =>
      findLookupIdByCode(statuses, "DRAFT") ??
      findLookupIdByName(statuses, "Draft"),
    [statuses],
  );

  // "Saved" status, used when the user clicks Save (vs. Draft) on a new issue.
  const defaultSavedStatusId = useMemo(
    () =>
      findLookupIdByCode(statuses, "SAVED") ??
      findLookupIdByName(statuses, "Saved"),
    [statuses],
  );

  const defaultSubmitStatusId = useMemo(
    () =>
      findLookupIdByCode(statuses, "NEW_SUBMISSION") ??
      findLookupIdByName(statuses, "New Submission"),
    [statuses],
  );

  const optionsError =
    (statusesQuery.error as Error | null)?.message ??
    (agenciesQuery.error as Error | null)?.message ??
    null;

  return {
    agencies: agenciesQuery.data ?? EMPTY_AGENCIES,
    statuses,
    createIssue,
    updateIssue,
    defaultDraftStatusId,
    defaultSavedStatusId,
    defaultSubmitStatusId,
    isLoadingOptions: statusesQuery.isLoading || agenciesQuery.isLoading,
    isSaving: createMutation.isPending || updateMutation.isPending,
    error: feedback.error ?? optionsError,
    successMessage: feedback.successMessage,
  };
}

// --- Delete mutation ---------------------------------------------------

export function useDeleteWorkingGroupIssue() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (issueId: number) => deleteWorkingGroupIssue(issueId),
    onSuccess: () => {
      void queryClient.invalidateQueries({ queryKey: wgIssueQueryKeys.list() });
      void queryClient.invalidateQueries({
        queryKey: wgIssueQueryKeys.summary(),
      });
    },
  });
}
