"use client";

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

import {
  addUserToStakeholder,
  createStakeholder,
  deleteStakeholder,
  getStakeholder,
  getStakeholderDashboardSummary,
  getStakeholderTypes,
  getStakeholderUserOptions,
  getStakeholderUsers,
  getStakeholders,
  updateStakeholder,
} from "@/features/stakeholder/service/stakeholder-service";
import type { StakeholderListQuery } from "@/features/stakeholder/stakeholder-list-query";
import type {
  Stakeholder,
  StakeholderDashboardSummary,
  StakeholderDetail,
  StakeholderFormPayload,
  StakeholderListMeta,
  StakeholderType,
  StakeholderUser,
} from "@/features/stakeholder/stakeholder-data";
import { STAKEHOLDER_LIST_LIMIT } from "@/features/stakeholder/components/table/stakeholder-table-config";

// Centralized query keys so mutations can invalidate the exact caches
// they affect.
const stakeholderQueryKeys = {
  all: ["stakeholders"] as const,
  list: () => [...stakeholderQueryKeys.all, "list"] as const,
  options: () => [...stakeholderQueryKeys.all, "options"] as const,
  summary: () => [...stakeholderQueryKeys.all, "summary"] as const,
  types: () => [...stakeholderQueryKeys.all, "types"] as const,
  detail: (stakeholderId: number) =>
      [...stakeholderQueryKeys.all, "detail", stakeholderId] as const,
  members: (stakeholderId: number) =>
      [...stakeholderQueryKeys.all, "members", stakeholderId] as const,
  userOptions: () => [...stakeholderQueryKeys.all, "user-options"] as const,
};

// Types lookup is small and rarely changes. Keep it warm for 5 minutes
// so the dropdown does not flicker between dialog opens.
const TYPES_STALE_TIME_MS = 5 * 60 * 1000;

const EMPTY_STAKEHOLDERS: Stakeholder[] = [];
const EMPTY_TYPES: StakeholderType[] = [];
const EMPTY_USERS: StakeholderUser[] = [];
const EMPTY_MEMBER_IDS: number[] = [];
const EMPTY_SUMMARY: StakeholderDashboardSummary = {
  totalStakeholders: 0,
  activeStakeholders: 0,
  inactiveStakeholders: 0,
  totalStakeholderTypes: 0,
  totalAssignedUsers: 0,
  stakeholderTypes: [],
};

const EMPTY_LIST_META: StakeholderListMeta = {
  page: 1,
  limit: 10,
  total: 0,
  totalPages: 0,
};

type State = {
  stakeholders: Stakeholder[];
  stakeholderOptions: Stakeholder[];
  listMeta: StakeholderListMeta;
  dashboardSummary: StakeholderDashboardSummary;
  stakeholderTypes: StakeholderType[];
  stakeholderDetail: StakeholderDetail | null;
  users: StakeholderUser[];
  memberUserIds: number[];

  isLoading: boolean;
  isLoadingStakeholders: boolean;
  isLoadingSummary: boolean;
  isLoadingTypes: boolean;
  isLoadingDetail: boolean;
  isLoadingUsers: boolean;

  isSaving: boolean;
  isDeleting: boolean;
  isAssigningUser: boolean;

  error: string | null;
  successMessage: string;
};

type UseStakeholdersResult = State & {
  loadStakeholders: () => Promise<void>;
  loadStakeholderTypes: () => Promise<void>;
  clearMessages: () => void;

  addStakeholder: (payload: StakeholderFormPayload) => Promise<boolean>;

  editStakeholder: (
      id: number,
      payload: StakeholderFormPayload,
  ) => Promise<boolean>;

  removeStakeholder: (id: number) => Promise<boolean>;

  loadStakeholderDetail: (stakeholderId: number) => Promise<void>;
  clearStakeholderDetail: () => void;

  loadUsersForStakeholder: (stakeholderId: number) => Promise<void>;

  assignUserToStakeholder: (
      stakeholderId: number,
      userId: number,
  ) => Promise<boolean>;
};

function getNumberId(value: number | string): number | null {
  const id = Number(value);

  return Number.isFinite(id) ? id : null;
}

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

export function useStakeholders(listQuery: StakeholderListQuery): UseStakeholdersResult {
  const queryClient = useQueryClient();

  // The screen drives the detail/members panels imperatively.
  // It calls loadStakeholderDetail(id) or loadUsersForStakeholder(id).
  // We store those selected IDs here and use them to enable the related queries.
  const [activeDetailId, setActiveDetailId] = useState<number | null>(null);
  const [activeMembersId, setActiveMembersId] = useState<number | null>(null);

  // Feedback messages produced by mutations.
  const [error, setError] = useState<string | null>(null);
  const [successMessage, setSuccessMessage] = useState("");

  const stakeholdersQuery = useQuery({
    queryKey: [...stakeholderQueryKeys.list(), listQuery],
    queryFn: () => getStakeholders(listQuery),
  });

  const stakeholderOptionsQuery = useQuery({
    queryKey: stakeholderQueryKeys.options(),
    queryFn: () =>
      getStakeholders({ page: 1, limit: STAKEHOLDER_LIST_LIMIT }),
    staleTime: TYPES_STALE_TIME_MS,
  });

  const summaryQuery = useQuery({
    queryKey: stakeholderQueryKeys.summary(),
    queryFn: getStakeholderDashboardSummary,
  });

  const typesQuery = useQuery({
    queryKey: stakeholderQueryKeys.types(),
    queryFn: getStakeholderTypes,
    staleTime: TYPES_STALE_TIME_MS,
  });

  const detailQuery = useQuery({
    queryKey:
        activeDetailId === null
            ? stakeholderQueryKeys.all
            : stakeholderQueryKeys.detail(activeDetailId),
    queryFn: () => getStakeholder(activeDetailId as number),
    enabled: activeDetailId !== null,
  });

  const userOptionsQuery = useQuery({
    queryKey: stakeholderQueryKeys.userOptions(),
    queryFn: getStakeholderUserOptions,
    enabled: activeMembersId !== null,
  });

  const membersQuery = useQuery({
    queryKey:
        activeMembersId === null
            ? stakeholderQueryKeys.all
            : stakeholderQueryKeys.members(activeMembersId),
    queryFn: () => getStakeholderUsers(activeMembersId as number),
    enabled: activeMembersId !== null,
  });

  // Memberships are returned as full user objects.
  // The screen only needs IDs to mark users as already members.
  const memberUserIds =
      membersQuery.data
          ?.map((member) => getNumberId(member.id))
          .filter((id): id is number => id !== null) ?? EMPTY_MEMBER_IDS;

  const surfaceError = useCallback((message: string) => {
    setError(message);
    setSuccessMessage("");
  }, []);

  const invalidateLists = useCallback(() => {
    void queryClient.invalidateQueries({
      queryKey: [...stakeholderQueryKeys.all, "list"],
    });
    void queryClient.invalidateQueries({
      queryKey: stakeholderQueryKeys.options(),
    });
    void queryClient.invalidateQueries({
      queryKey: stakeholderQueryKeys.summary(),
    });
  }, [queryClient]);

  const invalidateDetail = useCallback(
      (stakeholderId: number) => {
        void queryClient.invalidateQueries({
          queryKey: stakeholderQueryKeys.detail(stakeholderId),
        });
      },
      [queryClient],
  );

  const invalidateMembers = useCallback(
      (stakeholderId: number) => {
        void queryClient.invalidateQueries({
          queryKey: stakeholderQueryKeys.members(stakeholderId),
        });
      },
      [queryClient],
  );

  // ---------------------------------------------------------------------------
  // Mutations
  // ---------------------------------------------------------------------------

  const createMutation = useMutation({
    mutationFn: createStakeholder,
    onSuccess: (result) => {
      setError(null);
      setSuccessMessage(result.message || "Stakeholder created successfully.");
      invalidateLists();
    },
    onError: (mutationError) => {
      surfaceError(
          getErrorMessage(mutationError, "Unable to create stakeholder."),
      );
    },
  });

  const updateMutation = useMutation({
    mutationFn: ({
                   id,
                   payload,
                 }: {
      id: number;
      payload: StakeholderFormPayload;
    }) => updateStakeholder(id, payload),
    onSuccess: (result, variables) => {
      setError(null);
      setSuccessMessage(result.message || "Stakeholder updated successfully.");
      invalidateLists();
      invalidateDetail(variables.id);

      // Related stakeholder updates can affect another row too.
      // Example: stakeholder 7 related to stakeholder 8.
      // Backend updates both sides, so refresh the list.
      if (variables.payload.relatedStakeholderId) {
        invalidateDetail(variables.payload.relatedStakeholderId);
      }
    },
    onError: (mutationError) => {
      surfaceError(
          getErrorMessage(mutationError, "Unable to update stakeholder."),
      );
    },
  });

  const deleteMutation = useMutation({
    mutationFn: deleteStakeholder,
    onSuccess: (message) => {
      setError(null);
      setSuccessMessage(message || "Stakeholder deleted successfully.");

      invalidateLists();
    },
    onError: (mutationError) => {
      surfaceError(
          getErrorMessage(mutationError, "Unable to delete stakeholder."),
      );
    },
  });

  const assignUserMutation = useMutation({
    mutationFn: ({
                   stakeholderId,
                   userId,
                 }: {
      stakeholderId: number;
      userId: number;
    }) => addUserToStakeholder(stakeholderId, { userId }),
    onSuccess: (message, variables) => {
      setError(null);
      setSuccessMessage(message || "User added to stakeholder.");

      invalidateMembers(variables.stakeholderId);
      invalidateDetail(variables.stakeholderId);
    },
    onError: (mutationError) => {
      surfaceError(
          getErrorMessage(
              mutationError,
              "Unable to add user to stakeholder.",
          ),
      );
    },
  });

  // ---------------------------------------------------------------------------
  // Public actions exposed to screen
  // ---------------------------------------------------------------------------

  const loadStakeholders = useCallback(async () => {
    await stakeholdersQuery.refetch();
  }, [stakeholdersQuery]);

  const loadStakeholderTypes = useCallback(async () => {
    await typesQuery.refetch();
  }, [typesQuery]);

  const clearMessages = useCallback(() => {
    setError(null);
    setSuccessMessage("");
  }, []);

  const loadStakeholderDetail = useCallback(async (stakeholderId: number) => {
    setActiveDetailId(stakeholderId);
  }, []);

  const clearStakeholderDetail = useCallback(() => {
    setActiveDetailId(null);
  }, []);

  const loadUsersForStakeholder = useCallback(async (stakeholderId: number) => {
    setActiveMembersId(stakeholderId);
  }, []);

  const addStakeholder = useCallback(
      async (payload: StakeholderFormPayload) => {
        try {
          await createMutation.mutateAsync(payload);
          return true;
        } catch {
          return false;
        }
      },
      [createMutation],
  );

  const editStakeholder = useCallback(
      async (id: number, payload: StakeholderFormPayload) => {
        try {
          await updateMutation.mutateAsync({
            id,
            payload,
          });

          return true;
        } catch {
          return false;
        }
      },
      [updateMutation],
  );

  const removeStakeholder = useCallback(
      async (id: number) => {
        try {
          await deleteMutation.mutateAsync(id);
          return true;
        } catch {
          return false;
        }
      },
      [deleteMutation],
  );

  const assignUserToStakeholderAction = useCallback(
      async (stakeholderId: number, userId: number) => {
        try {
          await assignUserMutation.mutateAsync({
            stakeholderId,
            userId,
          });

          return true;
        } catch {
          return false;
        }
      },
      [assignUserMutation],
  );

  // ---------------------------------------------------------------------------
  // Query errors
  // ---------------------------------------------------------------------------

  const listError = stakeholdersQuery.error
      ? getErrorMessage(stakeholdersQuery.error, "Unable to load stakeholders.")
      : null;

  const detailError = detailQuery.error
      ? getErrorMessage(detailQuery.error, "Unable to load stakeholder detail.")
      : null;

  const membersError =
      membersQuery.error || userOptionsQuery.error
          ? getErrorMessage(
              membersQuery.error ?? userOptionsQuery.error,
              "Unable to load users for this stakeholder.",
          )
          : null;

  return {
    stakeholders: stakeholdersQuery.data?.data ?? EMPTY_STAKEHOLDERS,
    stakeholderOptions:
      stakeholderOptionsQuery.data?.data ?? EMPTY_STAKEHOLDERS,
    listMeta: stakeholdersQuery.data?.meta ?? EMPTY_LIST_META,
    dashboardSummary: summaryQuery.data ?? EMPTY_SUMMARY,
    stakeholderTypes: typesQuery.data ?? EMPTY_TYPES,
    stakeholderDetail: detailQuery.data ?? null,
    users: userOptionsQuery.data ?? EMPTY_USERS,
    memberUserIds,

    isLoading: stakeholdersQuery.isLoading || summaryQuery.isLoading,
    isLoadingStakeholders: stakeholdersQuery.isLoading,
    isLoadingSummary: summaryQuery.isLoading,
    isLoadingTypes: typesQuery.isLoading,
    isLoadingDetail: detailQuery.isLoading && activeDetailId !== null,
    isLoadingUsers:
        (membersQuery.isLoading || userOptionsQuery.isLoading) &&
        activeMembersId !== null,

    isSaving: createMutation.isPending || updateMutation.isPending,
    isDeleting: deleteMutation.isPending,
    isAssigningUser: assignUserMutation.isPending,

    error: error ?? listError ?? detailError ?? membersError,
    successMessage,

    loadStakeholders,
    loadStakeholderTypes,
    clearMessages,

    addStakeholder,
    editStakeholder,
    removeStakeholder,

    loadStakeholderDetail,
    clearStakeholderDetail,

    loadUsersForStakeholder,

    assignUserToStakeholder: assignUserToStakeholderAction,
  };
}
