/**
 * Layer Factory for Mapbox Globe Visualizations
 *
 * Creates optimized, best-practice layers for vaccine data visualization
 * using Point geometries and circle layers with data-driven styling.
 */

/**
 * Color scale presets for different metrics
 */
export const COLOR_SCALES = {
    // Coverage: Red (low) → Green (high)
    coverage: {
        type: 'sequential',
        domain: [0, 100],
        colors: ['#d73027', '#fc8d59', '#fee090', '#e0f3f8', '#91bfdb', '#4575b4'],
        stops: [0, 20, 40, 60, 80, 100]
    },

    // Coverage (reversed): Green (high) → Red (low)
    coverageReverse: {
        type: 'sequential',
        domain: [0, 100],
        colors: ['#d73027', '#fc8d59', '#fee08b', '#d9ef8b', '#91cf60', '#1a9850'],
        stops: [0, 20, 40, 60, 80, 100]
    },

    // Diverging: Green (good) ← Gray (neutral) → Red (bad)
    diverging: {
        type: 'diverging',
        domain: [0, 100],
        colors: ['#d73027', '#fc8d59', '#fee08b', '#ffffbf', '#d9ef8b', '#91cf60', '#1a9850'],
        stops: [0, 16.67, 33.33, 50, 66.67, 83.33, 100]
    },

    // Purple gradient (for HPV)
    purple: {
        type: 'sequential',
        domain: [0, 100],
        colors: ['#4a0e4e', '#6b1b6b', '#8e2a8e', '#b366ff', '#d499ff', '#ebccff'],
        stops: [0, 20, 40, 60, 80, 100]
    },

    // Blue-Orange diverging
    blueOrange: {
        type: 'diverging',
        domain: [0, 100],
        colors: ['#313695', '#4575b4', '#74add1', '#fee090', '#fdae61', '#f46d43', '#a50026'],
        stops: [0, 16.67, 33.33, 50, 66.67, 83.33, 100]
    }
};

/**
 * Layer Factory Class
 */
export class LayerFactory {
    constructor(map) {
        this.map = map;
    }

    /**
     * Create a circle layer with best practices
     * @param {Object} config - Layer configuration
     * @returns {Object} Mapbox layer specification
     */
    createCircleLayer(config) {
        const {
            id,
            source,
            sizeProperty = 'population',
            sizeRange = [5, 30],
            colorProperty = 'coverage',
            colorScale = 'coverage',
            opacityRange = [0.8, 0.95],
            filter = null
        } = config;

        const layer = {
            id: id,
            type: 'circle',
            source: source,
            paint: {
                // Size with zoom-responsive scaling
                'circle-radius': this.createSizeExpression(sizeProperty, sizeRange),

                // Color using specified scale
                'circle-color': this.createColorExpression(colorProperty, colorScale),

                // Opacity with zoom adjustment
                'circle-opacity': [
                    'interpolate',
                    ['linear'],
                    ['zoom'],
                    1, opacityRange[0],
                    4, (opacityRange[0] + opacityRange[1]) / 2,
                    8, opacityRange[1]
                ],

                // Stroke for definition
                'circle-stroke-width': [
                    'interpolate',
                    ['linear'],
                    ['zoom'],
                    1, 0.5,
                    4, 1,
                    8, 2
                ],
                'circle-stroke-color': '#ffffff',
                'circle-stroke-opacity': 0.6,

                // Subtle blur at low zoom for performance
                'circle-blur': [
                    'interpolate',
                    ['linear'],
                    ['zoom'],
                    1, 0.15,
                    4, 0.05,
                    8, 0
                ],

                // Pitch alignment for globe
                'circle-pitch-alignment': 'map',
                'circle-pitch-scale': 'map'
            }
        };

        // Only add filter if it's provided and not null
        if (filter) {
            layer.filter = filter;
        }

        return layer;
    }

    /**
     * Create size expression with zoom responsiveness
     * @param {string} property - Property to use for sizing
     * @param {Array} range - [minSize, maxSize]
     * @returns {Array} Mapbox expression
     */
    createSizeExpression(property, range) {
        const [minSize, maxSize] = range;

        return [
            'interpolate',
            ['linear'],
            ['zoom'],
            // At low zoom (globe view)
            1, [
                'interpolate',
                ['linear'],
                // Use coalesce to handle null/undefined values
                ['coalesce', ['get', property], 0],
                0, minSize * 0.6,
                100000000, maxSize * 0.6
            ],
            // At medium zoom
            4, [
                'interpolate',
                ['linear'],
                ['coalesce', ['get', property], 0],
                0, minSize,
                100000000, maxSize
            ],
            // At high zoom
            8, [
                'interpolate',
                ['linear'],
                ['coalesce', ['get', property], 0],
                0, minSize * 1.5,
                100000000, maxSize * 1.5
            ]
        ];
    }

    /**
     * Create color expression from scale preset
     * @param {string} property - Property to use for coloring
     * @param {string} scaleName - Name of color scale from COLOR_SCALES
     * @returns {Array} Mapbox expression
     */
    createColorExpression(property, scaleName) {
        const scale = COLOR_SCALES[scaleName] || COLOR_SCALES.coverage;

        const expression = [
            'interpolate',
            ['linear'],
            // Use coalesce to handle null/undefined values - defaults to 0
            ['coalesce', ['get', property], 0]
        ];

        // Add stops and colors
        scale.stops.forEach((stop, index) => {
            expression.push(stop, scale.colors[index]);
        });

        return expression;
    }

    /**
     * Create a custom color expression with specific stops
     * @param {string} property - Property to color by
     * @param {Array} stops - Array of [value, color] pairs
     * @returns {Array} Mapbox expression
     */
    createCustomColorExpression(property, stops) {
        const expression = [
            'interpolate',
            ['linear'],
            ['get', property]
        ];

        stops.forEach(([value, color]) => {
            expression.push(value, color);
        });

        return expression;
    }

    /**
     * Create a step color expression (discrete ranges)
     * @param {string} property - Property to color by
     * @param {Array} steps - Array of [threshold, color] pairs
     * @param {string} defaultColor - Default color
     * @returns {Array} Mapbox expression
     */
    createStepColorExpression(property, steps, defaultColor = '#cccccc') {
        const expression = [
            'step',
            ['get', property],
            defaultColor
        ];

        steps.forEach(([threshold, color]) => {
            expression.push(threshold, color);
        });

        return expression;
    }

    /**
     * Create a hover effect layer (larger, semi-transparent circles)
     * @param {string} sourceId - Source ID
     * @param {string} baseLayerId - Base layer ID to match
     * @returns {Object} Mapbox layer specification
     */
    createHoverLayer(sourceId, baseLayerId) {
        return {
            id: `${baseLayerId}-hover`,
            type: 'circle',
            source: sourceId,
            paint: {
                'circle-radius': [
                    'case',
                    ['boolean', ['feature-state', 'hover'], false],
                    35, // Larger when hovered
                    0   // Hidden otherwise
                ],
                'circle-color': '#ffffff',
                'circle-opacity': 0.3,
                'circle-stroke-width': 2,
                'circle-stroke-color': '#ffffff',
                'circle-stroke-opacity': 0.8
            }
        };
    }

    /**
     * Create a pulse animation layer
     * @param {string} sourceId - Source ID
     * @param {Object} config - Animation configuration
     * @returns {Object} Mapbox layer specification
     */
    createPulseLayer(sourceId, config = {}) {
        const {
            id = 'pulse-layer',
            sizeMultiplier = 1.5,
            color = 'rgba(74, 222, 128, 0.4)',
            filter = null
        } = config;

        return {
            id: id,
            type: 'circle',
            source: sourceId,
            filter: filter,
            paint: {
                'circle-radius': [
                    'interpolate',
                    ['linear'],
                    ['zoom'],
                    1, ['*', ['get', 'baseRadius'], sizeMultiplier * 0.8],
                    8, ['*', ['get', 'baseRadius'], sizeMultiplier * 1.2]
                ],
                'circle-color': color,
                'circle-opacity': 0.5,
                'circle-blur': 1
            }
        };
    }

    /**
     * Apply best-practice globe atmosphere
     * @param {Object} config - Atmosphere configuration
     */
    applyGlobeAtmosphere(config = {}) {
        const {
            theme = 'default',
            customConfig = null
        } = config;

        const atmospherePresets = {
            default: {
                color: 'rgba(186, 210, 235, 0.9)',
                'high-color': 'rgba(36, 92, 223, 0.5)',
                'horizon-blend': 0.02,
                'space-color': 'rgba(11, 11, 25, 1)',
                'star-intensity': 0.6
            },
            dark: {
                color: 'rgba(15, 20, 35, 0.95)',
                'high-color': 'rgba(40, 60, 100, 0.6)',
                'horizon-blend': 0.05,
                'space-color': 'rgba(5, 8, 15, 1)',
                'star-intensity': 0.85
            },
            medical: {
                color: 'rgba(10, 14, 26, 0.95)',
                'high-color': 'rgba(36, 92, 223, 0.4)',
                'horizon-blend': 0.04,
                'space-color': 'rgba(10, 10, 25, 1)',
                'star-intensity': 0.6
            },
            purple: {
                color: 'rgba(25, 15, 35, 0.9)',
                'high-color': 'rgba(80, 50, 120, 0.5)',
                'horizon-blend': 0.06,
                'space-color': 'rgba(8, 5, 15, 1)',
                'star-intensity': 0.8
            }
        };

        const atmosphere = customConfig || atmospherePresets[theme] || atmospherePresets.default;

        this.map.setFog(atmosphere);
    }

    /**
     * Setup interactive hover effects
     * @param {string} layerId - Layer to add hover to
     * @param {Function} callback - Optional callback on hover
     */
    setupHoverEffects(layerId, callback = null) {
        let hoveredFeatureId = null;

        // Mouse enter
        this.map.on('mouseenter', layerId, (e) => {
            this.map.getCanvas().style.cursor = 'pointer';

            if (e.features.length > 0) {
                if (hoveredFeatureId !== null) {
                    this.map.setFeatureState(
                        { source: layerId, id: hoveredFeatureId },
                        { hover: false }
                    );
                }

                hoveredFeatureId = e.features[0].id;

                this.map.setFeatureState(
                    { source: layerId, id: hoveredFeatureId },
                    { hover: true }
                );

                if (callback) callback(e.features[0]);
            }
        });

        // Mouse leave
        this.map.on('mouseleave', layerId, () => {
            this.map.getCanvas().style.cursor = '';

            if (hoveredFeatureId !== null) {
                this.map.setFeatureState(
                    { source: layerId, id: hoveredFeatureId },
                    { hover: false }
                );
            }

            hoveredFeatureId = null;
        });
    }

    /**
     * Create a popup with formatted content
     * @param {Object} feature - GeoJSON feature
     * @param {Object} config - Popup configuration
     * @returns {string} HTML content for popup
     */
    createPopupContent(feature, config = {}) {
        const props = feature.properties;
        const {
            title = props.name,
            metrics = [],
            showIncome = true,
            showRegion = true
        } = config;

        let html = `
            <div class="vaccine-popup">
                <h3 class="popup-title">${title}</h3>
        `;

        // Add region and income
        if (showRegion || showIncome) {
            html += '<div class="popup-meta">';
            if (showRegion && props.region) {
                html += `<span class="popup-region">${props.region}</span>`;
            }
            if (showIncome && props.income_level) {
                html += `<span class="popup-income">${this.formatIncome(props.income_level)}</span>`;
            }
            html += '</div>';
        }

        // Add metrics
        if (metrics.length > 0) {
            html += '<div class="popup-metrics">';
            metrics.forEach(metric => {
                const value = props[metric.property];
                const formatted = metric.format ? metric.format(value) : value;
                const className = metric.className || '';

                html += `
                    <div class="popup-metric ${className}">
                        <span class="metric-label">${metric.label}</span>
                        <span class="metric-value">${formatted}</span>
                    </div>
                `;
            });
            html += '</div>';
        }

        html += '</div>';

        return html;
    }

    /**
     * Format income level for display
     */
    formatIncome(income) {
        const formatted = {
            'low': 'Low Income',
            'lower-middle': 'Lower-Middle Income',
            'upper-middle': 'Upper-Middle Income',
            'high': 'High Income'
        };

        return formatted[income] || income;
    }

    /**
     * Add a simple legend to the map
     * @param {Object} config - Legend configuration
     */
    addLegend(config) {
        const {
            position = 'bottom-right',
            title = 'Legend',
            colorScale = 'coverage',
            labels = null
        } = config;

        const scale = COLOR_SCALES[colorScale] || COLOR_SCALES.coverage;

        const legendDiv = document.createElement('div');
        legendDiv.className = `legend legend-${position}`;
        legendDiv.style.cssText = `
            position: absolute;
            background: rgba(10, 14, 26, 0.95);
            padding: 20px;
            border-radius: 12px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            z-index: 1000;
            min-width: 200px;
        `;

        // Position
        const positions = {
            'top-left': 'top: 20px; left: 20px;',
            'top-right': 'top: 20px; right: 20px;',
            'bottom-left': 'bottom: 20px; left: 20px;',
            'bottom-right': 'bottom: 20px; right: 20px;'
        };
        legendDiv.style.cssText += positions[position] || positions['bottom-right'];

        // Build legend HTML
        let html = `<h3 style="margin: 0 0 15px 0; font-size: 14px; color: #9ca3af;">${title}</h3>`;

        // Gradient bar
        const gradient = `linear-gradient(to right, ${scale.colors.join(', ')})`;
        html += `
            <div style="
                height: 20px;
                border-radius: 4px;
                background: ${gradient};
                margin-bottom: 8px;
                border: 1px solid rgba(255, 255, 255, 0.2);
            "></div>
        `;

        // Labels
        const labelArray = labels || [
            scale.stops[0],
            scale.stops[Math.floor(scale.stops.length / 2)],
            scale.stops[scale.stops.length - 1]
        ];

        html += `
            <div style="
                display: flex;
                justify-content: space-between;
                font-size: 11px;
                color: #78909c;
            ">
                ${labelArray.map(l => `<span>${l}${scale.domain[0] === 0 && scale.domain[1] === 100 ? '%' : ''}</span>`).join('')}
            </div>
        `;

        legendDiv.innerHTML = html;
        this.map.getContainer().parentElement.appendChild(legendDiv);

        return legendDiv;
    }
}

/**
 * Convenience function to create a complete vaccine visualization layer
 */
export function createVaccineLayer(map, config) {
    const factory = new LayerFactory(map);

    const {
        sourceId,
        layerId,
        vaccineType = 'coverage',
        sizeProperty = 'population',
        colorProperty = 'coverage',
        colorScale = 'coverage',
        atmosphere = 'default',
        legend = true
    } = config;

    // Apply atmosphere
    factory.applyGlobeAtmosphere({ theme: atmosphere });

    // Create main layer
    const layer = factory.createCircleLayer({
        id: layerId,
        source: sourceId,
        sizeProperty: sizeProperty,
        colorProperty: colorProperty,
        colorScale: colorScale
    });

    map.addLayer(layer);

    // Setup hover effects
    factory.setupHoverEffects(layerId);

    // Add legend if requested
    if (legend) {
        factory.addLegend({
            title: `${vaccineType.charAt(0).toUpperCase() + vaccineType.slice(1)} Coverage`,
            colorScale: colorScale
        });
    }

    return factory;
}
