import {ISessionContext, ToolbarButton} from '@jupyterlab/apputils';
import {Cell} from '@jupyterlab/cells';
import {IChangedArgs} from '@jupyterlab/coreutils';
import {IObservableJSON} from '@jupyterlab/observables';
import {DocumentRegistry} from '@jupyterlab/docregistry';
import {NotebookPanel, INotebookModel, Notebook} from '@jupyterlab/notebook';
import {Kernel} from '@jupyterlab/services';
import {IDisposable, DisposableDelegate} from '@lumino/disposable';
import * as C from './constants';
import {NotebookRunner} from "./notebook-runner";
import {JSONObject} from "@lumino/coreutils";

/*
import {
   icon as iconFunc //, toHtml
} from '@fortawesome/fontawesome-svg-core';

import {
   faGlasses
} from '@fortawesome/free-solid-svg-icons';

import {
   faArrowAltCircleRight
} from '@fortawesome/free-regular-svg-icons';
 */

/**
 * adds the custom vadalog buttons to the UI (vadalogRun, codeAnalysis)
 * and manages the creation of a new widget for the explanation graphs
 */
export class VadalogButtons implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
    notebookRunner: NotebookRunner = new NotebookRunner();
    buttons: ToolbarButton[] = [];
    isVadalogKernel: boolean = false;
    private originalKernelInterrupt: (() => Promise<void>) | null = null;

    private static isVadalogKernel(name: String): boolean {
        if (!name) return false;
        return name.toLowerCase().indexOf("vadalog") >= 0
    }

    /**
     * shows or hides the buttons if the kernel is a vadalog kernel or not, respectively.
     */
    private _onKernelChanged(sender: ISessionContext, _args: IChangedArgs<Kernel.IKernelConnection | null, Kernel.IKernelConnection | null, 'kernel'>, panel?: NotebookPanel): void {
        if (!sender.session || !sender.session.kernel) return;

        if (VadalogButtons.isVadalogKernel(sender.session.kernel.name)) {
            this.buttons.forEach(button => button.show());
            this.isVadalogKernel = true;
            
            // Setup stop button interceptor for Vadalog kernel
            if (panel) {
                this.setupStopButtonInterceptor(panel);
            }
        } else {
            this.buttons.forEach(button => button.hide());
            this.isVadalogKernel = false;
            
            // Remove stop button interceptor when not Vadalog kernel
            if (panel) {
                this.removeStopButtonInterceptor(panel);
            }
        }
    }

    /**
     * hides the vadalog buttons if the active cell is a markdown or a raw cell.
     */
    private _onActiveCellChanged(_sender: Notebook, cell: Cell): void {
        if (!cell || !cell.model || !this.isVadalogKernel) return;

        if (cell.model.type == 'markdown' || cell.model.type == 'raw') {
            this.buttons.forEach(button => button.hide());
        } else if (cell.model.type == 'code') {
            this.buttons.filter(button => button.isHidden).forEach(button => button.show());
        }
    }

    // NOTE: Currently unused since custom run button is disabled. Preserved for future use.
    // @ts-ignore - Unused function warning suppressed
    private static getMode(obj: IObservableJSON): string {
        let vadalog = obj.get("vadalog");
        if (vadalog !== undefined) {
            vadalog = vadalog as JSONObject;
            let mode = vadalog["mode"];
            if (mode !== undefined)
                return "" + mode;
        }
        return C.RUN_PROGRAM_MODE;
    }

    /**
     * Get the Vadalog endpoint URL from cell metadata or use default
     */
    private getVadalogEndpoint(panel: NotebookPanel): string {
        const activeCell = panel.content.activeCell;
        
        if (activeCell) {
            // In JupyterLab 4, metadata is a plain object accessed via sharedModel
            const metadata = activeCell.model.sharedModel.getMetadata();
            if (metadata && typeof metadata === 'object' && 'vadalog' in metadata) {
                const vadalogMeta = metadata['vadalog'] as any;
                if (vadalogMeta && typeof vadalogMeta === 'object' && 'vadalog_endpoint' in vadalogMeta) {
                    return vadalogMeta['vadalog_endpoint'] as string;
                }
            }
        }
        // Fallback to notebook-level metadata or default
        const notebookMetadata = panel.content.model.sharedModel.getMetadata();
        if (notebookMetadata && typeof notebookMetadata === 'object' && 'vadalog' in notebookMetadata) {
            const vadalogNotebookMeta = notebookMetadata['vadalog'] as any;
            if (vadalogNotebookMeta && typeof vadalogNotebookMeta === 'object' && 'vadalog_endpoint' in vadalogNotebookMeta) {
                return vadalogNotebookMeta['vadalog_endpoint'] as string;
            }
        }
        // Default endpoint (from config-local.json)
        return "http://localhost:8080";
    }

    /**
     * Call the Vadalog backend /stop endpoint to interrupt execution
     */
    private async callVadalogStop(vadalogEndpoint: string): Promise<void> {
        try {
            const stopUrl = `${vadalogEndpoint}/stop`;
            
            const response = await fetch(stopUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
            });
            
            if (!response.ok) {
                console.warn("Vadalog stop endpoint returned status:", response.status);
            }
        } catch (error) {
            console.error("Error calling Vadalog stop endpoint:", error);
            throw error; // Re-throw to trigger fallback
        }
    }

    /**
     * Setup the stop button interceptor for Vadalog kernel
     * Overrides kernel.interrupt() to call Vadalog /stop endpoint instead
     */
    private setupStopButtonInterceptor(panel: NotebookPanel): void {
        // Clear any existing interceptor first
        this.removeStopButtonInterceptor(panel);
        
        // Use a small delay to ensure kernel is fully ready
        setTimeout(() => {
            const kernel = panel.sessionContext.session?.kernel;
            
            if (kernel && kernel.connectionStatus === 'connected') {
                // Save the original interrupt method
                this.originalKernelInterrupt = kernel.interrupt.bind(kernel);
                
                // Replace interrupt with our Vadalog stop logic
                kernel.interrupt = async () => {
                    const vadalogEndpoint = this.getVadalogEndpoint(panel);
                    
                    try {
                        // Call Vadalog stop endpoint instead of interrupting
                        await this.callVadalogStop(vadalogEndpoint);
                        // Don't call the original interrupt - let Vadalog respond naturally to the kernel
                        // This allows the kernel to receive Vadalog's response and clear the [*] indicator
                    } catch (error) {
                        console.error("Error calling Vadalog stop endpoint:", error);
                        // If stop fails, fall back to original kernel interrupt
                        if (this.originalKernelInterrupt) {
                            await this.originalKernelInterrupt();
                        }
                    }
                };
                
                // Note: Removed statusChanged listener to prevent infinite loop
                // The interceptor will be re-setup via kernelChanged event in createNew()
                
            } else {
                // Retry after another delay if kernel isn't ready
                if (kernel) {
                    setTimeout(() => this.setupStopButtonInterceptor(panel), 500);
                }
            }
        }, 200);
    }

    /**
     * Remove the stop button interceptor and restore original kernel interrupt
     */
    private removeStopButtonInterceptor(panel: NotebookPanel): void {
        // Restore original kernel interrupt method
        const kernel = panel.sessionContext.session?.kernel;
        if (kernel && this.originalKernelInterrupt) {
            kernel.interrupt = this.originalKernelInterrupt;
            this.originalKernelInterrupt = null;
        }
    }


    /**
     * Create a new extension object.
     */
    createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
        // add listeners for changes of the kernel or the active cell
        context.sessionContext.kernelChanged.connect((sender, args) => {
            this._onKernelChanged(sender, args, panel);
        }, this);
        panel.content.activeCellChanged.connect(this._onActiveCellChanged, this);

        if (context.sessionContext.session) {
            this.isVadalogKernel = VadalogButtons.isVadalogKernel(context.sessionContext.session.kernel.name);
            
            // Setup stop button interceptor if already using Vadalog kernel
            if (this.isVadalogKernel) {
                this.setupStopButtonInterceptor(panel);
            }
        }


        // === NO CUSTOM BUTTONS ===
        // We leverage the standard Jupyter run and stop buttons
        // The stop button is intercepted via setupStopButtonInterceptor()
        // The run button works automatically with the Vadalog kernel

        // === VADALOG CODE ANALYSIS BUTTON ===
        // REMOVED: Code analysis button not needed

        // === VADALOG CODE LINTER BUTTON ===
        //
        // NOTE: Linting and module depencies work with libvada only.
        /*
        let codeLintingButtonCallback = async () => {
            VadalogLinter.lintNotebook(context, panel.content).then();
        };
        let codeLintingButton = new ToolbarButton({
            className: 'vadalogCodeLintingBtn',
            iconClass: 'vadalogCodeLintingIcon',
            onClick: codeLintingButtonCallback,
            tooltip: 'Vadalog code linting for a selected cell'
        });
        this.buttons.push(codeLintingButton);
        if (!this.isVadalogKernel)
            codeLintingButton.hide();

        panel.toolbar.insertAfter('vadalogCodeAnalysisRun', 'vadalogCodeLinting', codeLintingButton);

        // === VADALOG MODULE DEPENDENCIES BUTTON ===
        let moduleDependenciesButtonCallback = async () => {
            if (context.sessionContext.session && context.sessionContext.session.kernel) {
                this.notebookRunner.run(panel.content, C.MODULE_DEPENDENCIES_MODE, true, context);
            }
        };
        let moduleDependenciesButton = new ToolbarButton({
            className: 'vadalogModuleDependenciesBtn',
            iconClass: 'vadalogModuleDependenciesIcon',
            onClick: moduleDependenciesButtonCallback,
            tooltip: 'Show module dependencies for current cell'
        });
        this.buttons.push(moduleDependenciesButton);
        if (!this.isVadalogKernel)
            moduleDependenciesButton.hide();

        panel.toolbar.insertAfter('vadalogCodeLinting', 'vadalogModuleDependencies', moduleDependenciesButton);
        */
        // === VADALOG RUN CELL MAGIC BUTTON ===
        // REMOVED: Magic directives button not needed


        return new DisposableDelegate(() => {
            // Clean up stop button interceptor
            this.removeStopButtonInterceptor(panel);
            
            // Dispose notebook runner
            this.notebookRunner.dispose();
        });
    }
}
