/** @odoo-module **/

import { _t } from "@web/core/l10n/translation";
import { getTemplate } from "@web/core/templates";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";

import { AccountMoveFormRenderer } from '@account/components/account_move_form/account_move_form';
import { BoxLayer } from '@odex30_account_invoice_extract/js/box_layer';

import { App, onWillUnmount, reactive, useExternalListener, useState } from "@odoo/owl";


export class InvoiceExtractFormRenderer extends AccountMoveFormRenderer {
    /**
     * @override
     */
    setup() {
        super.setup();

        this.store = useState(useService("mail.store"));
        this.dialog = useService("dialog");
        this.orm = useService("orm");
        this.mailPopoutService = useService("mail.popout");

        this._fieldsMapping = {
            'partner_id': 'supplier',
            'ref': 'invoice_id',
            'invoice_date': 'date',
            'invoice_date_due': 'due_date',
            'currency_id': 'currency',
            'quick_edit_total_amount': 'total',
        };

        this.dataMoveId = -1;

        this.boxLayerApps = [];
        this.activeField = undefined;
        this.activeFieldEl = undefined;
        this.boxes = [];
        this.selectedBoxes = {};

        this.state = useState({
            visibleBoxes: {},
        });

        useExternalListener(window, "focusin", (event) => {
            const field_widget = event.target.closest(".o_field_widget");
            if (field_widget){
                this.onFocusFieldWidget(field_widget);
            }
        });

        useExternalListener(window, "focusout", (event) => {
            if (event.target.closest(".o_field_widget") && !this.mailPopoutService.externalWindow){
                this.onBlurFieldWidget();
            }
        });

        onWillUnmount (() => {
            this.destroyBoxLayers();
        });
    }

    fetchBoxData() {
        this.dataMoveId = this.props.record.resId;
        return this.orm.call('account.move', 'get_boxes', [this.props.record.resId]);
    }


    createBoxLayerApp(props) {
        props.onClickBoxCallback = this.onClickBox.bind(this);
        return new App(BoxLayer, {
            env: this.env,
            dev: this.env.debug,
            getTemplate,
            props,
            translatableAttributes: ["data-tooltip"],
            translateFn: _t,
        });
    }


    renderBoxLayers(element) {
        const proms = [];
        if (element.classList.contains('img-fluid')) {
            this.destroyBoxLayers();
            const boxLayerApp = this.createBoxLayerApp({
                boxes: this.state.visibleBoxes[0] || [],
                mode: 'img',
                pageLayer: element,
            });
            proms.push(boxLayerApp.mount(element.parentElement));
            this.boxLayerApps = [boxLayerApp];
        }
        if (element.tagName === 'IFRAME') {
            const pdfDocument = element.contentDocument;
            if (!pdfDocument.querySelector('head link#box_layer')) {
                const win = this.mailPopoutService.externalWindow || window;
                const boxLayerStylesheet = win.document.createElement('link');
                boxLayerStylesheet.setAttribute('id', 'box_layer');
                boxLayerStylesheet.setAttribute('rel', 'stylesheet');
                boxLayerStylesheet.setAttribute('type', 'text/css');
                boxLayerStylesheet.setAttribute('href', '/odex30_account_invoice_extract/static/src/css/box_layer.css');
                pdfDocument.querySelector('head').append(boxLayerStylesheet);
            }
            const pageLayers = pdfDocument.querySelectorAll('.page');
            for (const pageLayer of pageLayers) {
                const pageNum = pageLayer.dataset['pageNumber'] - 1;
                const boxLayerApp = this.createBoxLayerApp({
                    boxes: this.state.visibleBoxes[pageNum] || [],
                    mode: 'pdf',
                    pageLayer: pageLayer,
                });
                proms.push(boxLayerApp.mount(pageLayer));
                this.boxLayerApps.push(boxLayerApp);
            }
        }
        return Promise.all(proms);
    }


    renderInvoiceExtract(attachment) {
        const thread = this.store.Thread.insert({
            id: this.props.record.resId,
            model: this.props.record.resModel,
        });
        const preview_attachment_id = thread.mainAttachment.id;
        if (
            ['in_invoice', 'in_refund', 'out_invoice', 'out_refund'].includes(this.props.record.data.move_type) &&
            this.props.record.data.state === 'draft' &&
            ['waiting_validation', 'validation_to_send'].includes(this.props.record.data.extract_state) &&
            this.props.record.data.extract_attachment_id &&
            preview_attachment_id === this.props.record.data.extract_attachment_id[0]
        ) {
            if (this.activeField !== undefined) {
                if (this.dataMoveId !== this.props.record.resId) {
                    for (const boxesForPage of Object.values(this.boxes)) {
                        boxesForPage.length = 0;
                    }
                }
                const dataToFetch = this.boxes.length === 0 || (this.dataMoveId !== this.props.record.resId);
                const prom = dataToFetch ? this.fetchBoxData() : new Promise(resolve => resolve([]));
                prom.then((boxes) => {
                    boxes.map(b => reactive(b)).forEach((box) => {
                        if (box.page in this.boxes) {
                            this.boxes[box.page].push(box);
                        }
                        else {
                            this.boxes[box.page] = [box];
                        }
                        if (box.user_selected) {
                            this.selectedBoxes[box.feature] = box;
                        }
                    });
                    for (const [page, boxesForPage] of Object.entries(this.boxes)) {
                        if (page in this.state.visibleBoxes) {
                            this.state.visibleBoxes[page].length = 0;
                        }
                        else {
                            this.state.visibleBoxes[page] = [];
                        }

                        const visibleBoxesForPage = boxesForPage.filter((box) => {
                            return (
                                box.feature === this.activeField ||
                                (box.feature === "VAT_Number" && this.activeField === "supplier")
                            );
                        });
                        this.state.visibleBoxes[page].push(...visibleBoxesForPage);
                    }
                    this.renderBoxLayers(attachment)
                });
            }
        }
    }


    showBoxesForField(fieldName) {
        const win = this.mailPopoutService.externalWindow || window;
        const iframe = win.document.querySelector('.o-mail-Attachment iframe');
        if (iframe) {
            const iframeDoc = iframe.contentDocument;
            if (iframeDoc) {
                this.renderInvoiceExtract(iframe);
                return;
            }
        }
        const attachment = win.document.getElementById('attachment_img');
        if (attachment && attachment.complete) {
            this.renderInvoiceExtract(attachment);
            return;
        }
    }

    resetActiveField() {
        Object.values(this.state.visibleBoxes).forEach(boxesForPage => {
            boxesForPage.length = 0;
        });
        this.activeField = undefined;
        this.activeFieldEl = undefined;
        this.destroyBoxLayers();
    }

    destroyBoxLayers() {
        for (const boxLayerApp of this.boxLayerApps) {
            boxLayerApp.destroy();
        }
        this.boxLayerApps = [];
    }

    async openCreatePartnerDialog(context) {
        const ctx_from_db = await this.orm.call('account.move', 'get_partner_create_data', [[this.props.record.resId], context]);
        this.dialog.add(
            FormViewDialog,
            {
                resModel: 'res.partner',
                context: Object.assign(
                    ctx_from_db,
                    Object.fromEntries(Object.entries(context).filter(([k, v]) => v !== undefined))
                ),
                title: _t("Create"),
                onRecordSaved: (record) => {
                    this.props.record.update({ partner_id: [record.resId] });
                },
            }
        );
    }


    async handleFieldChanged(fieldName, newFieldValue) {
        let changes = {};
        switch (fieldName) {
            case 'date':
                changes = { invoice_date: registry.category("parsers").get("date")(newFieldValue.split(' ')[0]) };
                break;
            case 'supplier':
            case 'VAT_Number':
                if (Array.isArray(newFieldValue) && newFieldValue.length == 2){
                    changes = { partner_id: [newFieldValue[0]], partner_bank_id: [newFieldValue[1]] };
                }
                else if (Number.isFinite(newFieldValue) && newFieldValue !== 0) {
                    changes = { partner_id: [newFieldValue] };
                } else {
                    await this.openCreatePartnerDialog({
                        default_name: this.selectedBoxes['supplier']?.text,
                        default_vat: this.selectedBoxes['VAT_Number']?.text
                    });
                    return;
                }
                break;
            case 'due_date':
                changes = { invoice_date_due: registry.category("parsers").get("date")(newFieldValue.split(' ')[0]) };
                break;
            case 'invoice_id':
                changes =  ['out_invoice', 'out_refund'].includes(this.props.record.context.default_move_type) ? { name: newFieldValue } : { ref: newFieldValue };
                break;
            case 'currency':
                changes = { currency_id: [newFieldValue] };
                break;
            case 'total':
                changes = { quick_edit_total_amount: Number(newFieldValue) };
                break;
        }
        this.props.record.update(changes)
    }


    onFocusFieldWidget(field_widget) {
        const fieldName = this._fieldsMapping[field_widget.getAttribute('name')];

        if (fieldName === undefined) {
            this.resetActiveField();
            return;
        }

        this.activeField = fieldName;
        this.activeFieldEl = field_widget;

        this.showBoxesForField(fieldName);
    }


    onBlurFieldWidget() {
        this.resetActiveField();
    }

    async onClickBox(boxId, boxPage) {
        const box = this.boxes[boxPage].find(box => box.id === boxId);
        const fieldName = box.feature;

        if (this.selectedBoxes[fieldName]) {
            this.selectedBoxes[fieldName].user_selected = false;
        }

        box.user_selected = true;
        this.selectedBoxes[fieldName] = box;

        const newFieldValue = await this.orm.call(
            'account.move',
            'set_user_selected_box',
            [[this.dataMoveId], boxId],
        )

        await this.handleFieldChanged(fieldName, newFieldValue);

        if (['supplier', 'VAT_Number'].includes(box.feature)) {
            this.activeFieldEl.querySelector('.o-autocomplete--dropdown-menu')?.classList.toggle('show');
        } else if (['date', 'due_date'].includes(box.feature)) {
            this.activeFieldEl.querySelector('input').dispatchEvent(new KeyboardEvent('keydown', {
                key: 'Escape',
            }));
        }
    }
};
