"use client";

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

import type {
  CdcIssueMatrixRow,
  CdcIssueMatrixStatus,
  CdcIssueMatrixSummary,
} from "../components/cdc-issue-matrix-data";
import {
  getCdcIssueMatrixIssues,
  getCdcIssueMatrixSummary,
  getCdcIssueMatrixDetail,
  deleteCdcIssue,
  updateCdcIssueMatrixRow,
} from "../service/cdc-issue-matrix-service";

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

const cdcIssueMatrixQueryKeys = {
  all: ["cdc-issue-matrix"] as const,
  list: () => [...cdcIssueMatrixQueryKeys.all, "list"] as const,
  summary: () => [...cdcIssueMatrixQueryKeys.all, "summary"] as const,
};

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

export function useCdcIssueMatrixList() {
  const query = useQuery({
    queryKey: cdcIssueMatrixQueryKeys.list(),
    queryFn: getCdcIssueMatrixIssues,
  });

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

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

/**
 * Flag an issue for the plenary, or take the flag off. The table checkbox
 * updates straight away and rolls back if the request fails.
 */
export function useCdcIssueMatrixPlenaryEscalation() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: ({ issueId, escalated }: PlenaryEscalationInput) =>
      updateCdcIssueMatrixRow(issueId, { escalation: escalated }),

    onMutate: async ({ issueId, escalated }: PlenaryEscalationInput) => {
      await queryClient.cancelQueries({
        queryKey: cdcIssueMatrixQueryKeys.list(),
      });

      const previousRows = queryClient.getQueryData<CdcIssueMatrixRow[]>(
        cdcIssueMatrixQueryKeys.list(),
      );

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

      return { previousRows };
    },

    onError: (_error, _input, context) => {
      if (context?.previousRows) {
        queryClient.setQueryData(
          cdcIssueMatrixQueryKeys.list(),
          context.previousRows,
        );
      }
    },

    onSettled: () => {
      void queryClient.invalidateQueries({
        queryKey: cdcIssueMatrixQueryKeys.list(),
      });
    },
  });

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

type RowUpdateInput = {
  issueId: number;
  // What gets sent to the API.
  governmentAgencyIds?: number[];
  issueStatusId?: number;
  // What the row should look like straight away, before the API answers.
  agencies?: CdcIssueMatrixRow["agencies"];
  status?: CdcIssueMatrixStatus;
};

/**
 * Change the ministries or the status of a row from the table. The row updates
 * straight away and rolls back if the request fails.
 */
export function useCdcIssueMatrixRowUpdate() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: ({ issueId, governmentAgencyIds, issueStatusId }: RowUpdateInput) =>
      updateCdcIssueMatrixRow(issueId, { governmentAgencyIds, issueStatusId }),

    onMutate: async (input: RowUpdateInput) => {
      await queryClient.cancelQueries({
        queryKey: cdcIssueMatrixQueryKeys.list(),
      });

      const previousRows = queryClient.getQueryData<CdcIssueMatrixRow[]>(
        cdcIssueMatrixQueryKeys.list(),
      );

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

            const agencies = input.agencies ?? row.agencies;
            const primaryAgency = agencies[0];

            return {
              ...row,
              agencies,
              primaryAgency: primaryAgency?.name ?? "-",
              primaryAgencyImage: primaryAgency?.logo ?? null,
              status: input.status ?? row.status,
              statusId: input.issueStatusId ?? row.statusId,
            };
          }),
      );

      return { previousRows };
    },

    onError: (_error, _input, context) => {
      if (context?.previousRows) {
        queryClient.setQueryData(
          cdcIssueMatrixQueryKeys.list(),
          context.previousRows,
        );
      }
    },

    onSettled: () => {
      void queryClient.invalidateQueries({
        queryKey: cdcIssueMatrixQueryKeys.list(),
      });
      // A status change moves the status cards and a ministry change moves the
      // primary-agency count, so the summary has to be refetched too.
      void queryClient.invalidateQueries({
        queryKey: cdcIssueMatrixQueryKeys.summary(),
      });
    },
  });

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

export function useCdcIssueMatrixSummary() {
  const query = useQuery({
    queryKey: cdcIssueMatrixQueryKeys.summary(),
    queryFn: getCdcIssueMatrixSummary,
  });

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

export function useCdcIssueMatrixDetail(issueId: number) {
  const query = useQuery({
    queryKey: ["cdc-issue-matrix", "issue-detail", issueId],
    queryFn: () => getCdcIssueMatrixDetail(issueId),
    enabled: Number.isFinite(issueId) && issueId > 0,
  });

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

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

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