// ============================================================
// context/ObjectiveMappingsContext.jsx
// Provides channels, objectives, and mappings state for
// the ObjectiveMappingPanel. Follows TemplatesContext pattern.
// ============================================================
import React, { createContext, useContext, useState, useCallback, useEffect, useMemo } from 'react';
import { useAdmin } from './AdminContext';

// ── Provider-specific objective options ──
// These stay as client-side helpers for the mapping form.
// Could move to a tbProviderObjective table later.
export const PROVIDER_OBJECTIVES = {
    google_ads: [
        { value: 'MAXIMIZE_CONVERSIONS',       label: 'Maximize Conversions' },
        { value: 'MAXIMIZE_CONVERSION_VALUE',   label: 'Maximize Conversion Value' },
        { value: 'TARGET_CPA',                  label: 'Target CPA' },
        { value: 'TARGET_ROAS',                 label: 'Target ROAS' },
        { value: 'MAXIMIZE_CLICKS',             label: 'Maximize Clicks' },
        { value: 'TARGET_IMPRESSION_SHARE',     label: 'Target Impression Share' },
        { value: 'MANUAL_CPC',                  label: 'Manual CPC' },
    ],
    meta: [
        { value: 'OUTCOME_AWARENESS',   label: 'Awareness' },
        { value: 'OUTCOME_TRAFFIC',     label: 'Traffic' },
        { value: 'OUTCOME_ENGAGEMENT',  label: 'Engagement' },
        { value: 'OUTCOME_LEADS',       label: 'Leads' },
        { value: 'OUTCOME_SALES',       label: 'Sales' },
        { value: 'CONVERSIONS',         label: 'Conversions' },
    ],
    tiktok: [
        { value: 'REACH',       label: 'Reach' },
        { value: 'TRAFFIC',     label: 'Traffic' },
        { value: 'VIDEO_VIEWS', label: 'Video Views' },
        { value: 'CONVERSION',  label: 'Conversion' },
        { value: 'APP_INSTALL', label: 'App Install' },
    ],
};

const ObjectiveMappingsContext = createContext(null);

export function useObjectiveMappings() {
    const ctx = useContext(ObjectiveMappingsContext);
    if (!ctx) throw new Error('useObjectiveMappings must be used within <ObjectiveMappingsProvider>');
    return ctx;
}

export function ObjectiveMappingsProvider({ children }) {
    const { data, apiCall, refresh, loading: adminLoading } = useAdmin();

    // ── Metadata state ──
    const [channels, setChannels]     = useState([]);
    const [objectives, setObjectives] = useState([]);
    const [metaLoading, setMetaLoading] = useState(true);
    const [metaError, setMetaError]   = useState(null);

    // ── Track whether API endpoints returned data or need fallback ──
    const [needsChannelFallback, setNeedsChannelFallback] = useState(false);
    const [needsObjectiveFallback, setNeedsObjectiveFallback] = useState(false);

    // ── Fetch channels + objectives once on mount ──
    useEffect(() => {
        let cancelled = false;

        const fetchMeta = async () => {
            setMetaLoading(true);
            setMetaError(null);

            try {
                const [chRes, objRes] = await Promise.all([
                    apiCall('/api/admin/template-config/channels', 'GET'),
                    apiCall('/api/admin/template-config/objectives', 'GET'),
                ]);

                if (!cancelled) {
                    // Channels
                    if (chRes?.ok && chRes.channels) {
                        setChannels(chRes.channels);
                    } else {
                        setNeedsChannelFallback(true);
                    }

                    // Objectives
                    if (objRes?.ok && objRes.objectives) {
                        setObjectives(objRes.objectives);
                    } else {
                        setNeedsObjectiveFallback(true);
                    }
                }
            } catch (err) {
                if (!cancelled) setMetaError(err.message);
            } finally {
                if (!cancelled) setMetaLoading(false);
            }
        };

        fetchMeta();
        return () => { cancelled = true; };
    }, [apiCall]); // apiCall is stable — runs once on mount

    // ── Fallback: derive from mapping data once it arrives ──
    useEffect(() => {
        if (!data?.mappings) return;
        if (needsChannelFallback && channels.length === 0) {
            console.info('[ObjectiveMappings] Deriving channels from mapping data');
            setChannels(deriveChannelsFromMappings(data));
        }
        if (needsObjectiveFallback && objectives.length === 0) {
            console.info('[ObjectiveMappings] Deriving objectives from mapping data');
            setObjectives(deriveObjectivesFromMappings(data));
        }
    }, [data, needsChannelFallback, needsObjectiveFallback, channels.length, objectives.length]);

    // ── Mappings come from AdminContext `data` (the /api/admin/objectives response) ──
    const mappings = useMemo(() => {
        if (!data?.mappings) return [];
        return data.mappings;
    }, [data]);

    // ── Lookup maps ──
    const channelMap = useMemo(() => {
        const map = {};
        channels.forEach(ch => {
            map[ch.code] = ch;
        });
        return map;
    }, [channels]);

    const objectiveColorMap = useMemo(() => {
        const map = {};
        objectives.forEach(obj => {
            map[obj.name] = obj.color || '#6B7280';
        });
        return map;
    }, [objectives]);

    // ── Coverage matrix: { [objective]: { [channelCode]: mapping | null } } ──
    const coverageMatrix = useMemo(() => {
        const activeObjectives = objectives.filter(o => o.isActive);
        const activeChannels = channels.filter(c => c.isActive);
        const matrix = {};

        activeObjectives.forEach(obj => {
            matrix[obj.name] = {};
            activeChannels.forEach(ch => {
                const mapping = mappings.find(
                    m => m.platformObjective === obj.name && m.channelType === ch.code && m.isActive
                );
                matrix[obj.name][ch.code] = mapping || null;
            });
        });

        return matrix;
    }, [objectives, channels, mappings]);

    // ── Coverage stats ──
    const coverageStats = useMemo(() => {
        const activeObjectives = objectives.filter(o => o.isActive);
        const activeChannels = channels.filter(c => c.isActive);
        const totalCells = activeObjectives.length * activeChannels.length;
        let filledCells = 0;

        activeObjectives.forEach(obj => {
            activeChannels.forEach(ch => {
                if (coverageMatrix[obj.name]?.[ch.code]) filledCells++;
            });
        });

        return {
            total: totalCells,
            filled: filledCells,
            gaps: totalCells - filledCells,
            pct: totalCells > 0 ? Math.round((filledCells / totalCells) * 100) : 0,
        };
    }, [coverageMatrix, objectives, channels]);

    // ── CRUD callbacks ──
    const createMapping = useCallback(async (body) => {
        const result = await apiCall('/api/admin/objectives', 'POST', body);
        if (result?.ok) refresh();
        return result;
    }, [apiCall, refresh]);

    const updateMapping = useCallback(async (mappingId, body) => {
        const result = await apiCall(`/api/admin/objectives/${mappingId}`, 'PUT', body);
        if (result?.ok) refresh();
        return result;
    }, [apiCall, refresh]);

    const deleteMapping = useCallback(async (mappingId) => {
        const result = await apiCall(`/api/admin/objectives/${mappingId}`, 'DELETE');
        if (result?.ok) refresh();
        return result;
    }, [apiCall, refresh]);

    // ── Provider objectives helper ──
    const getProviderObjectives = useCallback((channelCode) => {
        return PROVIDER_OBJECTIVES[channelCode] || [];
    }, []);

    const value = useMemo(() => ({
        // Data
        channels,
        objectives,
        mappings,
        channelMap,
        objectiveColorMap,
        coverageMatrix,
        coverageStats,

        // Loading
        loading: adminLoading || metaLoading,
        metaLoading,
        metaError,

        // CRUD
        createMapping,
        updateMapping,
        deleteMapping,

        // Helpers
        getProviderObjectives,
        refresh,
    }), [
        channels, objectives, mappings, channelMap, objectiveColorMap,
        coverageMatrix, coverageStats, adminLoading, metaLoading, metaError,
        createMapping, updateMapping, deleteMapping, getProviderObjectives, refresh,
    ]);

    return (
        <ObjectiveMappingsContext.Provider value={value}>
            {children}
        </ObjectiveMappingsContext.Provider>
    );
}

// ── Fallback derivation helpers ──
function deriveChannelsFromMappings(data) {
    if (!data?.mappings) return [];
    const codes = [...new Set(data.mappings.map(m => m.channelType))].sort();
    const defaults = {
        google_ads: { label: 'Google Ads', color: '#4285F4', icon: '🔵' },
        meta:       { label: 'Meta',       color: '#8B5CF6', icon: '🟣' },
        tiktok:     { label: 'TikTok',     color: '#FF004F', icon: '🔴' },
    };
    return codes.map((code, i) => ({
        channelId: i + 1,
        code,
        label: defaults[code]?.label || code,
        color: defaults[code]?.color || '#6B7280',
        icon: defaults[code]?.icon || '📢',
        sortOrder: i,
        isActive: true,
    }));
}

function deriveObjectivesFromMappings(data) {
    if (!data?.mappings) return [];
    const names = [...new Set(data.mappings.map(m => m.platformObjective))].sort();
    const defaultColors = {
        awareness:   '#3B82F6',
        traffic:     '#F59E0B',
        leads:       '#8B5CF6',
        conversions: '#10B981',
        sales:       '#EF4444',
    };
    return names.map((name, i) => ({
        objectiveId: i + 1,
        name,
        color: defaultColors[name] || '#6B7280',
        sortOrder: i,
        isActive: true,
    }));
}
