"use client";

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

import {
  getIssueMatrixIssueById,
  getIssueMatrixIssues,
  getIssueMatrixSummary,
  updateIssueMatrixPlenaryEscalation,
  updateIssueMatrixRow,
} from "@/features/pswg/issue-matrix/service/issue-matrix-service";
import type {
  IssueMatrixRow,
  IssueMatrixSummary,
} from "@/features/pswg/issue-matrix/components/issue-matrix-data";

const EMPTY_ROWS: IssueMatrixRow[] = [];
const EMPTY_SUMMARY: IssueMatrixSummary = {
  totalIssues: 0,
  solved: 0,
  inProgress: 0,
  notAddressed: 0,
  totalPrimaryAgencies: 0,
};

const issueMatrixQueryKeys = {
  all: ["issue-matrix"] as const,
  list: () => [...issueMatrixQueryKeys.all, "list"] as const,
  summary: () => [...issueMatrixQueryKeys.all, "summary"] as const,
  detail: (issueId: number) =>
    [...issueMatrixQueryKeys.all, "detail", issueId] as const,
};

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

export function useIssueMatrixList() {
  const query = useQuery({
    queryKey: issueMatrixQueryKeys.list(),
    queryFn: getIssueMatrixIssues,
  });

  return {
    rows: query.data ?? EMPTY_ROWS,
    isLoading: query.isLoading,
    error: query.error
      ? getErrorMessage(query.error, "Unable to load issue matrix.")
      : null,
  };
}

export function useIssueMatrixSummary() {
  const query = useQuery({
    queryKey: issueMatrixQueryKeys.summary(),
    queryFn: getIssueMatrixSummary,
  });

  return {
    summary: query.data ?? EMPTY_SUMMARY,
    isLoading: query.isLoading,
    error: query.error
      ? getErrorMessage(query.error, "Unable to load issue matrix summary.")
      : null,
  };
}

export function useIssueMatrixDetail(issueId: number, enabled = true) {
  const query = useQuery({
    queryKey: issueMatrixQueryKeys.detail(issueId),
    queryFn: () => getIssueMatrixIssueById(issueId),
    enabled: enabled && Number.isFinite(issueId),
  });

  return {
    issue: query.data ?? null,
    isLoading: query.isLoading,
    error: query.error
      ? getErrorMessage(query.error, "Unable to load issue detail.")
      : null,
  };
}

type IssueMatrixRowUpdateInput = {
  issueId: number;
  governmentAgencyIds?: number[];
  issueStatusId?: number;
  primaryAgency?: {
    id: number;
    name: string;
    logo?: string | null;
  };
  status?: IssueMatrixRow["status"];
  statusCode?: string;
};

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

  const mutation = useMutation({
    mutationFn: ({
      issueId,
      governmentAgencyIds,
      issueStatusId,
    }: IssueMatrixRowUpdateInput) =>
      updateIssueMatrixRow(issueId, { governmentAgencyIds, issueStatusId }),
    onMutate: async (input: IssueMatrixRowUpdateInput) => {
      await queryClient.cancelQueries({
        queryKey: issueMatrixQueryKeys.list(),
      });

      const previousRows = queryClient.getQueryData<IssueMatrixRow[]>(
        issueMatrixQueryKeys.list(),
      );

      queryClient.setQueryData<IssueMatrixRow[]>(
        issueMatrixQueryKeys.list(),
        (rows) =>
          rows?.map((row) => {
            if (row.id !== input.issueId) return row;

            return {
              ...row,
              ...(input.primaryAgency && {
                primaryAgencyId: input.primaryAgency.id,
                primaryAgency: input.primaryAgency.name,
                primaryAgencyImage: input.primaryAgency.logo ?? null,
              }),
              ...(input.status && { status: input.status }),
              ...(input.statusCode && { statusCode: input.statusCode }),
              ...(input.issueStatusId !== undefined && {
                statusId: input.issueStatusId,
              }),
            };
          }),
      );

      return { previousRows };
    },
    onError: (_error, _input, context) => {
      if (context?.previousRows) {
        queryClient.setQueryData(
          issueMatrixQueryKeys.list(),
          context.previousRows,
        );
      }
    },
    onSettled: () => {
      void queryClient.invalidateQueries({
        queryKey: issueMatrixQueryKeys.list(),
      });
      void queryClient.invalidateQueries({
        queryKey: issueMatrixQueryKeys.summary(),
      });
    },
  });

  return {
    updateRow: mutation.mutate,
    isUpdating: mutation.isPending,
    error: mutation.error
      ? getErrorMessage(
          mutation.error,
          "Unable to update the issue matrix row.",
        )
      : null,
    resetError: mutation.reset,
  };
}

type IssueMatrixPlenaryEscalationInput = {
  issueId: number;
  escalated: boolean;
};

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

  const mutation = useMutation({
    mutationFn: ({ issueId, escalated }: IssueMatrixPlenaryEscalationInput) =>
      updateIssueMatrixPlenaryEscalation(issueId, escalated),
    onMutate: async ({
      issueId,
      escalated,
    }: IssueMatrixPlenaryEscalationInput) => {
      await queryClient.cancelQueries({
        queryKey: issueMatrixQueryKeys.list(),
      });

      const previousRows = queryClient.getQueryData<IssueMatrixRow[]>(
        issueMatrixQueryKeys.list(),
      );

      queryClient.setQueryData<IssueMatrixRow[]>(
        issueMatrixQueryKeys.list(),
        (rows) =>
          rows?.map((row) =>
            row.id === issueId ? { ...row, plenaryEscalation: escalated } : row,
          ),
      );

      return { previousRows };
    },
    onError: (_error, _input, context) => {
      if (context?.previousRows) {
        queryClient.setQueryData(
          issueMatrixQueryKeys.list(),
          context.previousRows,
        );
      }
    },
    onSettled: () => {
      void queryClient.invalidateQueries({
        queryKey: issueMatrixQueryKeys.list(),
      });
    },
  });

  return {
    setPlenaryEscalation: mutation.mutate,
    isUpdating: mutation.isPending,
    error: mutation.error
      ? getErrorMessage(
          mutation.error,
          "Unable to update the plenary escalation.",
        )
      : null,
    resetError: mutation.reset,
  };
}
