import * as vscode from 'vscode';
import { ConnectionNode } from './connections'
import { spawn } from 'child_process'
import { TextDocumentContentProvider } from './resultsetview'

const extensionName = "frog";
const previewUri = vscode.Uri.parse('frog-view://authority/resultset');

export class SessionManager {
    private _currentConnection : ConnectionNode
    private outputChannel
    private diagnosticCollection;
    private statusBarItem
    private dbSession
    private provider

    constructor(context: vscode.ExtensionContext) {
        
        this.outputChannel = vscode.window.createOutputChannel('SQL Query Results');
        context.subscriptions.push(this.outputChannel);

        this.diagnosticCollection = vscode.languages.createDiagnosticCollection(extensionName);
        context.subscriptions.push(this.diagnosticCollection);

        this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
        this.statusBarItem.text = 'Frog: No connection'
        this.statusBarItem.show()
        context.subscriptions.push(this.statusBarItem);

        this.provider = new TextDocumentContentProvider();
        let registration = vscode.workspace.registerTextDocumentContentProvider('frog-view', this.provider);
    }

    public set currentConnection(c : ConnectionNode) {
        this.statusBarItem.text = c.name;
        if (this.dbSession) this.closeSession();
        this._currentConnection = c;
    }

    public get currentConnection() : ConnectionNode {
        return this._currentConnection;
    }

    public isSessionActive() : boolean {
        return this.dbSession;
    }

    public closeSession() {
        if (!this.dbSession) return;
        
        this.dbSession.stdin.end();
        this.dbSession = null;
    }

    public check() : boolean {
        if (!this.currentConnection) {
            vscode.window.showWarningMessage('No connection selected');
            return false;
        }

        let extPath = vscode.extensions.getExtension("todie.frog").extensionPath;
        this.dbSession = spawn("java", ['-jar', extPath + '/frog-runner.jar', this.currentConnection.host, this.currentConnection.port.toString(), this.currentConnection.schema, this.currentConnection.username, this.currentConnection.password]);

        let outputBuffer = "";

        this.dbSession.stdout.on('data', (data) => {
            outputBuffer += data.toString();
            
            try {
                let json = JSON.parse(outputBuffer);

                const diagnostics = [];
                let cd = json['columnDefinitions']

                if (!json['rows']) {
                    if ('dbOutput' in json) {
                        this.outputChannel.append(json['dbOutput'].map((x) => `DB: ${x}\n`).join(''));
                    }
                    if (json['userErrors']) {
                        this.outputChannel.append("Errors found:\n");

                        const editor = vscode.window.activeTextEditor;
                        const document = editor.document;
                        
                        for (let error of json['userErrors'][0]['lines']) {
                            let range = document.lineAt(editor.selection.start.line + error['line'] - 1).range;
                            let diagnostic = new vscode.Diagnostic(range, error['text'], vscode.DiagnosticSeverity.Error);
                            diagnostic.source = extensionName;
                            diagnostic.code = "code";
                            diagnostics.push(diagnostic);
                            this.outputChannel.append(error['line'] + ": " + error['text'] + "\n");
                        }

                        this.diagnosticCollection.set(document.uri, diagnostics);
                    } else if (json['exception']) {
                        this.outputChannel.append("Exception caught: " + json['exceptionMessage'] + "\n");
                    } else if ("affectedRows" in json) {
                        this.outputChannel.append(json['affectedRows'] + " rows affected.\n");
                    } else {
                        this.outputChannel.append("SQL executed successfully.\n");
                    }
                } else {
                    this.provider.setData(json);
                    this.provider.update(previewUri);

                    vscode.commands.executeCommand('vscode.previewHtml', previewUri, vscode.ViewColumn.Two, 'Result Set View').then((success) => {
                    }, (reason) => {
                        vscode.window.showErrorMessage(reason);
                    });
            
                    this.outputChannel.append("Query successful.\n");
                }

                this.outputChannel.append("-- " + new Date() + " -------------------------------\n");
                outputBuffer = "";
                this.statusBarItem.text = this.currentConnection.name + " (Connected)";
            } catch (e) {
                console.log(e);
            }
            this.statusBarItem.text = this.currentConnection.name + " (Connected)";
        });

        this.dbSession.stderr.on('data', (data) => {
            this.outputChannel.show(true);
            this.outputChannel.append(data.toString());
        });

        this.dbSession.on('close', (code) => {
            console.log(`child process exited with code ${code}`);
            this.dbSession = null;
        });

        return true;
    }

    public execute(text : string, rowLimit : number) {
        if (!this.check()) return;

        this.outputChannel.append("-- " + new Date() + " -------------------------------\n"); //Executing: '" + text + "'\n");
        let input = { "sql": text, "rowLimit": rowLimit };
        this.dbSession.stdin.write(JSON.stringify(input) + "\n--- END ---\n");
        this.statusBarItem.text = this.currentConnection.name + " (Connected, Query running...)";
    }
}