import type { ReactNode } from 'react';
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { apiClient } from '@/lib/api-client';
import {
    leadsQuery,
    campaignsQuery,
    adsQuery,
    followUpsQuery,
    leadActivitiesQuery,
    callsQuery,
    appointmentsQuery,
    patientsQuery,
} from '@/lib/queries';
import {
    transformLeads,
    transformCampaigns,
    transformAds,
    transformFollowUps,
    transformLeadActivities,
    transformCalls,
    transformAppointments,
    transformPatients,
} from '@/lib/transforms';

import type { Lead, Campaign, Ad, LeadActivity, FollowUp, WhatsAppTemplate, Agent, Call, LeadIngestionSource, Patient, Appointment } from '@/types/entities';

type DataContextType = {
    leads: Lead[];
    campaigns: Campaign[];
    ads: Ad[];
    followUps: FollowUp[];
    leadActivities: LeadActivity[];
    templates: WhatsAppTemplate[];
    agents: Agent[];
    calls: Call[];
    appointments: Appointment[];
    patients: Patient[];
    ingestionSources: LeadIngestionSource[];
    loading: boolean;
    error: string | null;
    updateLead: (id: string, updates: Partial<Lead>) => void;
    addCall: (call: Call) => void;
    refresh: () => void;
};

const DataContext = createContext<DataContextType | undefined>(undefined);

export const useData = (): DataContextType => {
    const context = useContext(DataContext);

    if (context === undefined) {
        throw new Error('useData must be used within a DataProvider');
    }

    return context;
};

interface DataProviderProps {
    children: ReactNode;
}

export const DataProvider = ({ children }: DataProviderProps) => {
    const [leads, setLeads] = useState<Lead[]>([]);
    const [campaigns, setCampaigns] = useState<Campaign[]>([]);
    const [ads, setAds] = useState<Ad[]>([]);
    const [followUps, setFollowUps] = useState<FollowUp[]>([]);
    const [leadActivities, setLeadActivities] = useState<LeadActivity[]>([]);
    const [calls, setCalls] = useState<Call[]>([]);
    const [appointments, setAppointments] = useState<Appointment[]>([]);
    const [patients, setPatients] = useState<Patient[]>([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState<string | null>(null);
    const hasLoadedRef = useRef(false);

    // These don't have platform entities yet — empty for now
    const [templates] = useState<WhatsAppTemplate[]>([]);
    const [agents] = useState<Agent[]>([]);
    const [ingestionSources] = useState<LeadIngestionSource[]>([]);

    const fetchData = useCallback(async () => {
        if (!apiClient.isAuthenticated()) {
            setLoading(false);
            return;
        }

        // Only flip the global loading flag on the very first fetch. Background
        // polls refresh data in place so the UI doesn't flash "Loading..." —
        // QA reported this as the supervisor surfaces randomly refreshing.
        if (!hasLoadedRef.current) {
            setLoading(true);
        }
        setError(null);

        try {
            const gql = <T,>(query: string) => apiClient.graphql<T>(query, undefined, { silent: true }).catch(() => null);

            // Generic Relay pagination. Keeps paging until hasNextPage=false
            // or we hit MAX_PAGES (guard against runaway loops on bad data).
            // Returned shape mirrors the original single-page response so
            // transformX functions work unchanged — they already read
            // `{ <rootField>: { edges } }`.
            const MAX_PAGES = 25;
            const fetchAll = async (rootField: string, builder: (after?: string) => string): Promise<any | null> => {
                const allEdges: any[] = [];
                let after: string | undefined = undefined;
                for (let page = 0; page < MAX_PAGES; page++) {
                    const data: any = await gql<any>(builder(after));
                    if (!data) return null;
                    const root: any = data[rootField];
                    if (!root) break;
                    if (Array.isArray(root.edges)) allEdges.push(...root.edges);
                    if (!root.pageInfo?.hasNextPage) break;
                    after = root.pageInfo.endCursor;
                    if (!after) break;
                }
                return { [rootField]: { edges: allEdges } };
            };

            const [leadsData, campaignsData, adsData, followUpsData, activitiesData, callsData, appointmentsData, patientsData] = await Promise.all([
                fetchAll('leads', leadsQuery),
                fetchAll('campaigns', campaignsQuery),
                fetchAll('ads', adsQuery),
                fetchAll('followUps', followUpsQuery),
                fetchAll('leadActivities', leadActivitiesQuery),
                fetchAll('calls', callsQuery),
                fetchAll('appointments', appointmentsQuery),
                fetchAll('patients', patientsQuery),
            ]);

            if (leadsData) setLeads(transformLeads(leadsData));
            if (campaignsData) setCampaigns(transformCampaigns(campaignsData));
            if (adsData) setAds(transformAds(adsData));
            if (followUpsData) setFollowUps(transformFollowUps(followUpsData));
            if (activitiesData) setLeadActivities(transformLeadActivities(activitiesData));
            if (callsData) setCalls(transformCalls(callsData));
            if (appointmentsData) setAppointments(transformAppointments(appointmentsData));
            if (patientsData) setPatients(transformPatients(patientsData));
        } catch (err: any) {
            setError(err.message ?? 'Failed to load data');
        } finally {
            hasLoadedRef.current = true;
            setLoading(false);
        }
    }, []);

    useEffect(() => {
        fetchData();
        // Poll every 30 seconds for fresh data (calls, leads, appointments)
        const interval = setInterval(() => {
            console.log('[DATA-PROVIDER] Polling for fresh data');
            fetchData();
        }, 30_000);
        return () => clearInterval(interval);
    }, [fetchData]);

    const updateLead = (id: string, updates: Partial<Lead>) => {
        setLeads((prev) => prev.map((lead) => (lead.id === id ? { ...lead, ...updates } : lead)));
    };

    const addCall = (call: Call) => {
        setCalls((prev) => [call, ...prev]);
    };

    return (
        <DataContext.Provider value={{
            leads, campaigns, ads, followUps, leadActivities, templates, agents, calls, appointments, patients, ingestionSources,
            loading, error,
            updateLead, addCall, refresh: fetchData,
        }}>
            {children}
        </DataContext.Provider>
    );
};
