/* global Excel, console */
import { SheetInfo, SheetMappingStatus, TARGET_COLUMNS } from "./models";

/**
 * Holt alle sichtbaren Arbeitsblätter des aktuellen Workbooks, außer "Gesamtliste".
 */
export async function getAvailableSheets(): Promise<SheetInfo[]> {
    return Excel.run(async (context) => {
        const sheets = context.workbook.worksheets;
        sheets.load("items/name, items/visibility");
        await context.sync();

        return sheets.items
            .filter((sheet) => sheet.name !== "Gesamtliste" && sheet.visibility === Excel.SheetVisibility.visible)
            .map((sheet) => ({
                id: sheet.name,
                name: sheet.name,
            }));
    });
}

/**
 * Extrahiert für ausgewählte Blätter die Kopfzeile und das Mapping-Ergebnis.
 * Die Funktion durchsucht die ersten 50 Zeilen (inkl. leerer Zeilen), 
 * um die eigentlichen Spaltennamen zu finden.
 */
export async function detectHeadersAndColumns(sheets: SheetInfo[]): Promise<SheetMappingStatus[]> {
    return Excel.run(async (context) => {
        const results: SheetMappingStatus[] = [];

        for (const sheetInfo of sheets) {
            let values: any[][] = [];

            if (sheetInfo.isExternal && sheetInfo.externalData) {
                // Bei externen Dateien nehmen wir einfach die ersten 50 Zeilen aus den geparsten Daten
                values = sheetInfo.externalData.slice(0, 50);
            } else {
                // Bei internen Dateien laden wir die Daten über die Excel API
                const sheet = context.workbook.worksheets.getItem(sheetInfo.name);
                const range = sheet.getRange("A1:AX50");
                range.load("values");
                await context.sync();
                values = range.values;
            }

            let bestRowIndex = 0;
            let maxMatches = -1;

            // Finde die Zeile, die die meisten Übereinstimmungen mit unseren Zielspalten hat
            for (let r = 0; r < values.length; r++) {
                const rowData = values[r];
                if (!rowData) continue; // Safety check in case external sheet has sparse arrays

                let matches = 0;
                for (let c = 0; c < rowData.length; c++) {
                    const cellStr = String(rowData[c]).trim().toLowerCase();
                    if (TARGET_COLUMNS.some((tc) => tc.aliases.includes(cellStr))) {
                        matches++;
                    }
                }
                if (matches > maxMatches) {
                    maxMatches = matches;
                    bestRowIndex = r;
                }
            }

            // Sicherstellen, dass values[bestRowIndex] existiert (falls Blatt komplett leer ist)
            // Wenn maxMatches immer noch -1 ist (oder 0), dann wurde absolut nichts gefunden
            const headerRow = (maxMatches > 0 && values[bestRowIndex]) ? values[bestRowIndex] : [];
            const status = buildSheetMappingStatus(sheetInfo, headerRow, maxMatches > 0 ? bestRowIndex : 0);
            results.push(status);
        }

        return results;
    });
}

/**
 * Erlaubt das Neuladen für eine spezifische (vom Nutzer geänderte) Kopfzeile.
 */
export async function detectHeadersForSingleSheetRow(sheetInfo: SheetInfo, rowIndex: number): Promise<SheetMappingStatus> {
    if (sheetInfo.isExternal && sheetInfo.externalData) {
        // Externe Datei
        const headerRow = sheetInfo.externalData[rowIndex] || [];
        return buildSheetMappingStatus(sheetInfo, headerRow, rowIndex);
    } else {
        // Interne Datei
        return Excel.run(async (context) => {
            const sheet = context.workbook.worksheets.getItem(sheetInfo.name);
            // rowIndex ist 0-basiert, Range ist 1-basiert
            const range = sheet.getRangeByIndexes(rowIndex, 0, 1, 50); // Lese Spalten 0-49 in der gewünschten Zeile
            range.load("values");
            await context.sync();

            return buildSheetMappingStatus(sheetInfo, range.values[0], rowIndex);
        });
    }
}

/**
 * Hilfsfunktion zum Erstellen des State-Objekts für das Mapping.
 */
function buildSheetMappingStatus(sheetInfo: SheetInfo, headerRow: any[], rowIndex: number): SheetMappingStatus {
    const availableColumns = headerRow.map((val, idx) => ({
        name: String(val).trim() || `(Leere Spalte ${idx + 1})`,
        index: idx,
    })).filter(col => col.name !== `(Leere Spalte ${col.index + 1})`);

    const mappings = TARGET_COLUMNS.map((tc) => {
        const foundIdx = headerRow.findIndex((cell) => tc.aliases.includes(String(cell).trim().toLowerCase()));
        return {
            targetColumn: tc.id,
            sourceColumnIndex: foundIdx,
        };
    });

    return {
        sheetName: sheetInfo.name, // Für Anzeige
        headerRowIndex: rowIndex,
        availableColumns,
        mappings,
        isExternal: sheetInfo.isExternal,
        externalData: sheetInfo.externalData
    };
}

/**
 * Führt die eigentliche Konsolidierung aus allen Arbeitsblättern durch und schreibt
 * das Ergebnis in das Blatt "Gesamtliste".
 */
export async function consolidateData(mappings: SheetMappingStatus[]): Promise<number> {
    return Excel.run(async (context) => {
        let rowsConsolidated = 0;
        const finalData: any[][] = [];

        // Daten auslesen und mergen
        for (const mapping of mappings) {
            if (mapping.mappings.length === 0) continue;

            let textValues: any[][] = [];
            let dataStartRowOffset = 0;
            const targetIndicesMap = mapping.mappings.map(m => m.sourceColumnIndex);

            if (mapping.isExternal && mapping.externalData) {
                // Bei externen Dateien
                textValues = mapping.externalData;
                dataStartRowOffset = mapping.headerRowIndex + 1;
            } else {
                // Bei internen Dateien
                const sheet = context.workbook.worksheets.getItem(mapping.sheetName);
                const usedRange = sheet.getUsedRange();
                usedRange.load(["rowIndex", "rowCount", "columnCount", "text"]);
                await context.sync();

                const startRowIdx = usedRange.rowIndex;
                // Offset berechnen: Wie viele Zeilen nach dem Start des UsedRange liegt die erste Datenzeile?
                dataStartRowOffset = (mapping.headerRowIndex + 1) - startRowIdx;

                // Wenn die Daten unterhalb des UsedRange beginnen oder das Blatt leer ist
                if (dataStartRowOffset >= usedRange.rowCount || usedRange.rowCount === 0) {
                    continue;
                }

                textValues = usedRange.text; // Text returns the exact formatted strings
            }

            // Schleife ab Zeile nach Header bis Ende
            for (let r = Math.max(0, dataStartRowOffset); r < textValues.length; r++) {
                const textRow = textValues[r] || [];

                // Prüfen ob Row komplett leer ist (oder zumindest in den Mapped Columns leer)
                const isRowEmpty = targetIndicesMap.every(srcColIdx => {
                    if (srcColIdx === -1) return true;
                    const val = textRow[srcColIdx];
                    return val === null || val === undefined || String(val).trim() === "";
                });

                if (isRowEmpty) continue;

                const consolidatedRow: any[] = [];
                // Die 6 Standard Target Columns
                for (const srcColIdx of targetIndicesMap) {
                    if (srcColIdx !== -1 && srcColIdx < textRow.length) {
                        consolidatedRow.push(String(textRow[srcColIdx]));
                    } else {
                        consolidatedRow.push("");
                    }
                }

                // Zusatzfelder für die Kabelliste: Länge, gezogen am, von (Monteur)
                consolidatedRow.push("");
                consolidatedRow.push("");
                consolidatedRow.push("");

                finalData.push(consolidatedRow);
                rowsConsolidated++;
            }
        }

        // Kabelliste erstellen oder überschreiben
        let targetSheet: Excel.Worksheet;
        try {
            targetSheet = context.workbook.worksheets.getItem("Kabelliste");
            // Falls sie existiert, zuerst löschen, um sie neu zu erstellen
            targetSheet.delete();
            await context.sync();
        } catch (e) {
            // Ignorieren (Blatt existiert noch nicht)
        }

        try {
            targetSheet = context.workbook.worksheets.add("Kabelliste");

            // Header Zeile schreiben
            const fullHeaders = [...TARGET_COLUMNS.map(tc => tc.id), "Länge", "gezogen am", "von (Monteur)"];

            // finalData hat fullHeaders.length Spalten
            const totalRowsCount = finalData.length + 1; // +1 für Header
            const totalColsCount = fullHeaders.length;

            const targetRange = targetSheet.getRangeByIndexes(0, 0, totalRowsCount, totalColsCount);

            const allValues = [fullHeaders, ...finalData];

            // Formatiere die Zielzellen als Text ("@"), BEVOR die Werte reingeschrieben werden, 
            // damit Excel nicht versucht, Datumsstrings als numerische Datumsformate umzuwandeln.
            const formatArray: string[][] = [];
            for (let i = 0; i < totalRowsCount; i++) {
                formatArray.push(new Array(totalColsCount).fill("@"));
            }

            targetRange.numberFormat = formatArray;
            targetRange.values = allValues;

            await context.sync();

            // Als Tabelle formatieren
            const table = targetSheet.tables.add(targetRange, true /* hasHeaders */);
            table.name = "KonsolidierteKabel";
            table.style = "TableStyleLight9";
            table.showFilterButton = true;

            // Spaltenbreite anpassen (AutoFit)
            targetRange.format.autofitColumns();
            await context.sync();

            targetSheet.activate();

            return rowsConsolidated;
        } catch (error) {
            console.error("Fehler beim Erstellen der Kabelliste:", error);
            throw new Error("Fehler beim Erstellen der 'Kabelliste'. Möglicherweise ist die Arbeitsmappe schreibgeschützt.");
        }
    });
}
