import { SalesRecord, AdsRecord, TrafficRecord, CombinedKPIs, FilterState, AggregatedData, LineGrowthMetric, ItemGrowthMetric, SeasonalityPoint, YearlySplitData, PivotRow, YearlyData, TimeSeriesData, ComparisonTimeSeriesPoint, ForecastRecord, MonthlyForecastPoint, ProductForecastData } from '../types';
import * as XLSX from 'xlsx';
import Papa from 'papaparse';

// Helper to parse currency values handling both EU (1.234,56) and US/Standard (1,234.56 or 1234.56) formats
const parseCurrency = (value: string): number => {
    if (!value) return 0;

    // Remove currency symbol and whitespace
    let clean = value.replace(/[€$£\s]/g, '').trim();

    // HEURISTIC: 
    // If it contains a comma, we assume it's likely European format (Decimal separator)
    // UNLESS it also contains a dot and the comma is before the dot (e.g. 1,000.50 - US format)
    // But given the context (DE data), comma is usually decimal.

    // Case A: European Format (e.g., "277.179,09" or "50,00" or "263,83")
    if (clean.includes(',') && !clean.includes('.')) {
        // Likely EU decimal without thousands or with thousands implicitly handled
        // e.g. "263,83" -> "263.83"
        clean = clean.replace(',', '.');
        return parseFloat(clean);
    }
    else if (clean.includes(',') && clean.includes('.')) {
        // Mixed: 1.234,56 -> EU
        if (clean.indexOf(',') > clean.indexOf('.')) {
            clean = clean.replace(/\./g, '').replace(',', '.');
        } else {
            // 1,234.56 -> US
            clean = clean.replace(/,/g, '');
        }
        return parseFloat(clean);
    }

    // Case B: Standard/US Format or Clean Number (e.g. "277179.09" or "1000")
    clean = clean.replace(/,/g, ''); // Remove commas just in case
    const num = parseFloat(clean);

    return isNaN(num) ? 0 : num;
};

const parseUnits = (value: string): number => {
    if (!value) return 0;
    // Remove dots (thousands separators in EU) and commas (thousands in US) just to be safe for integers
    const clean = value.replace(/[\.,]/g, '');
    const num = parseInt(clean, 10);
    return isNaN(num) ? 0 : num;
}

const MONTH_ORDER = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// Comprehensive Month Mapping (English + Spanish + Short/Full)
const MONTH_MAP: Record<string, string> = {
    // English Short
    'jan': 'Jan', 'feb': 'Feb', 'mar': 'Mar', 'apr': 'Apr', 'may': 'May', 'jun': 'Jun',
    'jul': 'Jul', 'aug': 'Aug', 'sep': 'Sep', 'oct': 'Oct', 'nov': 'Nov', 'dec': 'Dec',
    // Spanish Short
    'ene': 'Jan', 'abr': 'Apr', 'ago': 'Aug', 'dic': 'Dec', 'set': 'Sep',
    // Spanish Full
    'enero': 'Jan', 'febrero': 'Feb', 'marzo': 'Mar', 'abril': 'Apr', 'mayo': 'May', 'junio': 'Jun',
    'julio': 'Jul', 'agosto': 'Aug', 'septiembre': 'Sep', 'octubre': 'Oct', 'noviembre': 'Nov', 'diciembre': 'Dec',
    // English Full
    'january': 'Jan', 'february': 'Feb', 'march': 'Mar', 'april': 'Apr', 'june': 'Jun',
    'july': 'Jul', 'august': 'Aug', 'september': 'Sep', 'october': 'Oct', 'november': 'Nov', 'december': 'Dec'
};

// Robust Month Normalizer
const monthCache: Record<string, string> = {};

const normalizeMonth = (rawMonth: string): string => {
    if (!rawMonth) return '';
    if (monthCache[rawMonth]) return monthCache[rawMonth];

    let m = String(rawMonth).trim().toLowerCase();

    // 0. Check for Excel Serial Date (e.g. 45544 -> Sep)
    // 25569 is the offset days between Excel epoch (1899-12-30) and Unix epoch (1970-01-01)
    // We check if it's a number > 20000 (roughly year 1954+) to avoid confusion with valid days like "31"
    const potentialSerial = parseFloat(m);
    if (!isNaN(potentialSerial) && potentialSerial > 20000) {
        // Convert Excel serial to JS Date
        const date = new Date(Math.round((potentialSerial - 25569) * 86400 * 1000));
        if (!isNaN(date.getTime())) {
            return MONTH_ORDER[date.getMonth()];
        }
    }

    // 1. Direct Map Lookup (Handles "jan", "enero", "sep", etc.)
    if (MONTH_MAP[m]) {
        monthCache[rawMonth] = MONTH_MAP[m];
        return MONTH_MAP[m];
    }

    // 2. Handle numeric months "01", "1", "01-2023"
    // If it's a full date string like "2023-04-01" or "01/04/2023"
    if (m.includes('/') || m.includes('-')) {
        // Try parsing standard date
        const date = new Date(m);
        if (!isNaN(date.getTime())) {
            const monthIdx = date.getMonth();
            const yearShort = date.getFullYear().toString().slice(2);
            return `${MONTH_ORDER[monthIdx]}-${yearShort}`;
        }
    }

    const numMatch = m.match(/^(\d{1,2})([^\d]|$)/);
    if (numMatch) {
        const num = parseInt(numMatch[1]);
        if (num >= 1 && num <= 12) return MONTH_ORDER[num - 1];
    }

    // 3. Fallback: Extract first 3 letters and capitalize
    const alphaMatch = m.match(/([a-zA-Z\u00C0-\u00FF]+)/);
    if (alphaMatch) {
        let alpha = alphaMatch[1];
        if (alpha.length > 3) alpha = alpha.substring(0, 3);
        // Check map again with short version
        if (MONTH_MAP[alpha]) return MONTH_MAP[alpha];

        return alpha.charAt(0).toUpperCase() + alpha.slice(1);
    }

    // Try to grab year from original string to append (e.g. "Apr-23") if strict matching failed
    const yearMatch = rawMonth.match(/(\d{2,4})/);
    if (yearMatch) {
        let y = yearMatch[1];
        if (y.length === 4) y = y.slice(2);
        // This part is likely fallback for Sales Data records
        const letters = m.replace(/[^a-z]/g, '');
        if (letters && MONTH_MAP[letters]) {
            return `${MONTH_MAP[letters]}-${y}`;
        }
    }

    monthCache[rawMonth] = rawMonth;
    return rawMonth; // Return as-is if all else fails
};

// Robust CSV Column Value Extractor
const getColumnValue = (row: any, aliases: string[]): string => {
    const rowKeys = Object.keys(row);
    const normalizedRowKeys: Record<string, string> = {};
    rowKeys.forEach(k => {
        normalizedRowKeys[k.trim().toLowerCase()] = k;
    });

    for (const alias of aliases) {
        const lookup = alias.trim().toLowerCase();
        if (normalizedRowKeys[lookup]) {
            const actualKey = normalizedRowKeys[lookup];
            const val = row[actualKey];
            if (val !== undefined && val !== null) {
                const strVal = String(val).trim();
                if (strVal.length > 0) {
                    return strVal;
                }
            }
        }
    }
    return '';
};
// Allowed Customers Whitelist
export const PAN_EU_COUNTRIES = ['Amazon DE', 'Amazon IT', 'Amazon FR', 'Amazon ES'];
const ALLOWED_CUSTOMERS = [...PAN_EU_COUNTRIES, 'Amazon UK', 'Amazon SC'];

const isAllowedCustomer = (customer: string): boolean => {
    if (!customer) return false;
    const normCustomer = customer.trim().toLowerCase();
    return ALLOWED_CUSTOMERS.some(allowed => allowed.toLowerCase() === normCustomer);
};

// --- SALES / SELL OUT MAPPING ---

const mapRowToRecord = (row: any, index: number): SalesRecord => {
    const customer = getColumnValue(row, ['NEW CUSTOMER', 'Customer', 'Client', 'Account', 'Partner', 'COUNTRY', 'Country', 'Market']) || 'Unknown';
    const yearStr = getColumnValue(row, ['YEAR', 'Year', 'D']);
    // Sanitize year string before parsing (remove commas/dots e.g. "2,023")
    let year = parseInt(yearStr.replace(/[,.]/g, '')) || 0;
    const monthStr = getColumnValue(row, ['MONTH', 'Month', 'Period']);
    const month = normalizeMonth(monthStr);

    // BACKFILL YEAR if missing but present in Month (e.g. "Apr-23")
    if (year === 0 && month.includes('-')) {
        const parts = month.split('-');
        if (parts.length === 2) {
            const yPart = parts[1];
            // assume 20xx for 2 digits
            if (yPart.length === 2) year = 2000 + parseInt(yPart);
            else if (yPart.length === 4) year = parseInt(yPart);
        }
    }

    const weekStr = getColumnValue(row, ['WEEK', 'Week', 'CW', 'Semana', 'KW', 'E']);
    const weekNum = weekStr ? parseInt(weekStr.replace(/cw/i, '').trim(), 10) : NaN;
    const week = isNaN(weekNum) ? undefined : weekNum;
    const line = getColumnValue(row, ['LICENSE', 'License']) || 'Unassigned';

    const asin = getColumnValue(row, [
        'CUSTOMER REFERENCE', 'AMAZON ASIN', 'ASIN', 'Asin', 'PRODUCT ID', 'ITEM IDENTIFIER', 'ASIN NO.', 'Product ASIN', 'IDENTIFIER'
    ]);

    const sku = getColumnValue(row, ['RAW ARTICLE NO.', 'SKU', 'Sku', 'Item No']);
    const title = getColumnValue(row, ['ARTICLE NAME (Craze)', 'Title', 'TITLE', 'Product Title', 'Article Name', 'ArticleName']);
    const articleName = getColumnValue(row, ['ARTICLE NAME (Craze)', 'Article Name', 'ArticleName', 'Title']);

    const unitsRaw = getColumnValue(row, ['UNITS', 'Units', 'Quantity', 'Qty']);
    const sellOutRaw = getColumnValue(row, ['AMOUNT', 'Sell Out', 'SellOut', 'Revenue', 'Sales', 'Turnover']);

    return {
        id: `row-${index}`,
        customer,
        year,
        month,
        week,
        asin,
        sku,
        title,
        articleName,
        units: parseUnits(unitsRaw),
        sellOut: parseCurrency(sellOutRaw),
        line
    };
};

export const processCSV = (fileOrContent: File | string): Promise<SalesRecord[]> => {
    return new Promise((resolve, reject) => {
        // @ts-ignore
        Papa.parse(fileOrContent, {
            header: true,
            skipEmptyLines: true,
            complete: (results: any) => {
                try {
                    const data: SalesRecord[] = results.data.map((row: any, index: number) => {
                        return mapRowToRecord(row, index);
                    })
                        // Filter: Valid Year > 2023 (exclude incomplete 2023 data) AND Allowed Customer
                        .filter((r: SalesRecord) => r.year > 2023 && isAllowedCustomer(r.customer));

                    resolve(data);
                } catch (err) {
                    reject(err);
                }
            },
            error: (error: any) => reject(error)
        });
    });
};

export const processExcel = async (file: File): Promise<SalesRecord[]> => {
    try {
        const arrayBuffer = await file.arrayBuffer();
        const workbook = XLSX.read(arrayBuffer);
        const firstSheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[firstSheetName];
        const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: "" });

        const data: SalesRecord[] = jsonData.map((row: any, index: number) => {
            return mapRowToRecord(row, index);
        })
            // Filter: Valid Year AND Allowed Customer
            .filter((r: SalesRecord) => r.year > 0 && isAllowedCustomer(r.customer));

        return data;
    } catch (error) {
        console.error("Error processing Excel file:", error);
        throw error;
    }
}

// --- ADS DATA MAPPING ---

const mapCountryToMarketplace = (country: string): string => {
    const c = String(country).toLowerCase().trim();
    if (c.includes('germany') || c.includes('deutschland') || c.includes('de')) return 'Amazon DE';
    if (c.includes('spain') || c.includes('espana') || c.includes('españa') || c.includes('es')) return 'Amazon ES';
    if (c.includes('france') || c.includes('fr')) return 'Amazon FR';
    if (c.includes('italy') || c.includes('italia') || c.includes('it')) return 'Amazon IT';
    if (c.includes('kingdom') || c.includes('uk') || c === 'gb') return 'Amazon UK';
    if (c.includes('netherlands') || c.includes('nederland') || c.includes('holland') || c.includes('nl')) return 'Amazon NL';
    return country.toUpperCase(); // Fallback
};

export const processAdsCSV = (file: File): Promise<AdsRecord[]> => {
    return new Promise((resolve, reject) => {
        // @ts-ignore
        Papa.parse(file, {
            header: false, // Index-based mapping
            skipEmptyLines: true,
            complete: (results: any) => {
                try {
                    const data: AdsRecord[] = [];
                    const rows = results.data;
                    const len = rows.length;

                    for (let i = 0; i < len; i++) {
                        const row = rows[i];
                        if (!Array.isArray(row) || row.length < 12) continue;

                        // Check header row (Column A: Customer or Country)
                        const c0 = String(row[0]).trim().toLowerCase();
                        if (c0.includes('customer') || c0.includes('country') || c0.includes('marketplace')) continue;

                        // Column mapping for CSV (same as Excel):
                        // A (0): Country
                        // B (1): Week
                        // C (2): ASIN
                        // D (3): Cost
                        // E (4): Clicks
                        // F (5): Impressions
                        // G (6): CPC
                        // H (7): CTR %
                        // I (8): ACOS %
                        // J (9): Conversions (30d)
                        // K (10): Units (30d)
                        // L (11): Sales (30d)

                        const countryRaw = row[0];
                        const weekRaw = row[1];
                        const asin = row[2];
                        const costRaw = row[3];
                        const clicksRaw = row[4];
                        const impressionsRaw = row[5];
                        const cpcRaw = row[6];
                        const ctrRaw = row[7];
                        const acosRaw = row[8];
                        const conversionsRaw = row[9];
                        const unitsRaw = row[10];
                        const salesRaw = row[11];

                        if (!asin || !countryRaw || weekRaw === undefined) continue;

                        const weekNum = parseInt(String(weekRaw));
                        if (isNaN(weekNum) || weekNum < 1 || weekNum > 53) continue;

                        // For CSV without sheet names, assume current year
                        const currentYear = new Date().getFullYear();

                        data.push({
                            country: mapCountryToMarketplace(String(countryRaw)),
                            year: currentYear,
                            week: weekNum,
                            asin: String(asin).trim(),
                            cost: parseCurrency(String(costRaw)),
                            clicks: parseUnits(String(clicksRaw)),
                            impressions: parseUnits(String(impressionsRaw)),
                            cpc: parseCurrency(String(cpcRaw)),
                            ctr: parseCurrency(String(ctrRaw)),
                            acos: parseCurrency(String(acosRaw)),
                            conversions: parseUnits(String(conversionsRaw)),
                            attributedUnits30d: parseUnits(String(unitsRaw)),
                            attributedSales30d: parseCurrency(String(salesRaw)),
                        });
                    }
                    resolve(data);
                } catch (err) {
                    reject(err);
                }
            },
            error: (error: any) => reject(error)
        });
    });
};

export const processAdsExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<AdsRecord[]> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });
        const allData: AdsRecord[] = [];

        // Process ALL sheets (e.g., "2025", "2026")
        for (const sheetName of workbook.SheetNames) {
            const year = parseInt(sheetName);
            if (isNaN(year) || year < 2020 || year > 2100) {
                console.warn(`Skipping sheet "${sheetName}" - not a valid year`);
                continue;
            }

            const worksheet = workbook.Sheets[sheetName];
            // Use header: 1 to get array of arrays (row-based)
            const jsonData: any[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "" });

            console.log(`Processing sheet ${sheetName}: ${jsonData.length} rows`);

            // Skip header row (index 0), process data rows
            for (let i = 1; i < jsonData.length; i++) {
                const row = jsonData[i];
                if (!row || row.length < 12) continue;

                // Column mapping for Ads Weekly.xlsx:
                // A (0): Country
                // B (1): Week
                // C (2): ASIN
                // D (3): Cost
                // E (4): Clicks
                // F (5): Impressions
                // G (6): CPC
                // H (7): CTR %
                // I (8): ACOS %
                // J (9): Conversions (30d)
                // K (10): Units (30d)
                // L (11): Sales (30d)

                const countryRaw = row[0];
                const weekRaw = row[1];
                const asin = row[2];
                const costRaw = row[3];
                const clicksRaw = row[4];
                const impressionsRaw = row[5];
                const cpcRaw = row[6];
                const ctrRaw = row[7];
                const acosRaw = row[8];
                const conversionsRaw = row[9];
                const unitsRaw = row[10];
                const salesRaw = row[11];

                // Skip if missing essential data
                if (!asin || !countryRaw || weekRaw === undefined || weekRaw === '') continue;

                const weekNum = parseInt(String(weekRaw));
                if (isNaN(weekNum) || weekNum < 1 || weekNum > 53) continue;

                allData.push({
                    country: mapCountryToMarketplace(String(countryRaw)),
                    year,
                    week: weekNum,
                    asin: String(asin).trim(),
                    cost: parseCurrency(String(costRaw)),
                    clicks: parseUnits(String(clicksRaw)),
                    impressions: parseUnits(String(impressionsRaw)),
                    cpc: parseCurrency(String(cpcRaw)),
                    ctr: parseCurrency(String(ctrRaw)),
                    acos: parseCurrency(String(acosRaw)),
                    conversions: parseUnits(String(conversionsRaw)),
                    attributedUnits30d: parseUnits(String(unitsRaw)),
                    attributedSales30d: parseCurrency(String(salesRaw)),
                });
            }
        }

        console.log(`Total Ads records loaded: ${allData.length}`);
        return allData;
    } catch (error) {
        console.error("Error processing Ads Excel:", error);
        throw error;
    }
};

// --- TRAFFIC DATA PARSING ---

export const processTrafficExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<TrafficRecord[]> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });
        const allData: TrafficRecord[] = [];

        // Process first sheet only (Traffic Weekly.xlsx typically has one sheet)
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData: any[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "" });

        console.log(`Processing Traffic sheet "${sheetName}": ${jsonData.length} rows`);

        // Column mapping for Traffic Weekly.xlsx:
        // A (0): Year
        // B (1): Week
        // C (2): ASIN
        // F (5): Country
        // G (6): Glance Views (GV)

        // Skip header row (index 0), process data rows
        for (let i = 1; i < jsonData.length; i++) {
            const row = jsonData[i];
            if (!row || row.length < 7) continue;

            const yearRaw = row[0];
            const weekRaw = row[1];
            const asin = row[2];
            const countryRaw = row[5];
            const gvRaw = row[6];

            // Skip if missing essential data
            if (!asin || yearRaw === undefined || weekRaw === undefined || !countryRaw) continue;

            const year = parseInt(String(yearRaw));
            const weekNum = parseInt(String(weekRaw));
            if (isNaN(year) || year < 2020 || year > 2100) continue;
            if (isNaN(weekNum) || weekNum < 1 || weekNum > 53) continue;

            allData.push({
                country: mapCountryToMarketplace(String(countryRaw)),
                year,
                week: weekNum,
                asin: String(asin).trim().toUpperCase(),
                glanceViews: parseUnits(String(gvRaw)),
            });
        }

        console.log(`Total Traffic records loaded: ${allData.length}`);
        return allData;
    } catch (error) {
        console.error("Error processing Traffic Excel:", error);
        throw error;
    }
};

// --- DATA MERGING ---

export const mergeSalesAndAdsData = (
    salesData: SalesRecord[],
    adsData: AdsRecord[],
    asinMetadataMap?: Map<string, { sku: string; title: string; line: string }>,
    trafficData?: TrafficRecord[],
    velocityMap?: Map<string, number>
): CombinedKPIs[] => {
    const stringCache: Record<string, string> = {};
    const getNorm = (s: string) => {
        if (!s) return '';
        if (stringCache[s]) return stringCache[s];
        const v = s.trim().toUpperCase();
        stringCache[s] = v;
        return v;
    };

    // Key for both sales and ads: ASIN|Customer|Year|Week
    const createKey = (asin: string, customer: string, year: number, week: number) =>
        `${getNorm(asin)}|${getNorm(customer)}|${year}|${week}`;

    // Build traffic lookup map - Use a more efficient key
    const trafficMap = new Map<string, number>();
    if (trafficData) {
        for (let i = 0; i < trafficData.length; i++) {
            const t = trafficData[i];
            const key = `${getNorm(t.asin)}|${getNorm(t.country)}|${t.year}|${t.week}`;
            trafficMap.set(key, (trafficMap.get(key) || 0) + (t.glanceViews || 0));
        }
    }

    // 1. Initialize metadata lookup map with provided global map if available, otherwise build from current sales
    const asinMetadata = asinMetadataMap || new Map<string, { sku: string; title: string; line: string }>();

    // 2. Aggregate Sales by ASIN|Customer|Year|Week (combine all SKUs)
    const salesMap = new Map<string, {
        sellOut: number;
        units: number;
        sku: string;
        title: string;
        line: string;
        asin: string;
        customer: string;
        year: number;
        week: number;
        month: string;
    }>();

    for (let i = 0; i < salesData.length; i++) {
        const sale = salesData[i];
        const weekNum = sale.week || 0;
        if (weekNum === 0) continue;

        const asinUpper = getNorm(sale.asin);
        const key = `${asinUpper}|${getNorm(sale.customer)}|${sale.year}|${weekNum}`;

        // If no global map provided, build it on the fly
        if (!asinMetadataMap) {
            const existingMeta = asinMetadata.get(asinUpper);
            if (!existingMeta || (sale.title && sale.title.length > (existingMeta.title?.length || 0))) {
                asinMetadata.set(asinUpper, { sku: sale.sku, title: sale.title, line: sale.line });
            }
        }

        const existing = salesMap.get(key);
        if (existing) {
            existing.sellOut += sale.sellOut;
            existing.units += sale.units;
            if (sale.title && sale.title.length > (existing.title?.length || 0)) {
                existing.title = sale.title;
            }
            if (sale.sku && !existing.sku) {
                existing.sku = sale.sku;
            }
        } else {
            salesMap.set(key, {
                sellOut: sale.sellOut,
                units: sale.units,
                sku: sale.sku,
                title: sale.title,
                line: sale.line,
                asin: sale.asin,
                customer: sale.customer,
                year: sale.year,
                week: weekNum,
                month: sale.month
            });
        }
    }

    // 3. Aggregate Ads by ASIN|Customer|Year|Week
    const adsMap = new Map<string, AdsRecord>();
    for (let i = 0; i < adsData.length; i++) {
        const ad = adsData[i];
        const key = `${getNorm(ad.asin)}|${getNorm(ad.country)}|${ad.year}|${ad.week}`;
        const existing = adsMap.get(key);
        if (existing) {
            existing.cost += ad.cost;
            existing.clicks += ad.clicks;
            existing.impressions += ad.impressions;
            existing.attributedSales30d += ad.attributedSales30d;
            existing.attributedUnits30d += ad.attributedUnits30d;
            existing.conversions += ad.conversions;
        } else {
            adsMap.set(key, { ...ad });
        }
    }

    const mergedData: CombinedKPIs[] = [];
    const processedKeys = new Set<string>();

    // 4. Create ONE record per ASIN/Customer/Year/Week from sales
    salesMap.forEach((sale, key) => {
        processedKeys.add(key);
        const ad = adsMap.get(key);

        const adCost = ad?.cost || 0;
        const adClicks = ad?.clicks || 0;
        const adImpressions = ad?.impressions || 0;
        const adSales = ad?.attributedSales30d || 0;
        const adUnits = ad?.attributedUnits30d || 0;

        const salesTotal = sale.sellOut;
        const unitsTotal = sale.units;
        const salesOrganic = Math.max(0, salesTotal - adSales);
        const unitsOrganic = Math.max(0, unitsTotal - adUnits);

        const acos = adSales > 0 ? (adCost / adSales) * 100 : 0;
        const tacos = salesTotal > 0 ? (adCost / salesTotal) * 100 : 0;
        const roas = adCost > 0 ? adSales / adCost : 0;
        const ctr = adImpressions > 0 ? (adClicks / adImpressions) * 100 : 0;
        const cpc = adClicks > 0 ? adCost / adClicks : 0;
        const cvrUnits = adClicks > 0 ? (adUnits / adClicks) * 100 : 0;
        const avgWeeklySales = velocityMap?.get(sale.asin.trim().toUpperCase()) || 0;

        mergedData.push({
            id: `merged-${key}`,
            marketplace: sale.customer,
            customer: sale.customer,
            month: sale.month,
            week: sale.week,
            year: sale.year,
            asin: getNorm(sale.asin),
            title: sale.title,
            line: sale.line,
            sku: sale.sku,
            salesTotal,
            unitsTotal,
            salesAds: adSales,
            unitsAds: adUnits,
            cost: adCost,
            clicks: adClicks,
            impressions: adImpressions,
            conversions: ad?.conversions || 0,
            salesOrganic,
            unitsOrganic,
            paidSalesShare: salesTotal > 0 ? (adSales / salesTotal) * 100 : 0,
            organicSalesShare: salesTotal > 0 ? (salesOrganic / salesTotal) * 100 : 0,
            acos,
            tacos,
            roas,
            ctr,
            cpc,
            cvrUnits,
            glanceViews: trafficMap.get(key) || 0,
            avgWeeklySales
        });
    });

    // 5. Add ads-only records - use provided metadata for line/title
    adsMap.forEach((ad, key) => {
        if (!processedKeys.has(key)) {
            const asin = getNorm(ad.asin);
            const meta = asinMetadata.get(asin);
            const avgWeeklySales = velocityMap?.get(asin) || 0;

            mergedData.push({
                id: `ads-only-${key}`,
                marketplace: ad.country,
                customer: ad.country,
                month: 'N/A',
                week: ad.week,
                year: ad.year,
                asin: asin,
                title: meta?.title || ad.asin,
                line: meta?.line || 'Unassigned',
                sku: meta?.sku || '',
                salesTotal: 0,
                unitsTotal: 0,
                salesAds: ad.attributedSales30d,
                unitsAds: ad.attributedUnits30d,
                cost: ad.cost,
                clicks: ad.clicks,
                impressions: ad.impressions,
                conversions: ad.conversions,
                salesOrganic: 0,
                unitsOrganic: 0,
                paidSalesShare: 0,
                organicSalesShare: 0,
                acos: ad.attributedSales30d > 0 ? (ad.cost / ad.attributedSales30d) * 100 : 0,
                tacos: 0,
                roas: ad.cost > 0 ? ad.attributedSales30d / ad.cost : 0,
                ctr: ad.impressions > 0 ? (ad.clicks / ad.impressions) * 100 : 0,
                cpc: ad.clicks > 0 ? ad.cost / ad.clicks : 0,
                cvrUnits: ad.clicks > 0 ? (ad.attributedUnits30d / ad.clicks) * 100 : 0,
                glanceViews: trafficMap.get(key) || 0,
                avgWeeklySales
            });
        }
    });

    return mergedData;
};



// --- EXISTING HELPERS ---

// Helper to check stock filter
const checkStockFilter = (sku: string, filters: string[], stockMap?: Map<string, number>): boolean => {
    if (!filters || !Array.isArray(filters) || filters.length === 0) return true;
    if (!stockMap) return true;

    // Normalize SKU (remove DE/EN) to match stock map
    const baseSku = sku?.replace(/(DE|EN)$/i, '');
    const stockValue = stockMap.get(baseSku) || 0;

    return filters.some(f => {
        // Smart Filters
        if (f === 'Out of Stock (0)') return stockValue === 0;
        if (f === 'In Stock (>0)') return stockValue > 0;
        if (f === 'Low Stock (<10)') return stockValue < 10;

        // Numeric Range (e.g. ">10", "1-50")
        const input = f.trim().toLowerCase();
        if (input.includes('-')) {
            const [start, end] = input.split('-').map(s => parseFloat(s.trim()));
            if (!isNaN(start) && !isNaN(end)) return stockValue >= start && stockValue <= end;
        } else if (input.startsWith('<=')) {
            const val = parseFloat(input.substring(2).trim());
            if (!isNaN(val)) return stockValue <= val;
        } else if (input.startsWith('>=')) {
            const val = parseFloat(input.substring(2).trim());
            if (!isNaN(val)) return stockValue >= val;
        } else if (input.startsWith('<')) {
            const val = parseFloat(input.substring(1).trim());
            if (!isNaN(val)) return stockValue < val;
        } else if (input.startsWith('>')) {
            const val = parseFloat(input.substring(1).trim());
            if (!isNaN(val)) return stockValue > val;
        }

        // Exact Match
        return stockValue.toString() === f;
    });
};

const checkVendorStockFilter = (asin: string, filters: string[], vendorStockMap?: Map<string, { eu: number; uk: number }>, mode: 'eu' | 'uk' = 'eu'): boolean => {
    if (!filters || !Array.isArray(filters) || filters.length === 0) return true;
    if (!vendorStockMap) return true;

    const data = vendorStockMap.get(asin);
    const stockValue = data ? (mode === 'uk' ? data.uk : data.eu) : 0;

    return filters.some(f => {
        if (f === 'Out of Stock (0)') return stockValue === 0;
        if (f === 'In Stock (>20)') return stockValue > 20;
        if (f === 'In Stock (>0)') return stockValue > 0;
        if (f === 'Low Stock (<10)') return stockValue < 10;

        const input = f.trim().toLowerCase();
        if (input.includes('-')) {
            const [start, end] = input.split('-').map(s => parseFloat(s.trim()));
            if (!isNaN(start) && !isNaN(end)) return stockValue >= start && stockValue <= end;
        } else if (input.startsWith('<=')) {
            const val = parseFloat(input.substring(2).trim());
            if (!isNaN(val)) return stockValue <= val;
        } else if (input.startsWith('>=')) {
            const val = parseFloat(input.substring(2).trim());
            if (!isNaN(val)) return stockValue >= val;
        } else if (input.startsWith('<')) {
            const val = parseFloat(input.substring(1).trim());
            if (!isNaN(val)) return stockValue < val;
        } else if (input.startsWith('>')) {
            const val = parseFloat(input.substring(1).trim());
            if (!isNaN(val)) return stockValue > val;
        } else if (!isNaN(parseFloat(input))) {
            return stockValue === parseFloat(input);
        }
        return false;
    });
};

// Filter Ads Data by Country, Year, Week, ASIN, SKU, and Product Line
export const filterAdsData = (
    adsData: AdsRecord[],
    filters: FilterState,
    asinMetadata?: Map<string, { sku: string; line: string }>,
    stockMap?: Map<string, number>,
    vendorStockMap?: Map<string, { eu: number; uk: number }>,
    top50Mode: 'eu' | 'uk' = 'eu'
): AdsRecord[] => {
    return adsData.filter(ad => {
        const asin = ad.asin.trim().toUpperCase();
        const meta = asinMetadata?.get(asin);

        // Country/Customer match (ads use 'country', sales use 'customer')
        const countryMatch = filters.customer.length === 0
            ? PAN_EU_COUNTRIES.some(c => c.toUpperCase() === ad.country.toUpperCase())
            : filters.customer.some(c => c.toUpperCase() === ad.country.toUpperCase());

        // Year match
        const yearMatch = filters.year.length === 0 ||
            filters.year.includes(ad.year.toString());

        // Week match (filters use "W1", "W2" format)
        const weekStr = `W${ad.week}`;
        const weekMatch = filters.week.length === 0 || filters.week.includes(weekStr);

        // ASIN match
        const asinMatch = filters.asin.length === 0 ||
            filters.asin.some(a => a.toUpperCase() === asin);

        // SKU match (Requires metadata)
        let skuMatch = true;
        if (filters.sku.length > 0) {
            skuMatch = meta ? filters.sku.some(s => s.toUpperCase() === meta.sku.toUpperCase()) : false;
        }

        // Line match (Requires metadata)
        let lineMatch = true;
        if (filters.line.length > 0) {
            lineMatch = meta ? filters.line.includes(meta.line) : false;
        }

        // Stock match
        const stockMatch = checkStockFilter(meta?.sku || '', filters.stock, stockMap);
        const vendorStockMatch = checkVendorStockFilter(asin, filters.vendorStock, vendorStockMap, top50Mode);

        return countryMatch && yearMatch && weekMatch && asinMatch && skuMatch && lineMatch && stockMatch && vendorStockMatch;
    });
};

export const filterData = (
    data: SalesRecord[],
    filters: FilterState,
    stockMap?: Map<string, number>,
    vendorStockMap?: Map<string, { eu: number; uk: number }>,
    top50Mode: 'eu' | 'uk' = 'eu'
): SalesRecord[] => {
    return data.filter(item => {
        // 1. Month Logic: Handle "Apr-23" matching "Apr" filter
        const recordMonth = item.month; // e.g. "Apr-23"
        const pureMonth = recordMonth.split('-')[0]; // "Apr"

        // 2. Filter Checks
        const customerMatch = filters.customer.length === 0
            ? PAN_EU_COUNTRIES.includes(item.customer)
            : filters.customer.includes(item.customer);
        const yearMatch = filters.year.length === 0 || filters.year.includes(item.year.toString());

        // Check match against pure month ("Apr") OR full month ("Apr-23") just in case filters evolve
        const monthMatch = filters.month.length === 0 || filters.month.includes(pureMonth) || filters.month.includes(recordMonth);

        const lineMatch = filters.line.length === 0 || filters.line.includes(item.line);
        const asinMatch = filters.asin.length === 0 || filters.asin.includes(item.asin);
        const skuMatch = filters.sku.length === 0 || filters.sku.includes(item.sku);
        const titleMatch = filters.title.length === 0 || filters.title.includes(item.title);

        // Week Logic: Match "W1", "W2" etc.
        // item.week is a number (e.g. 1), filter uses strings "W1"
        const weekStr = item.week ? `W${item.week}` : '';
        const weekMatch = filters.week.length === 0 || (weekStr !== '' && filters.week.includes(weekStr));

        // Stock match
        const stockMatch = checkStockFilter(item.sku, filters.stock, stockMap);
        const vendorStockMatch = checkVendorStockFilter(item.asin, filters.vendorStock, vendorStockMap, top50Mode);

        return customerMatch && yearMatch && monthMatch && lineMatch && asinMatch && skuMatch && titleMatch && weekMatch && stockMatch && vendorStockMatch;
    });
};

const calculateSeasonality = (data: SalesRecord[]): { seasonality: SeasonalityPoint[], seasonalityUnits: SeasonalityPoint[], years: string[] } => {
    const seasonalityMap = new Map<string, SeasonalityPoint>();
    const seasonalityUnitsMap = new Map<string, SeasonalityPoint>();
    const yearsSet = new Set<string>();

    // Initialize all months
    MONTH_ORDER.forEach(m => {
        seasonalityMap.set(m, { name: m });
        seasonalityUnitsMap.set(m, { name: m });
    });

    data.forEach(record => {
        const monthName = record.month;
        // Extract year from record.month if it's in Format "Mon-YY", else use record.year
        // record.year is numeric, record.month is "Apr-23".
        const yearStr = record.year.toString();
        yearsSet.add(yearStr);

        // We need to match month name purely (Jan, Feb) for the X Axis, ignoring year
        const pureMonth = monthName.split('-')[0];

        if (seasonalityMap.has(pureMonth)) {
            // Sell Out
            const entrySO = seasonalityMap.get(pureMonth)!;
            const currentValSO = (entrySO[yearStr] as number) || 0;
            entrySO[yearStr] = currentValSO + record.sellOut;

            // Units
            const entryUnits = seasonalityUnitsMap.get(pureMonth)!;
            const currentValUnits = (entryUnits[yearStr] as number) || 0;
            entryUnits[yearStr] = currentValUnits + record.units;
        }
    });

    const seasonality = Array.from(seasonalityMap.values());
    const seasonalityUnits = Array.from(seasonalityUnitsMap.values());
    const years = Array.from(yearsSet).sort();

    return { seasonality, seasonalityUnits, years };
};

const calculateTopLinesSplit = (data: SalesRecord[]): YearlySplitData[] => {
    // 1. Identify Lines by Sell Out (Sort desc)
    const lineTotals = new Map<string, number>();
    data.forEach(item => {
        lineTotals.set(item.line, (lineTotals.get(item.line) || 0) + item.sellOut);
    });

    // Return ALL lines
    const topLines = Array.from(lineTotals.entries())
        .sort((a, b) => b[1] - a[1])
        .map(([line]) => line);

    // 2. Aggregate data by Year
    const resultMap = new Map<string, YearlySplitData>();

    topLines.forEach(line => {
        resultMap.set(line, { name: line });
    });

    data.forEach(item => {
        if (resultMap.has(item.line)) {
            const entry = resultMap.get(item.line)!;
            const keyVal = `${item.year}_value`;
            const keyUnits = `${item.year}_units`;

            entry[keyVal] = ((entry[keyVal] as number) || 0) + item.sellOut;
            entry[keyUnits] = ((entry[keyUnits] as number) || 0) + item.units;
        }
    });

    return Array.from(resultMap.values());
};

const calculateGenericSplit = (data: SalesRecord[], groupField: keyof SalesRecord, valueField: 'sellOut' | 'units', limit?: number): YearlySplitData[] => {
    const totals = new Map<string, number>();
    data.forEach(item => {
        const key = String(item[groupField]);
        totals.set(key, (totals.get(key) || 0) + item[valueField]);
    });

    let sortedKeys = Array.from(totals.entries()).sort((a, b) => b[1] - a[1]).map(e => e[0]);
    if (limit) sortedKeys = sortedKeys.slice(0, limit);
    const keySet = new Set(sortedKeys);

    const resultMap = new Map<string, YearlySplitData>();
    sortedKeys.forEach(k => resultMap.set(k, { name: k }));

    data.forEach(item => {
        const key = String(item[groupField]);
        if (keySet.has(key)) {
            const entry = resultMap.get(key)!;
            const yearKey = item.year.toString();
            entry[yearKey] = ((entry[yearKey] as number) || 0) + item[valueField];
        }
    });

    return Array.from(resultMap.values());
};

// Renamed from calculateMovers
export const calculateLineMovers = (data: SalesRecord[]): { topMovers: LineGrowthMetric[], bottomMovers: LineGrowthMetric[], comparisonPeriods: { current: string, previous: string } } => {
    const lineYearMap = new Map<string, Map<number, { sellOut: number; units: number }>>();
    const allYears = new Set<number>();

    data.forEach(item => {
        if (!lineYearMap.has(item.line)) {
            lineYearMap.set(item.line, new Map());
        }
        const yearMap = lineYearMap.get(item.line)!;
        const current = yearMap.get(item.year) || { sellOut: 0, units: 0 };
        yearMap.set(item.year, {
            sellOut: current.sellOut + item.sellOut,
            units: current.units + item.units
        });
        allYears.add(item.year);
    });

    const sortedYears = Array.from(allYears).sort((a, b) => b - a);

    if (sortedYears.length < 2) {
        return { topMovers: [], bottomMovers: [], comparisonPeriods: { current: 'N/A', previous: 'N/A' } };
    }

    const currentYear = sortedYears[0];
    const prevYear = sortedYears[1];

    const metrics: LineGrowthMetric[] = [];

    lineYearMap.forEach((yearMap, line) => {
        const currData = yearMap.get(currentYear) || { sellOut: 0, units: 0 };
        const prevData = yearMap.get(prevYear) || { sellOut: 0, units: 0 };

        // Sell Out Growth
        let sellOutGrowthValue = 0;
        let sellOutGrowthPercentage = 0;
        if (prevData.sellOut > 0) {
            sellOutGrowthValue = currData.sellOut - prevData.sellOut;
            sellOutGrowthPercentage = (sellOutGrowthValue / prevData.sellOut) * 100;
        } else if (currData.sellOut > 0) {
            sellOutGrowthValue = currData.sellOut;
            sellOutGrowthPercentage = 100;
        } else if (currData.sellOut === 0 && prevData.sellOut > 0) {
            sellOutGrowthValue = -prevData.sellOut;
            sellOutGrowthPercentage = -100;
        }

        // Unit Growth
        let unitsGrowthValue = 0;
        let unitsGrowthPercentage = 0;
        if (prevData.units > 0) {
            unitsGrowthValue = currData.units - prevData.units;
            unitsGrowthPercentage = (unitsGrowthValue / prevData.units) * 100;
        } else if (currData.units > 0) {
            unitsGrowthValue = currData.units;
            unitsGrowthPercentage = 100;
        } else if (currData.units === 0 && prevData.units > 0) {
            unitsGrowthValue = -prevData.units;
            unitsGrowthPercentage = -100;
        }

        if (currData.sellOut > 0 || prevData.sellOut > 0) {
            metrics.push({
                line,
                currentYearSellOut: currData.sellOut,
                previousYearSellOut: prevData.sellOut,
                sellOutGrowthValue,
                sellOutGrowthPercentage,
                currentYearUnits: currData.units,
                previousYearUnits: prevData.units,
                unitsGrowthValue,
                unitsGrowthPercentage
            });
        }
    });

    const topMovers = metrics
        .filter(m => m.sellOutGrowthValue > 0)
        .sort((a, b) => b.sellOutGrowthValue - a.sellOutGrowthValue);

    const bottomMovers = metrics
        .filter(m => m.sellOutGrowthValue < 0)
        .sort((a, b) => a.sellOutGrowthValue - b.sellOutGrowthValue);

    return {
        topMovers,
        bottomMovers,
        comparisonPeriods: { current: currentYear.toString(), previous: prevYear.toString() }
    };
};


const createItemKey = (record: SalesRecord) => {
    // A robust key combining all identifiers
    return `${record.sku || 'NO_SKU'}||${record.asin || 'NO_ASIN'}||${record.title || 'NO_TITLE'}`;
}

export const calculateItemMovers = (
    currentFilteredData: SalesRecord[],
    selectedCustomerFromPage: string | null,
    currentComparisonYearFromPage: number | null
): { topMovers: ItemGrowthMetric[], bottomMovers: ItemGrowthMetric[], comparisonPeriods: { current: string, previous: string } } => {

    let dataToProcess = currentFilteredData;

    // Apply customer filter if selected on the Top Movers page
    if (selectedCustomerFromPage) {
        dataToProcess = dataToProcess.filter(item => item.customer === selectedCustomerFromPage);
    }

    if (dataToProcess.length === 0) {
        return { topMovers: [], bottomMovers: [], comparisonPeriods: { current: 'N/A', previous: 'N/A' } };
    }

    // Map to store item data aggregated by year
    const itemYearMap = new Map<string, Map<number, { sellOut: number; units: number, sku: string, asin: string, title: string, line: string }>>();
    const allYearsInFilteredData = new Set<number>();

    dataToProcess.forEach(item => {
        const itemKey = createItemKey(item);
        if (!itemYearMap.has(itemKey)) {
            itemYearMap.set(itemKey, new Map());
        }
        const yearMap = itemYearMap.get(itemKey)!;
        const current = yearMap.get(item.year) || { sellOut: 0, units: 0, sku: item.sku, asin: item.asin, title: item.title, line: item.line };
        yearMap.set(item.year, {
            sellOut: current.sellOut + item.sellOut,
            units: current.units + item.units,
            sku: item.sku,
            asin: item.asin,
            title: item.title,
            line: item.line
        });
        allYearsInFilteredData.add(item.year);
    });

    const sortedYearsInFilteredData = Array.from(allYearsInFilteredData).sort((a, b) => b - a); // Descending (most recent first)

    let currentYear: number;
    let prevYear: number;

    if (currentComparisonYearFromPage) {
        // If a specific comparison year is provided by the user on the Top Movers page
        currentYear = currentComparisonYearFromPage;
        const currentYearIndex = sortedYearsInFilteredData.indexOf(currentYear);
        if (currentYearIndex === -1 || currentYearIndex === sortedYearsInFilteredData.length - 1) {
            // Specified year not found in filtered data or it's the oldest year (no previous year for comparison)
            return { topMovers: [], bottomMovers: [], comparisonPeriods: { current: currentYear.toString(), previous: 'N/A' } };
        }
        prevYear = sortedYearsInFilteredData[currentYearIndex + 1]; // The year directly before the currentComparisonYear
    } else {
        // Default to the two most recent years from the *filtered data* if no specific year is chosen
        if (sortedYearsInFilteredData.length < 2) {
            return { topMovers: [], bottomMovers: [], comparisonPeriods: { current: 'N/A', previous: 'N/A' } };
        }
        currentYear = sortedYearsInFilteredData[0]; // Most recent
        prevYear = sortedYearsInFilteredData[1];   // Second most recent
    }

    const metrics: ItemGrowthMetric[] = [];

    itemYearMap.forEach((yearMap) => {
        const currData = yearMap.get(currentYear) || { sellOut: 0, units: 0, sku: '', asin: '', title: '', line: '' };
        const prevData = yearMap.get(prevYear) || { sellOut: 0, units: 0, sku: '', asin: '', title: '', line: '' };

        // Only include items that had some activity in at least one of the comparison years
        if ((currData.sellOut === 0 && currData.units === 0) && (prevData.sellOut === 0 && prevData.units === 0)) {
            return;
        }

        // Use metadata from current year, if not available use previous (for sku/asin/title/line)
        const itemMeta = currData.sku ? currData : prevData;


        // Sell Out Growth
        let sellOutGrowthValue = currData.sellOut - prevData.sellOut;
        let sellOutGrowthPercentage = 0;
        if (prevData.sellOut !== 0) {
            sellOutGrowthPercentage = (sellOutGrowthValue / prevData.sellOut) * 100;
        } else if (currData.sellOut > 0) {
            sellOutGrowthPercentage = 100; // Growth from zero
        } else if (currData.sellOut === 0 && prevData.sellOut > 0) {
            sellOutGrowthPercentage = -100; // Decline to zero
        }

        // Unit Growth
        let unitsGrowthValue = currData.units - prevData.units;
        let unitsGrowthPercentage = 0;
        if (prevData.units !== 0) {
            unitsGrowthPercentage = (unitsGrowthValue / prevData.units) * 100;
        } else if (currData.units > 0) {
            unitsGrowthPercentage = 100; // Growth from zero
        } else if (currData.units === 0 && prevData.units > 0) {
            unitsGrowthPercentage = -100; // Decline to zero
        }

        metrics.push({
            sku: itemMeta.sku,
            asin: itemMeta.asin,
            title: itemMeta.title,
            line: itemMeta.line,
            currentYearSellOut: currData.sellOut,
            previousYearSellOut: prevData.sellOut,
            sellOutGrowthValue,
            sellOutGrowthPercentage,
            currentYearUnits: currData.units,
            previousYearUnits: prevData.units,
            unitsGrowthValue,
            unitsGrowthPercentage
        });
    });

    const topMovers = metrics
        .sort((a, b) => b.unitsGrowthValue - a.unitsGrowthValue) // Sort by unitsGrowthValue
        .slice(0, 20); // Top 20 Gainers

    const bottomMovers = metrics
        .sort((a, b) => a.unitsGrowthValue - b.unitsGrowthValue) // Sort by unitsGrowthValue
        .slice(0, 20); // Top 20 Losers

    return {
        topMovers,
        bottomMovers,
        comparisonPeriods: { current: currentYear.toString(), previous: prevYear.toString() }
    };
};


export const aggregateData = (data: SalesRecord[]): AggregatedData => {
    const totalSellOut = data.reduce((acc, curr) => acc + curr.sellOut, 0);
    const totalUnits = data.reduce((acc, curr) => acc + curr.units, 0);

    const totalsByYear: Record<string, { sellOut: number; units: number }> = {};
    data.forEach(item => {
        const y = item.year.toString();
        if (!totalsByYear[y]) totalsByYear[y] = { sellOut: 0, units: 0 };
        totalsByYear[y].sellOut += item.sellOut;
        totalsByYear[y].units += item.units;
    });

    const lineMap = new Map<string, { value: number; units: number }>();
    data.forEach(item => {
        const current = lineMap.get(item.line) || { value: 0, units: 0 };
        lineMap.set(item.line, {
            value: current.value + item.sellOut,
            units: current.units + item.units
        });
    });
    const byLine = Array.from(lineMap.entries())
        .map(([name, data]) => ({ name, value: data.value, units: data.units }))
        .sort((a, b) => b.value - a.value);

    const customerMap = new Map<string, number>();
    data.forEach(item => {
        customerMap.set(item.customer, (customerMap.get(item.customer) || 0) + item.sellOut);
    });
    const byCustomer = Array.from(customerMap.entries())
        .map(([name, value]) => ({ name, value }))
        .sort((a, b) => b.value - a.value);

    const { seasonality, seasonalityUnits, years } = calculateSeasonality(data);
    const { topMovers, bottomMovers, comparisonPeriods } = calculateLineMovers(data); // Use calculateLineMovers
    const topLinesSplit = calculateTopLinesSplit(data);
    const byCustomerSplit = calculateGenericSplit(data, 'customer', 'sellOut');
    const byLineOverviewSplit = calculateGenericSplit(data, 'line', 'units', 10);

    return {
        totalSellOut,
        totalUnits,
        totalsByYear,
        byLine,
        byCustomer,
        seasonality,
        seasonalityUnits,
        availableYears: years,
        topMovers,
        bottomMovers,
        comparisonPeriods,
        topLinesSplit,
        byCustomerSplit,
        byLineOverviewSplit
    };
};

/**
 * Groups Pan-EU countries (Amazon DE, IT, FR, ES) into a single "Pan-EU" customer
 * when no customer filter is applied. This provides a consolidated view of European
 * markets while keeping UK and SC separate.
 * 
 * @param data - Array of sales records
 * @param hasCustomerFilter - Whether a customer filter is currently applied
 * @returns Processed data with Pan-EU grouping applied if appropriate
 */
export const applyPanEUGrouping = (
    data: SalesRecord[],
    hasCustomerFilter: boolean
): SalesRecord[] => {
    // If customer filter is applied, don't group - show selected countries as-is
    if (hasCustomerFilter) {
        return data;
    }

    // Replace Pan-EU country names with "Pan-EU" for grouping
    return data.map(record => {
        if (PAN_EU_COUNTRIES.includes(record.customer)) {
            return { ...record, customer: 'Pan-EU' };
        }
        return record;
    });
};

export const getUniqueValues = (data: SalesRecord[], field: keyof SalesRecord): string[] => {
    const values = new Set(data.map(item => String(item[field])));
    return Array.from(values).sort();
};

export const pivotSalesData = (data: any[], dimensions: string[] = ['title', 'customer', 'line', 'sku']): { rows: PivotRow[], years: string[] } => {
    // 1. Determine all years present in the data for columns
    const yearsSet = new Set(data.map(d => d.year));
    const years = Array.from(yearsSet).sort((a, b) => b - a).map(String);

    const map = new Map<string, PivotRow>();

    data.forEach(record => {
        // Group by Dynamic Dimensions
        // Use a fallback for 'customer' dimension as some records use 'marketplace'
        const keyParts = new Array(dimensions.length);
        for (let i = 0; i < dimensions.length; i++) {
            const dim = dimensions[i];
            if (dim === 'customer') keyParts[i] = String(record.customer || record.marketplace || '');
            else keyParts[i] = String(record[dim] || '');
        }
        const key = keyParts.join('||');

        if (!map.has(key)) {
            map.set(key, {
                id: key,
                customer: record.customer || record.marketplace || '',
                line: record.line || '',
                title: record.title || '',
                articleName: record.articleName || '',
                sku: record.sku || '',
                asin: record.asin || '',
                // Initialize 12 months with empty year maps
                months: Array(12).fill(null).map((_, i) => ({
                    monthIndex: i,
                    byYear: {}
                })),
                totalsByYear: {},
                adsByYear: {}
            });
        }

        const row = map.get(key)!;
        const monthRaw = record.month || '';
        const monthPart = monthRaw.split('-')[0]; // Handle "Apr-23" -> "Apr"
        const monthIdx = MONTH_ORDER.indexOf(monthPart);
        const yearStr = record.year.toString();

        // 1. Update Row Totals for Year
        if (!row.totalsByYear[yearStr]) {
            row.totalsByYear[yearStr] = { sellOut: 0, units: 0 };
        }
        row.totalsByYear[yearStr].sellOut += (record.sellOut || record.salesTotal || 0);
        row.totalsByYear[yearStr].units += (record.units || record.unitsTotal || 0);

        // 2. Update Ads Data (if present in the record)
        if (record.cost !== undefined || record.salesAds !== undefined) {
            if (!row.adsByYear) row.adsByYear = {};
            if (!row.adsByYear[yearStr]) {
                row.adsByYear[yearStr] = { adSpend: 0, attributedSales: 0, acos: 0, tacos: 0 };
            }
            row.adsByYear[yearStr].adSpend += (record.cost || 0);
            row.adsByYear[yearStr].attributedSales += (record.salesAds || 0);

            // Recalculate ACOS/TACOS at the aggregated level
            const ads = row.adsByYear[yearStr];
            const sales = row.totalsByYear[yearStr].sellOut;
            ads.acos = ads.attributedSales > 0 ? (ads.adSpend / ads.attributedSales) * 100 : 0;
            ads.tacos = sales > 0 ? (ads.adSpend / sales) * 100 : 0;
        }

        // 3. Update Monthly Data
        if (monthIdx !== -1) {
            const m = row.months[monthIdx];
            if (!m.byYear[yearStr]) {
                m.byYear[yearStr] = { sellOut: 0, units: 0 };
            }
            m.byYear[yearStr].sellOut += (record.sellOut || record.salesTotal || 0);
            m.byYear[yearStr].units += (record.units || record.unitsTotal || 0);
        }
    });

    return {
        rows: Array.from(map.values()),
        years
    };
};

export const generateXLSX = (rows: PivotRow[], dimensions: string[], years: string[]) => {
    // Flatten PivotRows into Excel-friendly objects
    const flatData = rows.map(row => {
        const flatRow: any = {};

        // Add Dimension Columns
        dimensions.forEach(dim => {
            let header = dim;
            if (dim === 'line') header = 'Product Line';
            if (dim === 'title') header = 'Title';
            if (dim === 'customer') header = 'Customer';
            if (dim === 'sku' || dim === 'SKU') header = 'SKU';
            if (dim === 'asin' || dim === 'ASIN') header = 'ASIN';

            flatRow[header] = row[dim as keyof PivotRow];
        });

        // Add Yearly Totals
        years.forEach(year => {
            const data = row.totalsByYear[year];
            flatRow[`Total Sell Out ${year}`] = data?.sellOut || 0;
            flatRow[`Total Units ${year}`] = data?.units || 0;
        });

        // Add Monthly Data
        row.months.forEach(m => {
            const monthName = MONTH_ORDER[m.monthIndex];
            years.forEach(year => {
                const data = m.byYear[year];
                flatRow[`${monthName} ${year} Sell Out`] = data?.sellOut || 0;
                flatRow[`${monthName} ${year} Units`] = data?.units || 0;
            });
        });

        return flatRow;
    });

    const ws = XLSX.utils.json_to_sheet(flatData);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, 'Business Data');
    XLSX.writeFile(wb, `Business_Data_Export_${new Date().toISOString().slice(0, 10)}.xlsx`);
};

export const generateItemMoversXLSX = (
    data: ItemGrowthMetric[],
    periods: { current: string; previous: string },
    type: 'Gainers' | 'Losers'
) => {
    const flatData = data.map(item => ({
        SKU: item.sku || '-',
        ASIN: item.asin || '-',
        'Product Title': item.title || '-',
        'Product Line': item.line || '-',
        [`Sell Out ${periods.previous}`]: item.previousYearSellOut,
        [`Sell Out ${periods.current}`]: item.currentYearSellOut,
        'SO Diff': item.sellOutGrowthValue,
        'SO Growth %': Number(item.sellOutGrowthPercentage.toFixed(2)),
        [`Units ${periods.previous}`]: item.previousYearUnits,
        [`Units ${periods.current}`]: item.currentYearUnits,
        'Units Diff': item.unitsGrowthValue,
        'Units Growth %': Number(item.unitsGrowthPercentage.toFixed(2)),
    }));

    const ws = XLSX.utils.json_to_sheet(flatData);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, type);
    XLSX.writeFile(wb, `${type}_${periods.current}_vs_${periods.previous}_${new Date().toISOString().split('T')[0]}.xlsx`);
};


export const aggregateForTimeSeries = (data: SalesRecord[]): TimeSeriesData[] => {
    const map = new Map<string, { sellOut: number; units: number }>();
    const recordsWithWeek = data.filter(r => r.week != null && r.year != null && r.week >= 1 && r.week <= 53);

    if (recordsWithWeek.length === 0) return []; // No weekly data to process

    recordsWithWeek.forEach(record => {
        // Create a sortable key YYYY-WW
        const weekStr = record.week!.toString().padStart(2, '0');
        const key = `${record.year}-${weekStr}`;

        const current = map.get(key) || { sellOut: 0, units: 0 };
        // Support both SalesRecord (sellOut/units) and CombinedKPIs (salesTotal/unitsTotal)
        current.sellOut += (record as any).sellOut ?? (record as any).salesTotal ?? 0;
        current.units += (record as any).units ?? (record as any).unitsTotal ?? 0;
        map.set(key, current);
    });

    // Convert map to array and sort chronologically
    return Array.from(map.entries())
        .sort((a, b) => a[0].localeCompare(b[0]))
        .map(([key, values]) => {
            const [year, weekNum] = key.split('-');
            const yearShort = year.substring(2);

            return {
                name: `W${weekNum} '${yearShort}`,
                sellOut: values.sellOut,
                units: values.units
            };
        });
};

export const aggregateForComparisonTimeSeries = (data: SalesRecord[]): ComparisonTimeSeriesPoint[] => {
    const map = new Map<number, { [key: string]: number }>(); // Key is week number
    const years = Array.from(new Set(data.map(d => d.year)));

    // Initialize map for all 53 possible weeks to ensure a consistent X-axis
    for (let i = 1; i <= 53; i++) {
        const initialWeekData: { [key: string]: number } = {};
        years.forEach(year => {
            initialWeekData[`${year}_sellOut`] = 0;
            initialWeekData[`${year}_units`] = 0;
        });
        map.set(i, initialWeekData);
    }

    data.forEach(record => {
        if (record.week != null && record.year != null && record.week >= 1 && record.week <= 53) {
            const weekData = map.get(record.week)!;

            const sellOutKey = `${record.year}_sellOut`;
            const unitsKey = `${record.year}_units`;

            // Support both SalesRecord (sellOut/units) and CombinedKPIs (salesTotal/unitsTotal)
            const sellOut = (record as any).sellOut ?? (record as any).salesTotal ?? 0;
            const units = (record as any).units ?? (record as any).unitsTotal ?? 0;

            weekData[sellOutKey] = (weekData[sellOutKey] || 0) + sellOut;
            weekData[unitsKey] = (weekData[unitsKey] || 0) + units;

            map.set(record.week, weekData);
        }
    });

    // Convert map to array, filter out weeks with no data across all years, and sort
    return Array.from(map.entries())
        .map(([week, values]) => ({
            week,
            name: `W${week}`,
            ...values,
        }))
        .filter(d => {
            // Check if there is any non-zero value for this week
            return Object.values(d).some(val => typeof val === 'number' && val > 0);
        })
        .sort((a, b) => a.week - b.week);
};
export interface WeeklyPivotRow {
    id: string;
    sku: string;
    title: string;
    asin: string;
    line: string;
    customer: string;
    unitsByWeek: { [weekKey: string]: number }; // Key: "YYYY-WW"
    spendByWeek: { [weekKey: string]: number }; // Key: "YYYY-WW"
    gvByWeek: { [weekKey: string]: number }; // Key: "YYYY-WW"
}

export const pivotWeeklySalesData = (data: CombinedKPIs[]): {
    rows: WeeklyPivotRow[],
    weeks: string[]
} => {
    const weekKeysSet = new Set<string>();
    const map = new Map<string, WeeklyPivotRow>();

    // Cache week keys to avoid repeated string formatting
    // Key: year|week, Value: YYYY-WW
    const weekCache = new Map<string, string>();

    const getWeekKey = (year: number, week: number) => {
        const cacheKey = `${year}|${week}`;
        let k = weekCache.get(cacheKey);
        if (!k) {
            k = `${year}-${String(week).padStart(2, '0')}`;
            weekCache.set(cacheKey, k);
        }
        return k;
    };

    const len = data.length;
    for (let i = 0; i < len; i++) {
        const record = data[i];
        if (!record.week) continue;

        const weekKey = getWeekKey(record.year, record.week);
        weekKeysSet.add(weekKey);

        const key = record.asin || record.sku || `${record.title}-${record.line}`;
        if (!key) continue;

        let row = map.get(key);
        if (!row) {
            row = {
                id: key,
                sku: record.sku || '',
                title: record.title || '',
                asin: record.asin || '',
                line: record.line || '',
                customer: record.customer || record.marketplace || '',
                unitsByWeek: {},
                spendByWeek: {},
                gvByWeek: {}
            };
            map.set(key, row);
        }

        row.unitsByWeek[weekKey] = (row.unitsByWeek[weekKey] || 0) + (record.unitsTotal || 0);
        row.spendByWeek[weekKey] = (row.spendByWeek[weekKey] || 0) + (record.cost || 0);
        row.gvByWeek[weekKey] = (row.gvByWeek[weekKey] || 0) + (record.glanceViews || 0);
    }

    const sortedWeeks = Array.from(weekKeysSet).sort((a, b) => b.localeCompare(a));

    return {
        rows: Array.from(map.values()),
        weeks: sortedWeeks
    };
};

export const processForecastExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<ForecastRecord[]> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData: any[] = XLSX.utils.sheet_to_json(worksheet, { defval: "" });

        return jsonData.map(row => ({
            asin: String(row['ASIN'] || row['asin'] || '').trim().toUpperCase(),
            annualForecast: parseUnits(String(row['Forecast 2026'] || row['forecast 2026'] || '0')),
            sku: row['SKU'] || row['sku'] || undefined,
            title: row['Title'] || row['title'] || row['Article Name'] || undefined,
            line: row['Product Line'] || row['line'] || row['ProductLine'] || undefined
        })).filter(r => r.asin && r.annualForecast > 0);
    } catch (error) {
        console.error("Error processing Forecast Excel:", error);
        throw error;
    }
};

export const calculateForecastViewData = (
    rawData: SalesRecord[],
    forecastData: ForecastRecord[],
    asinMetadata: Map<string, { sku: string; title: string; line: string }>,
    filters?: FilterState,
    velocityMap?: Map<string, number>
): ProductForecastData[] => {
    const data2025 = rawData.filter(r => r.year === 2025);
    const data2026 = rawData.filter(r => r.year === 2026);

    // Calculate Seasonality weights for 2025
    const getWeightsInfo = (records: SalesRecord[]): { weights: number[]; monthsCount: number } | null => {
        const weights = new Array(12).fill(0);
        let total = 0;
        const seenMonths = new Set<string>();

        records.forEach(r => {
            const m = r.month.split('-')[0];
            const idx = MONTH_ORDER.indexOf(m);
            if (idx !== -1 && r.units > 0) {
                weights[idx] += r.units;
                total += r.units;
                seenMonths.add(m);
            }
        });

        // If ASIN has any 2025 sales, we trust its specific seasonality.
        // Return null ONLY if there's no data at all for this ASIN in 2025.
        if (total === 0) return null;

        return {
            weights: weights.map(w => w / total),
            monthsCount: seenMonths.size
        };
    };

    // 1. Determine Global/Default Weights
    const panEuData2025 = data2025.filter(r => PAN_EU_COUNTRIES.includes(r.customer));
    // For Global Weights, we do NOT return null on sparse data (we accept whatever we have for the whole catalog)
    // We recreate a simple version of getWeights that doesn't return null for the global set
    const getGlobalWeightsInner = (records: SalesRecord[]) => {
        const weights = new Array(12).fill(0);
        let total = 0;
        const seenMonths = new Set<string>();

        records.forEach(r => {
            const m = r.month.split('-')[0];
            const idx = MONTH_ORDER.indexOf(m);
            if (idx !== -1) {
                weights[idx] += r.units;
                total += r.units;
                if (r.units > 0) seenMonths.add(m);
            }
        });

        // SAFETY NET: Even for Global Weights, if the reference file (2025 Sales) 
        // has fewer than 4 months of data (e.g. user only uploaded Jan 2025),
        // we should NOT assume 100% seasonality in those months. Fallback to flat.
        if (total === 0 || seenMonths.size < 4) {
            return new Array(12).fill(1 / 12);
        }

        return weights.map(w => w / total);
    };

    const panEuWeights = getGlobalWeightsInner(panEuData2025);

    // Check if we are in UK-only mode
    const isUkOnly = filters?.customer?.includes('Amazon UK') && filters.customer.length === 1;

    const getHybridWeights = (paEuRecords: SalesRecord[], ukRecords: SalesRecord[]) => {
        // Use Inner helper to ensure we always get weights for global subsets
        const peWeights = getGlobalWeightsInner(paEuRecords);
        const ukWeights = getGlobalWeightsInner(ukRecords);

        // Blend: Jan-Aug from Pan-EU, Sep-Dec from UK
        const hybrid = new Array(12).fill(0);
        const hasUkHistory = ukRecords.length > 0;

        for (let i = 0; i < 12; i++) {
            if (i < 8) { // Jan-Aug
                hybrid[i] = peWeights[i];
            } else { // Sep-Dec
                hybrid[i] = hasUkHistory ? ukWeights[i] : peWeights[i];
            }
        }

        // Normalize
        const sum = hybrid.reduce((a, b) => a + b, 0);
        return sum > 0 ? hybrid.map(w => w / sum) : peWeights;
    };

    const globalWeights = isUkOnly
        ? getHybridWeights(panEuData2025, data2025.filter(r => r.customer === 'Amazon UK'))
        : panEuWeights;

    // Map 2025 data by ASIN for quick access
    const dataByAsin2025 = new Map<string, SalesRecord[]>();
    data2025.forEach(r => {
        const key = r.asin.trim().toUpperCase();
        if (!dataByAsin2025.has(key)) dataByAsin2025.set(key, []);
        dataByAsin2025.get(key)!.push(r);
    });

    // Map 2026 actual sales by ASIN and Month
    const actuals2026 = new Map<string, Map<string, number>>();
    data2026.forEach(r => {
        const key = r.asin.trim().toUpperCase();
        const m = r.month.split('-')[0];
        if (!actuals2026.has(key)) actuals2026.set(key, new Map());
        const monthMap = actuals2026.get(key)!;
        monthMap.set(m, (monthMap.get(m) || 0) + r.units);
    });

    return forecastData.map(fc => {
        const identifier = fc.asin.toUpperCase();
        const meta = asinMetadata.get(identifier);
        const avgWeeklySales = velocityMap?.get(identifier) || 0;

        // 2. Determine weights for this ASIN
        const productRecords2025 = dataByAsin2025.get(identifier) || [];
        let productWeights = globalWeights;

        if (productRecords2025.length > 0) {
            if (isUkOnly) {
                const ukProd = productRecords2025.filter(r => r.customer === 'Amazon UK');
                const info = getWeightsInfo(ukProd);
                if (info) {
                    if (info.monthsCount < 6) {
                        // Smart Blending: 50% specific, 50% global
                        productWeights = info.weights.map((w, i) => (w * 0.5) + (globalWeights[i] * 0.5));
                    } else {
                        productWeights = info.weights;
                    }
                }
            } else {
                const info = getWeightsInfo(productRecords2025);
                if (info) {
                    if (info.monthsCount < 6) {
                        // Smart Blending: 50% specific, 50% global
                        productWeights = info.weights.map((w, i) => (w * 0.5) + (globalWeights[i] * 0.5));
                    } else {
                        productWeights = info.weights;
                    }
                }
                // If info is null (sparse data), productWeights remains globalWeights (Default)
            }
        }

        // 3. Build monthly points and aggregate
        const monthlyData: Record<string, MonthlyForecastPoint> = {};
        let totalActualUnits = 0;
        let totalForecastUnits = 0;

        MONTH_ORDER.forEach((m, idx) => {
            const forecastUnits = Math.round(fc.annualForecast * productWeights[idx]);
            const actualUnits = actuals2026.get(identifier)?.get(m) || 0;

            monthlyData[m] = {
                month: m,
                forecastUnits,
                actualUnits,
                units: actualUnits // Added for chart compatibility
            } as any;

            totalActualUnits += actualUnits;
            totalForecastUnits += forecastUnits;
        });

        const accuracy = totalForecastUnits > 0
            ? Math.min(100, Math.round((1 - Math.abs(totalActualUnits - totalForecastUnits) / totalForecastUnits) * 100))
            : 0;

        return {
            asin: identifier,
            sku: meta?.sku || fc.sku || identifier,
            title: meta?.title || fc.title || identifier,
            line: meta?.line || fc.line || "Unassigned",
            annualForecast: fc.annualForecast,
            actualUnits: totalActualUnits,
            forecastUnits: totalForecastUnits,
            accuracy: Math.max(0, accuracy),
            avgWeeklySales,
            monthlyData
        };
    });
};

export const processVendorStockExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<Map<string, { eu: number; uk: number }>> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData: any[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

        const vendorStockMap = new Map<string, { eu: number; uk: number }>();

        // Find header row (it contains "ASIN")
        let headerRowIndex = -1;
        for (let i = 0; i < Math.min(jsonData.length, 20); i++) {
            if (jsonData[i] && jsonData[i].includes('ASIN')) {
                headerRowIndex = i;
                break;
            }
        }

        if (headerRowIndex === -1) {
            console.warn("Could not find header row in Vendor Stock Excel");
            return vendorStockMap;
        }

        // Skip headers
        // A (0): ASIN
        // D (3): Marketplace (Country)
        // P (15): Sellable on hands units
        for (let i = headerRowIndex + 1; i < jsonData.length; i++) {
            const row = jsonData[i];
            if (!row || row.length < 16) continue;

            const asin = String(row[0] || '').trim().toUpperCase();
            const marketplace = String(row[3] || '').trim().toLowerCase();
            const stockValue = parseUnits(String(row[15] || '0'));

            if (!asin) continue;

            if (!vendorStockMap.has(asin)) {
                vendorStockMap.set(asin, { eu: 0, uk: 0 });
            }

            const current = vendorStockMap.get(asin)!;

            // Map to UK or EU
            if (marketplace.includes('uk') || marketplace.includes('kingdom') || marketplace === 'gb') {
                current.uk += stockValue;
            } else {
                // Assume everything else is Pan-EU for now if it's not UK
                current.eu += stockValue;
            }
        }

        return vendorStockMap;
    } catch (error) {
        console.error("Error processing Vendor Stock Excel:", error);
        throw error;
    }
};

export const calculateVelocityMap = (data: SalesRecord[]): Map<string, number> => {
    // 4-Week Average Sales Calculation
    const validYears = data.map(r => r.year).filter(y => y > 0);
    if (validYears.length === 0) return new Map();

    const latestYear = Math.max(...validYears);
    const yearData = data.filter(r => r.year === latestYear);
    const latestWeek = yearData.length > 0 ? Math.max(...yearData.map(r => r.week).filter(w => w !== undefined) as number[]) : 0;

    const last4WeeksKeys = new Set<string>();
    for (let i = 0; i < 4; i++) {
        let w = latestWeek - i;
        let y = latestYear;
        if (w <= 0) {
            w = 52 + w;
            y = latestYear - 1;
        }
        last4WeeksKeys.add(`${y}|${w}`);
    }

    const asin4WeekSales = new Map<string, number>();
    data.forEach(r => {
        if (r.week === undefined) return;
        if (last4WeeksKeys.has(`${r.year}|${r.week}`)) {
            const key = r.asin.trim().toUpperCase();
            asin4WeekSales.set(key, (asin4WeekSales.get(key) || 0) + r.units);
        }
    });

    const velocityMap = new Map<string, number>();
    asin4WeekSales.forEach((total, asin) => {
        velocityMap.set(asin, total / 4);
    });

    return velocityMap;
};

export const processStockExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<Map<string, number>> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData: any[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

        const stockMap = new Map<string, number>();

        // Skip headers (index 0)
        for (let i = 1; i < jsonData.length; i++) {
            const row = jsonData[i];
            const rawSku = String(row[0] || '').trim();
            if (!rawSku) continue;

            // Normalize SKU: Remove trailing EN or DE
            const normalizedSku = rawSku.replace(/(DE|EN)$/i, '');

            // User requested Column I which is index 8 (After Assembly Orders GMBH)
            const stockValue = Number(row[8] || 0);

            if (!isNaN(stockValue)) {
                const current = stockMap.get(normalizedSku) || 0;
                stockMap.set(normalizedSku, current + stockValue);
            }
        }

        return stockMap;
    } catch (error) {
        console.error("Error processing Stock Excel:", error);
        throw error;
    }
};

// Sheet configuration for Buy Box tracking
const BB_SHEET_CONFIG: { sheet: string; country: string; reasonCol: number }[] = [
    { sheet: 'BB_FR', country: 'FR', reasonCol: 18 },  // Col S = index 18
    { sheet: 'BB_UK', country: 'UK', reasonCol: 9 },   // Col J = index 9
    { sheet: 'BB_DE', country: 'DE', reasonCol: 13 },  // Col N = index 13
    { sheet: 'BB_IT', country: 'IT', reasonCol: 12 },  // Col M = index 12
    { sheet: 'BB_ES', country: 'ES', reasonCol: 13 },  // Col N = index 13
];

export const processBuyBoxExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<Map<string, { countries: string[]; reasons: Record<string, string> }>> => {
    try {
        const arrayBuffer = fileOrBuffer instanceof File
            ? await fileOrBuffer.arrayBuffer()
            : fileOrBuffer;
        const workbook = XLSX.read(arrayBuffer, { type: 'array' });

        // Map: ASIN -> { countries: [], reasons: {} }
        const buyBoxMap = new Map<string, { countries: string[]; reasons: Record<string, string> }>();

        for (const config of BB_SHEET_CONFIG) {
            const worksheet = workbook.Sheets[config.sheet];
            if (!worksheet) {
                console.warn(`[BuyBox] Sheet ${config.sheet} not found, skipping...`);
                continue;
            }

            const jsonData: any[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

            // Skip header row (index 0)
            for (let i = 1; i < jsonData.length; i++) {
                const row = jsonData[i];
                const asin = String(row[1] || '').trim().toUpperCase();  // Col B = index 1
                if (!asin || asin.length < 5) continue;

                const rawReason = String(row[config.reasonCol] || '').trim();

                // Only consider as BB lost if there is an Issue Type / Reason specified
                // Empty reason or "Fixed" status means no issue = they have the Buy Box
                if (!rawReason || rawReason.toLowerCase() === 'fixed') continue;

                const reason = rawReason;

                if (!buyBoxMap.has(asin)) {
                    buyBoxMap.set(asin, { countries: [], reasons: {} });
                }

                const entry = buyBoxMap.get(asin)!;
                if (!entry.countries.includes(config.country)) {
                    entry.countries.push(config.country);
                }
                entry.reasons[config.country] = reason;
            }
        }

        console.log(`[BuyBox] Processed ${buyBoxMap.size} ASINs with BB lost`);
        return buyBoxMap;
    } catch (error) {
        console.error("Error processing Buy Box Excel:", error);
        throw error;
    }
};
