"use client";

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

import type {
  CefpIssueMatrixRow,
  CefpIssueMatrixStatus,
  CefpIssueMatrixSummary,
} from "../components/cefp-issue-matrix-data";

import type { CefpAddIssueOption } from "../components/cefp-add-issue-types";

import {
  createCefpIssue,
  deleteCefpIssue,
  getCefpGovernmentAgencies,
  getCefpIssueCategories,
  getCefpIssueMatrixIssues,
  getCefpIssueMatrixSummary,
  getCefpWorkingGroups,
  updateCefpIssue,
  updateCefpIssueEscalation,
  updateCefpIssueStatus,
  type CreateCefpIssuePayload,
  type UpdateCefpIssuePayload,
} from "../service/cefp-issue-matrix-service";

const EMPTY_ROWS: CefpIssueMatrixRow[] = [];
const EMPTY_OPTIONS: CefpAddIssueOption[] = [];

const EMPTY_SUMMARY: CefpIssueMatrixSummary = {
  totalIssues: 0,
  solved: 0,
  inProgress: 0,
  notAddressed: 0,
  totalPrimaryAgencies: 0,
};

export const cefpIssueMatrixQueryKeys = {
  all: ["cefp-issue-matrix"] as const,

  list: () =>
    [
      ...cefpIssueMatrixQueryKeys.all,
      "list",
    ] as const,

  summary: () =>
    [
      ...cefpIssueMatrixQueryKeys.all,
      "summary",
    ] as const,

  workingGroups: () =>
    [
      ...cefpIssueMatrixQueryKeys.all,
      "working-groups",
    ] as const,

  governmentAgencies: () =>
    [
      ...cefpIssueMatrixQueryKeys.all,
      "government-agencies",
    ] as const,

  issueCategories: () =>
    [
      ...cefpIssueMatrixQueryKeys.all,
      "issue-categories",
    ] as const,
};

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

export function useCefpIssueMatrixList() {
  const query = useQuery({
    queryKey:
      cefpIssueMatrixQueryKeys.list(),

    queryFn:
      getCefpIssueMatrixIssues,
  });

  return {
    rows:
      query.data ?? EMPTY_ROWS,

    isLoading:
      query.isLoading,

    isFetching:
      query.isFetching,

    error: query.error
      ? getErrorMessage(
          query.error,
          "Unable to load CEFP issue matrix.",
        )
      : null,

    refetch:
      query.refetch,
  };
}

export function useCefpIssueMatrixSummary() {
  const query = useQuery({
    queryKey:
      cefpIssueMatrixQueryKeys.summary(),

    queryFn:
      getCefpIssueMatrixSummary,
  });

  return {
    summary:
      query.data ?? EMPTY_SUMMARY,

    isLoading:
      query.isLoading,

    isFetching:
      query.isFetching,

    error: query.error
      ? getErrorMessage(
          query.error,
          "Unable to load CEFP issue matrix summary.",
        )
      : null,

    refetch:
      query.refetch,
  };
}

export function useCefpWorkingGroups() {
  const query = useQuery({
    queryKey:
      cefpIssueMatrixQueryKeys.workingGroups(),

    queryFn:
      getCefpWorkingGroups,

    staleTime:
      5 * 60 * 1000,

    retry: 1,
  });

  const workingGroups: CefpAddIssueOption[] =
    Array.isArray(query.data)
      ? query.data.map((item) => ({
          id: item.id,
          name: item.name,
          logo: item.logo,
        }))
      : EMPTY_OPTIONS;

  return {
    workingGroups,

    isLoadingWorkingGroups:
      query.isLoading,

    isFetchingWorkingGroups:
      query.isFetching,

    workingGroupsError:
      query.error
        ? getErrorMessage(
            query.error,
            "Unable to load working groups.",
          )
        : null,

    refetchWorkingGroups:
      query.refetch,
  };
}

export function useCefpGovernmentAgencies() {
  const query = useQuery({
    queryKey:
      cefpIssueMatrixQueryKeys.governmentAgencies(),

    queryFn:
      getCefpGovernmentAgencies,

    staleTime:
      5 * 60 * 1000,

    retry: 1,
  });

  const governmentAgencies: CefpAddIssueOption[] =
    Array.isArray(query.data)
      ? query.data.map((item) => ({
          id: item.id,
          name: item.name,
          logo: item.logo,
        }))
      : EMPTY_OPTIONS;

  return {
    governmentAgencies,

    isLoadingGovernmentAgencies:
      query.isLoading,

    isFetchingGovernmentAgencies:
      query.isFetching,

    governmentAgenciesError:
      query.error
        ? getErrorMessage(
            query.error,
            "Unable to load government agencies.",
          )
        : null,

    refetchGovernmentAgencies:
      query.refetch,
  };
}

export function useCefpIssueCategories() {
  const query = useQuery({
    queryKey:
      cefpIssueMatrixQueryKeys.issueCategories(),

    queryFn:
      getCefpIssueCategories,

    staleTime:
      5 * 60 * 1000,

    retry: 1,
  });

  const categories: CefpAddIssueOption[] =
    Array.isArray(query.data)
      ? query.data.map((item) => ({
          id: item.id,
          name: item.name,
          logo: null,
        }))
      : EMPTY_OPTIONS;

  return {
    categories,

    isLoadingCategories:
      query.isLoading,

    isFetchingCategories:
      query.isFetching,

    categoriesError:
      query.error
        ? getErrorMessage(
            query.error,
            "Unable to load issue categories.",
          )
        : null,

    refetchCategories:
      query.refetch,
  };
}

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

  const mutation = useMutation({
    mutationFn: (
      payload: CreateCefpIssuePayload,
    ) =>
      createCefpIssue(payload),

    onSuccess: async () => {
      await Promise.all([
        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.list(),
        }),

        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.summary(),
        }),
      ]);
    },
  });

  return {
    createIssue:
      mutation.mutateAsync,

    isCreating:
      mutation.isPending,

    createError:
      mutation.error
        ? getErrorMessage(
            mutation.error,
            "Unable to create CEFP issue.",
          )
        : null,

    resetCreateError:
      mutation.reset,
  };
}

type UpdateEscalationVariables = {
  issueId: number;
  escalation: boolean;
};

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

  const mutation = useMutation({
    mutationFn: ({
      issueId,
      escalation,
    }: UpdateEscalationVariables) =>
      updateCefpIssueEscalation(
        issueId,
        escalation,
      ),

    onMutate: async ({
      issueId,
      escalation,
    }) => {
      const queryKey =
        cefpIssueMatrixQueryKeys.list();

      await queryClient.cancelQueries({
        queryKey,
      });

      const previousRows =
        queryClient.getQueryData<
          CefpIssueMatrixRow[]
        >(queryKey);

      queryClient.setQueryData<
        CefpIssueMatrixRow[]
      >(
        queryKey,
        (currentRows = []) =>
          currentRows.map((row) =>
            row.issueId === issueId
              ? {
                  ...row,
                  plenaryEscalation:
                    escalation,
                }
              : row,
          ),
      );

      return {
        previousRows,
      };
    },

    onError: (
      _error,
      _variables,
      context,
    ) => {
      if (!context?.previousRows) {
        return;
      }

      queryClient.setQueryData<
        CefpIssueMatrixRow[]
      >(
        cefpIssueMatrixQueryKeys.list(),
        context.previousRows,
      );
    },

    onSettled: async () => {
      await Promise.all([
        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.list(),
        }),

        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.summary(),
        }),
      ]);
    },
  });

  return {
    updateEscalation:
      mutation.mutateAsync,

    isUpdatingEscalation:
      mutation.isPending,

    updatingEscalationIssueId:
      mutation.variables?.issueId ??
      null,

    updateEscalationError:
      mutation.error
        ? getErrorMessage(
            mutation.error,
            "Unable to update plenary escalation.",
          )
        : null,

    resetUpdateEscalation:
      mutation.reset,
  };
}

type UpdateStatusVariables = {
  issueId: number;
  issueStatusId: number;
  statusName: CefpIssueMatrixStatus;
};

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

  const mutation = useMutation({
    mutationFn: ({
      issueId,
      issueStatusId,
    }: UpdateStatusVariables) =>
      updateCefpIssueStatus(
        issueId,
        issueStatusId,
      ),

    onMutate: async ({
      issueId,
      issueStatusId,
      statusName,
    }) => {
      const queryKey =
        cefpIssueMatrixQueryKeys.list();

      await queryClient.cancelQueries({
        queryKey,
      });

      const previousRows =
        queryClient.getQueryData<
          CefpIssueMatrixRow[]
        >(queryKey);

      queryClient.setQueryData<
        CefpIssueMatrixRow[]
      >(
        queryKey,
        (currentRows = []) =>
          currentRows.map((row) =>
            row.issueId === issueId
              ? {
                  ...row,
                  status: statusName,
                  statusId:
                    issueStatusId,
                }
              : row,
          ),
      );

      return {
        previousRows,
      };
    },

    onError: (
      _error,
      _variables,
      context,
    ) => {
      if (!context?.previousRows) {
        return;
      }

      queryClient.setQueryData<
        CefpIssueMatrixRow[]
      >(
        cefpIssueMatrixQueryKeys.list(),
        context.previousRows,
      );
    },

    onSettled: async () => {
      await Promise.all([
        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.list(),
        }),

        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.summary(),
        }),
      ]);
    },
  });

  return {
    updateStatus:
      mutation.mutateAsync,

    isUpdatingStatus:
      mutation.isPending,

    updatingStatusIssueId:
      mutation.variables?.issueId ??
      null,

    updateStatusError:
      mutation.error
        ? getErrorMessage(
            mutation.error,
            "Unable to update issue status.",
          )
        : null,

    resetUpdateStatus:
      mutation.reset,
  };
}

type UpdateIssueVariables = {
  issueId: number;
  payload: UpdateCefpIssuePayload;
};

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

  const mutation = useMutation({
    mutationFn: ({
      issueId,
      payload,
    }: UpdateIssueVariables) =>
      updateCefpIssue(
        issueId,
        payload,
      ),

    onSuccess: async () => {
      await Promise.all([
        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.list(),
        }),

        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.summary(),
        }),
      ]);
    },
  });

  return {
    updateIssue:
      mutation.mutateAsync,

    isUpdatingIssue:
      mutation.isPending,

    updateIssueError:
      mutation.error
        ? getErrorMessage(
            mutation.error,
            "Unable to update CEFP issue.",
          )
        : null,

    resetUpdateIssue:
      mutation.reset,
  };
}

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

  const mutation = useMutation({
    mutationFn: (
      issueId: number,
    ) =>
      deleteCefpIssue(
        issueId,
      ),

    onSuccess: async () => {
      await Promise.all([
        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.list(),
        }),

        queryClient.invalidateQueries({
          queryKey:
            cefpIssueMatrixQueryKeys.summary(),
        }),
      ]);
    },
  });

  return {
    deleteIssue:
      mutation.mutateAsync,

    isDeletingIssue:
      mutation.isPending,

    deletingIssueId:
      mutation.variables ??
      null,

    deleteIssueError:
      mutation.error
        ? getErrorMessage(
            mutation.error,
            "Unable to remove CEFP issue.",
          )
        : null,

    resetDeleteIssue:
      mutation.reset,
  };
}