import type { QueryClient } from '@tanstack/vue-query';
import { api } from '@/packages/api/src';
import { getCurrentOrganizationId, getCurrentMembershipId } from '@/utils/useUser';
import { canViewClients, canViewMembers } from '@/utils/permissions';
import {
    getInitialWeekRange,
    getExpandedCalendarDateRange,
    createCalendarQueryKey,
    fetchAllCalendarEntries,
} from '@/utils/useTimeEntriesCalendarQuery';
import { fetchAllProjects } from '@/utils/useProjectsQuery';
import { fetchAllTasks } from '@/utils/useTasksQuery';
import { fetchAllTags } from '@/utils/useTagsQuery';
import { fetchAllClients } from '@/utils/useClientsQuery';
import { fetchAllMembers } from '@/utils/useMembersQuery';
import { fetchAllReports } from '@/utils/useReportsQuery';
import { fetchAllProjectMembers } from '@/utils/useProjectMembersQuery';

/**
 * Route patterns mapped to their prefetch functions.
 * Each function receives the QueryClient and prefetches relevant data.
 */
const routePrefetchers: Record<string, (queryClient: QueryClient) => void> = {
    '/': (queryClient) => {
        prefetchDashboard(queryClient);
    },

    '/dashboard': (queryClient) => {
        prefetchDashboard(queryClient);
    },

    '/time': (queryClient) => {
        prefetchProjects(queryClient);
        prefetchTasks(queryClient);
        prefetchTags(queryClient);
        prefetchClients(queryClient);
        prefetchTimeEntries(queryClient);
    },

    '/calendar': (queryClient) => {
        prefetchProjects(queryClient);
        prefetchTasks(queryClient);
        prefetchTags(queryClient);
        prefetchClients(queryClient);
        prefetchCalendarTimeEntries(queryClient);
    },

    '/projects': (queryClient) => {
        prefetchProjects(queryClient);
        prefetchClients(queryClient);
    },

    '/clients': (queryClient) => {
        prefetchClients(queryClient);
    },

    '/tags': (queryClient) => {
        prefetchTags(queryClient);
    },

    '/members': (queryClient) => {
        prefetchMembers(queryClient);
    },

    '/reporting': (queryClient) => {
        prefetchProjects(queryClient);
        prefetchTags(queryClient);
        prefetchClients(queryClient);
        prefetchMembers(queryClient);
    },

    '/reporting/detailed': (queryClient) => {
        prefetchProjects(queryClient);
        prefetchTasks(queryClient);
        prefetchTags(queryClient);
        prefetchClients(queryClient);
        prefetchMembers(queryClient);
    },

    '/reporting/shared': (queryClient) => {
        prefetchReports(queryClient);
    },
};

function prefetchDashboard(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId) return;

    // Prefetch projects and tasks for RecentlyTrackedTasksCard
    prefetchProjects(queryClient);
    prefetchTasks(queryClient);

    // Prefetch all dashboard card data
    // Must match the query in RecentlyTrackedTasksCard exactly — same key, same params
    queryClient.prefetchQuery({
        queryKey: ['timeEntries', organizationId],
        queryFn: () =>
            api.getTimeEntries({
                params: { organization: organizationId },
                queries: { member_id: getCurrentMembershipId(), type: 'work' },
            }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['lastSevenDays', organizationId],
        queryFn: () => api.lastSevenDays({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['dailyTrackedHours', organizationId],
        queryFn: () => api.dailyTrackedHours({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['weeklyProjectOverview', organizationId],
        queryFn: () => api.weeklyProjectOverview({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['totalWeeklyTime', organizationId],
        queryFn: () => api.totalWeeklyTime({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['totalWeeklyBillableTime', organizationId],
        queryFn: () => api.totalWeeklyBillableTime({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['totalWeeklyBillableAmount', organizationId],
        queryFn: () => api.totalWeeklyBillableAmount({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    queryClient.prefetchQuery({
        queryKey: ['weeklyHistory', organizationId],
        queryFn: () => api.weeklyHistory({ params: { organization: organizationId } }),
        staleTime: 30000,
    });

    // Prefetch team activity only if user has permission
    if (canViewMembers()) {
        queryClient.prefetchQuery({
            queryKey: ['latestTeamActivity', organizationId],
            queryFn: () => api.latestTeamActivity({ params: { organization: organizationId } }),
            staleTime: 30000,
        });
    }
}

function prefetchProjects(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId) return;

    queryClient.prefetchQuery({
        queryKey: ['projects', organizationId],
        queryFn: async () => ({ data: await fetchAllProjects(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchTasks(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId) return;

    queryClient.prefetchQuery({
        queryKey: ['tasks', organizationId],
        queryFn: async () => ({ data: await fetchAllTasks(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchTags(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId) return;

    queryClient.prefetchQuery({
        queryKey: ['tags', organizationId],
        queryFn: async () => ({ data: await fetchAllTags(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchClients(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId || !canViewClients()) return;

    queryClient.prefetchQuery({
        queryKey: ['clients', organizationId],
        queryFn: async () => ({ data: await fetchAllClients(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchMembers(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId || !canViewMembers()) return;

    queryClient.prefetchQuery({
        queryKey: ['members', organizationId],
        queryFn: async () => ({ data: await fetchAllMembers(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchReports(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId) return;

    queryClient.prefetchQuery({
        queryKey: ['reports', organizationId],
        queryFn: async () => ({ data: await fetchAllReports(organizationId) }),
        staleTime: 30000,
    });
}

function prefetchTimeEntries(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    const memberId = getCurrentMembershipId();
    if (!organizationId) return;

    queryClient.prefetchInfiniteQuery({
        queryKey: ['timeEntries', 'infinite', { organizationId, memberId }],
        queryFn: async () => {
            const response = await api.getTimeEntries({
                params: { organization: organizationId },
                queries: {
                    only_full_dates: 'true',
                    member_id: memberId,
                },
            });
            return response;
        },
        initialPageParam: undefined,
        staleTime: 30000,
    });
}

function prefetchCalendarTimeEntries(queryClient: QueryClient) {
    const organizationId = getCurrentOrganizationId();
    const memberId = getCurrentMembershipId();
    if (!organizationId) return;

    const { start, end } = getInitialWeekRange();
    const { start: formattedStart, end: formattedEnd } = getExpandedCalendarDateRange(start, end);

    queryClient.prefetchQuery({
        queryKey: createCalendarQueryKey(formattedStart, formattedEnd, organizationId),
        queryFn: () =>
            fetchAllCalendarEntries(organizationId, memberId, formattedStart, formattedEnd),
        staleTime: 30000,
    });
}

function prefetchProjectMembers(queryClient: QueryClient, projectId: string) {
    const organizationId = getCurrentOrganizationId();
    if (!organizationId || !canViewMembers()) return;

    queryClient.prefetchQuery({
        queryKey: ['projectMembers', organizationId, projectId],
        queryFn: async () => ({
            data: await fetchAllProjectMembers(organizationId, projectId),
        }),
        staleTime: 30000,
    });
}

/**
 * Matches a URL to find the appropriate prefetcher.
 * Handles both exact matches and pattern matching for dynamic routes.
 */
function findPrefetcher(url: string): ((queryClient: QueryClient) => void) | undefined {
    // Extract pathname from URL
    const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0]!;

    // Try exact match first
    if (pathname && routePrefetchers[pathname]) {
        return routePrefetchers[pathname];
    }

    // Try pattern matching for dynamic routes like /projects/{id}
    const projectMatch = pathname?.match(/^\/projects\/([^/]+)$/);
    if (projectMatch) {
        const projectId = projectMatch[1]!;
        return (queryClient) => {
            prefetchProjects(queryClient);
            prefetchTasks(queryClient);
            prefetchProjectMembers(queryClient, projectId);
        };
    }

    return undefined;
}

/**
 * Sets up Inertia prefetch event listener to warm TanStack Query cache.
 * Call this once during app initialization.
 */
export function setupPrefetching(queryClient: QueryClient) {
    // Listen for the 'prefetching' event which fires when Inertia starts prefetching a page
    // The event detail contains the visit object with the URL being prefetched
    document.addEventListener('inertia:prefetching', ((event: CustomEvent) => {
        const visit = event.detail?.visit;
        if (!visit?.url) return;

        const url = visit.url.href || visit.url.toString();
        const prefetcher = findPrefetcher(url);

        if (prefetcher) {
            prefetcher(queryClient);
        }
    }) as EventListener);
}
