import React, { useEffect, useRef } from 'react';
import { classNames } from 'primereact/utils';
import { createUniver, LocaleType, mergeLocales } from '@univerjs/presets';
import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core';
import UniverPresetSheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US';
import '@univerjs/preset-sheets-core/lib/index.css';
import { __ } from '@wordpress/i18n';

// tools
import { xlsxToWorkbookData } from '../../../../pages/BandoEdit/components/BandoEditFormStep3Excel/xlsxToWorkbookData';

const TAG_RE = /^\{\{gepafin_field:[^|]+\|[^}]+}}$/;

const BLOCKED_COMMANDS = new Set([
    'sheet.command.set-range-values',
    'sheet.command.clear-selection-content',
    'sheet.command.clear-selection-all',
    'sheet.command.delete-range',
    'sheet.command.insert-range',
    'sheet.command.set-range-format',
    'sheet.command.insert-sheet',
    'sheet.command.delete-sheet',
    'sheet.command.rename-sheet',
]);

const buildTagMap = (workbookData) => {
    const map = {};
    if (!workbookData?.sheets) return map;
    const sheetId = workbookData.sheetOrder?.[0];
    const sheet = sheetId ? workbookData.sheets[sheetId] : Object.values(workbookData.sheets)[0];
    if (!sheet?.cellData) return map;
    Object.entries(sheet.cellData).forEach(([r, row]) => {
        Object.entries(row).forEach(([c, cell]) => {
            if (typeof cell.f === 'string' && TAG_RE.test(cell.f)) {
                map[`${r}:${c}`] = { v: cell.v, f: cell.f };
            }
        });
    });
    return map;
};

const parseWorkbook = (val) => {
    if (!val) return null;
    if (typeof val === 'object' && val.sheets) return val;
    if (typeof val === 'string') {
        try {
            const parsed = JSON.parse(val);
            return parsed && parsed.sheets ? parsed : null;
        } catch {
            return null;
        }
    }
    return null;
};

const Spreadsheet = ({ fieldName, label, errors = {}, defaultValue, setDataFn, template, register, config = {} }) => {
    const containerRef = useRef(null);
    const univerRef = useRef(null);
    const univerAPIRef = useRef(null);
    const saveTimerRef = useRef(null);
    const isRestoringRef = useRef(false);
    const tagCellMapRef = useRef({});
    const reinitializeRef = useRef(null);
    const initializedFromSavedRef = useRef(false);
    const fileInputRef = useRef(null);
    const addSheetsFileInputRef = useRef(null);

    useEffect(() => {
        if (!containerRef.current) return;

        const templateData = parseWorkbook(template);
        const gepafin = templateData?.gepafin ?? {};
        const editableSet = new Set(gepafin.editableCells ?? []);
        const inputOnly = editableSet.size > 0;
        const validationCellKeys = new Set((gepafin.validationCells ?? []).map(vc => vc.key ?? vc));

        const doInit = (workbookData) => {
            if (univerRef.current) {
                univerRef.current.dispose();
                univerRef.current = null;
                univerAPIRef.current = null;
            }

            const { univer, univerAPI } = createUniver({
                locale: LocaleType.EN_US,
                locales: {
                    [LocaleType.EN_US]: mergeLocales(UniverPresetSheetsCoreEnUS),
                },
                presets: [
                    UniverSheetsCorePreset({
                        container: containerRef.current,
                    }),
                ],
            });

            univerRef.current = univer;
            univerAPIRef.current = univerAPI;

            tagCellMapRef.current = buildTagMap(workbookData);
            univerAPI.createWorkbook(workbookData);

            // Apply visual markers for editable and validation cells in input-only mode
            if (inputOnly && (editableSet.size > 0 || validationCellKeys.size > 0)) {
                setTimeout(() => {
                    const wb = univerAPIRef.current?.getActiveWorkbook();
                    const sheet = wb?.getActiveSheet();
                    if (!sheet) return;
                    editableSet.forEach((key) => {
                        const [r, c] = key.split(':').map(Number);
                        sheet.getRange(r, c, 1, 1).setBackground('#dcfce7');
                    });
                    validationCellKeys.forEach((key) => {
                        const [r, c] = key.split(':').map(Number);
                        sheet.getRange(r, c, 1, 1).setBackground('#fef3c7');
                    });
                }, 300);
            }

            const restoreTagCells = () => {
                if (isRestoringRef.current) return;
                const wb = univerAPIRef.current?.getActiveWorkbook();
                if (!wb) return;
                const sheet = wb.getActiveSheet();
                if (!sheet) return;
                const saved = wb.save();
                if (!saved?.sheets) return;
                const sheetId = saved.sheetOrder?.[0];
                const sheetData = sheetId ? saved.sheets[sheetId] : Object.values(saved.sheets)[0];
                if (!sheetData?.cellData) return;

                const restorations = [];
                Object.entries(sheetData.cellData).forEach(([r, row]) => {
                    Object.entries(row).forEach(([c, cell]) => {
                        if (typeof cell.v === 'string' && TAG_RE.test(cell.v)) {
                            const orig = tagCellMapRef.current[`${r}:${c}`];
                            if (orig) restorations.push({ r: +r, c: +c, v: orig.v, f: orig.f });
                        }
                    });
                });

                if (restorations.length === 0) return;
                isRestoringRef.current = true;
                restorations.forEach(({ r, c, v, f }) => {
                    sheet.getRange(r, c, 1, 1).setValues([[{ v, f }]]);
                });
                isRestoringRef.current = false;
            };

            let restoreTimer;
            univerAPI.addEvent(univerAPI.Event.BeforeCommandExecute, (event) => {
                // Input-only mode: block write commands on non-editable cells
                if (inputOnly && BLOCKED_COMMANDS.has(event.id)) {
                    const sheet = univerAPIRef.current?.getActiveWorkbook()?.getActiveSheet();
                    const range = sheet?.getActiveRange();
                    if (range) {
                        const key = `${range.getRow()}:${range.getColumn()}`;
                        if (!editableSet.has(key)) {
                            event.cancel = true;
                            return;
                        }
                    }
                }

                clearTimeout(saveTimerRef.current);
                saveTimerRef.current = setTimeout(() => {
                    const wb = univerAPIRef.current?.getActiveWorkbook();
                    if (wb) {
                        setDataFn(fieldName, wb.save(), { shouldValidate: true });
                    }
                }, 300);
                clearTimeout(restoreTimer);
                restoreTimer = setTimeout(restoreTagCells, 80);
            });
        };

        const initialData = parseWorkbook(defaultValue) || templateData || { name: 'Sheet' };
        initializedFromSavedRef.current = !!parseWorkbook(defaultValue);
        doInit(initialData);
        reinitializeRef.current = doInit;

        return () => {
            clearTimeout(saveTimerRef.current);
            if (univerRef.current) {
                univerRef.current.dispose();
                univerRef.current = null;
                univerAPIRef.current = null;
            }
        };
    }, []); // eslint-disable-line react-hooks/exhaustive-deps

    // If the component mounted before saved data was available (reset() cleared form values),
    // reinitialize univerjs once when the valid saved workbook arrives via defaultValue.
    useEffect(() => {
        if (initializedFromSavedRef.current) return; // already showing saved data
        if (!reinitializeRef.current) return;         // init useEffect hasn't run yet
        const parsedDefault = parseWorkbook(defaultValue);
        if (!parsedDefault) return;                   // still no valid workbook
        initializedFromSavedRef.current = true;       // prevent re-triggering on user edits
        reinitializeRef.current(parsedDefault);
    }, [defaultValue]); // eslint-disable-line react-hooks/exhaustive-deps

    useEffect(() => {
        const el = containerRef.current;
        if (!el) return;
        const handler = (e) => { e.stopPropagation(); e.preventDefault(); };
        el.addEventListener('wheel', handler, { passive: false });
        return () => el.removeEventListener('wheel', handler);
    }, []);

    useEffect(() => {
        register(fieldName, config);
    }, []); // eslint-disable-line react-hooks/exhaustive-deps

    const handleImport = (e) => {
        const file = e.target.files?.[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = (ev) => {
            const data = xlsxToWorkbookData(ev.target.result);
            reinitializeRef.current?.(data);
            setTimeout(() => {
                const wb = univerAPIRef.current?.getActiveWorkbook();
                if (wb) setDataFn(fieldName, wb.save(), { shouldValidate: true });
            }, 400);
        };
        reader.readAsArrayBuffer(file);
        e.target.value = '';
    };

    const handleImportAsSheets = (e) => {
        const file = e.target.files?.[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = (ev) => {
            const imported = xlsxToWorkbookData(ev.target.result);
            const wb = univerAPIRef.current?.getActiveWorkbook();
            if (!wb) return;
            const current = wb.save();
            const existingIds = new Set(current.sheetOrder || []);
            const mergedSheets = { ...(current.sheets || {}) };
            const mergedOrder = [...(current.sheetOrder || [])];
            (imported.sheetOrder || []).forEach((importedId) => {
                let newId = importedId;
                let counter = 0;
                while (existingIds.has(newId)) {
                    counter++;
                    newId = `${importedId}_${counter}`;
                }
                existingIds.add(newId);
                mergedSheets[newId] = { ...imported.sheets[importedId], id: newId };
                mergedOrder.push(newId);
            });
            const merged = { ...current, sheets: mergedSheets, sheetOrder: mergedOrder };
            reinitializeRef.current?.(merged);
            setTimeout(() => {
                const wbAfter = univerAPIRef.current?.getActiveWorkbook();
                if (wbAfter) setDataFn(fieldName, wbAfter.save(), { shouldValidate: true });
            }, 400);
        };
        reader.readAsArrayBuffer(file);
        e.target.value = '';
    };

    const templateData = parseWorkbook(template);
    const inputOnly = (templateData?.gepafin?.editableCells?.length ?? 0) > 0;

    return (
        <>
            {label
                ? <label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
                    {label}{config.required || config.isRequired ?
                    <span className="appForm__field--required">*</span> : null}
                </label>
                : null}
            {inputOnly && (
                <div className="appPageSection__message warning">
                    <span>
                        {__('Modalità inserimento dati — modifica solo le celle evidenziate in verde', 'gepafin')}
                    </span>
                </div>
            )}
            {!inputOnly && (
                <div style={{ marginBottom: '8px', display: 'flex', gap: '8px' }}>
                    <input
                        ref={fileInputRef}
                        type="file"
                        accept=".xlsx,.xls,.csv,.ods"
                        style={{ display: 'none' }}
                        onChange={handleImport}
                    />
                    <input
                        ref={addSheetsFileInputRef}
                        type="file"
                        accept=".xlsx,.xls,.csv,.ods"
                        style={{ display: 'none' }}
                        onChange={handleImportAsSheets}
                    />
                    <button
                        type="button"
                        className="p-button p-button-outlined p-button-sm"
                        onClick={() => fileInputRef.current?.click()}
                    >
                        {__('Importa foglio', 'gepafin')}
                    </button>
                    <button
                        type="button"
                        className="p-button p-button-outlined p-button-sm"
                        onClick={() => addSheetsFileInputRef.current?.click()}
                    >
                        {__('Aggiungi fogli', 'gepafin')}
                    </button>
                </div>
            )}
            <div
                ref={containerRef}
                style={{ width: '100%', height: '500px' }}
            />
        </>
    );
};

export default Spreadsheet;
