import blessed from 'blessed';
import { exec, spawn } from 'child_process';
import { writeFile } from 'fs/promises';
import { createWriteStream } from 'fs';
import { join } from 'path';
import { homedir } from 'os';

function _esc(s) { return String(s).replace(/[{}]/g, ch => `\\${ch}`); }

function _traceFileHeader(publicUrl, companyName) {
    const now = new Date().toLocaleString('fr-FR');
    return [
        '# Rainbow Webhook Listener — API Trace',
        `# Date    : ${now}`,
        `# Company : ${companyName}`,
        `# URL     : ${publicUrl}`,
        '# ' + '─'.repeat(60),
        ''
    ].join('\n');
}

function _traceEventToText({ ts, method, endpoint, status, headers, requestBody, snippet }) {
    const ep = (endpoint || '').replace(/^https?:\/\/[^/]+/, '');
    const lines = [`[${ts}] ${method || '?'} ${ep}  →  ${status ?? '?'}`];
    if (headers && Object.keys(headers).length > 0) {
        lines.push('  Headers:');
        for (const [k, v] of Object.entries(headers)) lines.push(`    ${k}: ${v}`);
    }
    if (requestBody) lines.push(`  Request:  ${requestBody}`);
    if (snippet)     lines.push(`  Response: ${snippet}`);
    lines.push('');
    return lines.join('\n') + '\n';
}

/**
 * TUI temps reel pour afficher les evenements webhook Rainbow.
 * Layout : info bar (haut) + event list + detail panel + footer.
 */
export class WebhookListenerDisplay {
    constructor({ screen, publicUrl, companyName, companyId, eventTypes, onExit, traceMode = false, traceFile = null, onSubscriptionInfo = null }) {
        this.screen = screen;
        this.onExit = onExit;
        this.publicUrl = publicUrl;
        this._onSubscriptionInfo = onSubscriptionInfo;
        this.events = [];
        this.focusedPanel = 'events';

        this._traceMode = traceMode;
        this._traceEvents = [];
        this._traceStream = null;
        this._showTrace = true;

        if (traceFile) {
            this._traceStream = createWriteStream(traceFile, { flags: 'a' });
            this._traceStream.write(_traceFileHeader(publicUrl, companyName));
        }

        this.eventColors = {
            presence: 'green',
            device_state: 'blue',
            calllog: 'yellow',
            authentication: 'magenta',
            security: 'red'
        };

        this._footerContent = ' {bold}?{/bold} Help  {bold}Tab{/bold} Switch  {bold}o{/bold} Open URL  {bold}y{/bold} Copy  {bold}e{/bold} Export  {bold}c{/bold} Clear' +
            (onSubscriptionInfo ? '  {bold}i{/bold} Sub info' : '') +
            (traceMode ? '  {bold}t{/bold} API trace  {bold}s{/bold} save trace' : '') +
            '  {bold}q{/bold} Quit';

        this._createWidgets(publicUrl, companyName, companyId, eventTypes);
        this._initEventListeners();
        this.eventList.focus();
    }

    _openInBrowser(url) {
        // WSL / Linux : tente xdg-open puis explorer.exe (WSL)
        // macOS : open, Windows natif : start
        if (process.platform === 'darwin') {
            exec(`open "${url}"`);
        } else if (process.platform === 'win32') {
            exec(`start "" "${url}"`);
        } else {
            exec(`xdg-open "${url}" 2>/dev/null || explorer.exe "${url}" 2>/dev/null || true`);
        }
    }

    _createWidgets(publicUrl, companyName, companyId, eventTypes) {
        // Barre d'info (haut) — 2 lignes de contenu → height 4
        const companyLabel = companyId ? `${companyName} (${companyId})` : companyName;
        this.statusBar = blessed.box({
            parent: this.screen,
            top: 0,
            left: 0,
            width: '100%',
            height: 4,
            tags: true,
            border: { type: 'line' },
            style: { fg: 'white', bg: 'blue', border: { fg: 'cyan' } },
            content: ` {bold}Webhook Listener{/bold} | ${companyLabel} | ${eventTypes.join(', ')} | Events: 0\n URL: ${publicUrl}  {grey-fg}[o] open in browser{/grey-fg}`
        });

        // Liste des evenements
        this.eventList = blessed.list({
            parent: this.screen,
            top: 4,
            left: 0,
            width: '100%',
            height: '55%-4',
            tags: true,
            border: { type: 'line' },
            label: ' {bold}Events{/bold} ',
            keys: true,
            vi: true,
            mouse: true,
            style: {
                fg: 'white',
                border: { fg: 'green' },
                selected: { bg: 'blue' },
                item: { fg: 'white' }
            },
            scrollbar: {
                ch: ' ',
                track: { bg: 'yellow' },
                style: { inverse: true }
            }
        });

        // Panneau detail (bas) — tags: false pour afficher le JSON tel quel
        this.detailPanel = blessed.box({
            parent: this.screen,
            top: '55%',
            left: 0,
            width: '100%',
            height: '45%-3',
            tags: false,
            border: { type: 'line' },
            label: ' Details ',
            scrollable: true,
            alwaysScroll: true,
            keys: true,
            vi: true,
            mouse: true,
            style: {
                fg: 'white',
                border: { fg: 'cyan' }
            },
            scrollbar: {
                ch: ' ',
                track: { bg: 'yellow' },
                style: { inverse: true }
            },
            content: '\n  Waiting for events...'
        });

        // Footer
        this.footer = blessed.box({
            parent: this.screen,
            bottom: 0,
            left: 0,
            width: '100%',
            height: 3,
            tags: true,
            border: { type: 'line' },
            style: { fg: 'white', border: { fg: 'cyan' } },
            content: this._footerContent
        });

        const traceHelpSection = this._traceMode ? `

  {bold}API Trace{/bold}
  t                 Toggle API trace visibility in event list
  s                 Save API trace to file` : '';

        const subInfoHelpSection = this._onSubscriptionInfo ? `
  i                 Query snapshot & status for all active subscriptions` : '';

        // Overlay d'aide
        this.helpBox = blessed.box({
            parent: this.screen,
            top: 'center',
            left: 'center',
            width: '60%',
            height: (this._traceMode || this._onSubscriptionInfo) ? '60%' : '50%',
            tags: true,
            border: { type: 'line' },
            style: { fg: 'white', bg: 'black', border: { fg: 'white' } },
            label: ' Help (Escape to hide) ',
            hidden: true,
            content: `
  {bold}Rainbow Webhook Listener{/bold}

  {bold}Navigation{/bold}
  Up / Down         Navigate event list
  Tab               Switch focus between events and detail panel
  PgUp / PgDown     Scroll detail panel

  {bold}Actions{/bold}
  o                 Open webhook URL in browser (smee.io dashboard)
  y                 Copy selected event payload to clipboard
  e                 Export all events to ~/rainbow-webhook-<timestamp>.json
  c                 Clear all events${subInfoHelpSection}${traceHelpSection}

  {bold}General{/bold}
  F1 or ?           Show / hide this help
  q                 Quit and cleanup (delete webhook + subscriptions)`
        });

        // Overlay subscription info (snapshot + status)
        this.subscriptionInfoBox = blessed.box({
            parent: this.screen,
            top: 'center',
            left: 'center',
            width: '80%',
            height: '70%',
            tags: false,
            border: { type: 'line' },
            style: { fg: 'white', bg: 'black', border: { fg: 'yellow' } },
            label: ' Subscription Info  (Escape / i to close) ',
            hidden: true,
            scrollable: true,
            alwaysScroll: true,
            keys: true,
            vi: true,
            mouse: true,
            scrollbar: {
                ch: ' ',
                track: { bg: 'yellow' },
                style: { inverse: true }
            },
        });
    }

    _initEventListeners() {
        // Selection dans la liste → afficher le detail
        this.eventList.on('select item', (item, index) => {
            this._showEventDetail(index);
        });

        // Navigation fleches → auto-afficher le detail
        this.eventList.on('keypress', (ch, key) => {
            if (key.name === 'up' || key.name === 'down') {
                process.nextTick(() => {
                    const idx = this.eventList.selected;
                    if (idx >= 0 && idx < this.events.length) {
                        this._showEventDetail(idx);
                    }
                });
            }
        });

        // Tab : changer de panneau
        this.screen.key('tab', () => {
            if (this.focusedPanel === 'events') {
                this.focusedPanel = 'detail';
                this.detailPanel.focus();
                this.eventList.style.border.fg = 'cyan';
                this.detailPanel.style.border.fg = 'green';
            } else {
                this.focusedPanel = 'events';
                this.eventList.focus();
                this.eventList.style.border.fg = 'green';
                this.detailPanel.style.border.fg = 'cyan';
            }
            this.screen.render();
        });

        // Ouvrir l'URL dans le navigateur
        this.screen.key('o', () => {
            if (!this.helpBox.hidden) return;
            this._openInBrowser(this.publicUrl);
        });

        // Copier la payload de l'evenement selectionne dans le presse-papiers
        this.screen.key('y', () => {
            if (!this.helpBox.hidden) return;
            const idx = this.eventList.selected;
            if (idx < 0 || idx >= this.events.length) {
                this._showNotification('{yellow-fg}No event selected{/yellow-fg}');
                return;
            }
            const event = this.events[idx];
            if (event._isTrace) {
                this._showNotification('{yellow-fg}API trace — use {bold}s{/bold} to save trace{/yellow-fg}');
                return;
            }
            const json = JSON.stringify(event, null, 2);
            this._copyToClipboard(json);
            this._showNotification('{green-fg}Payload copied to clipboard{/green-fg}');
        });

        // Exporter tous les evenements vers un fichier JSON
        this.screen.key('e', async () => {
            if (!this.helpBox.hidden) return;
            const webhookEvents = this.events.filter(e => !e._isTrace);
            if (webhookEvents.length === 0) {
                this._showNotification('{yellow-fg}No events to export{/yellow-fg}');
                return;
            }
            try {
                const path = await this._exportEvents(webhookEvents);
                this._showNotification(`{green-fg}Exported ${webhookEvents.length} event(s) → ${path}{/green-fg}`);
            } catch (err) {
                this._showNotification(`{red-fg}Export failed: ${err.message}{/red-fg}`);
            }
        });

        // Clear events
        this.screen.key('c', () => {
            if (!this.helpBox.hidden) return;
            this.events = [];
            this.eventList.clearItems();
            this.detailPanel.setContent('\n  Events cleared.');
            this._updateStatusBar();
            this.screen.render();
        });

        // Basculer la visibilite des traces API
        this.screen.key('t', () => {
            if (!this.helpBox.hidden) return;
            if (!this._traceMode) return;
            this._showTrace = !this._showTrace;
            this._showNotification(this._showTrace
                ? '{green-fg}API trace visible{/green-fg}'
                : '{yellow-fg}API trace hidden (new traces only){/yellow-fg}');
        });

        // Sauvegarder la trace API dans un fichier
        this.screen.key('s', async () => {
            if (!this.helpBox.hidden) return;
            if (!this._traceMode) return;
            if (this._traceEvents.length === 0) {
                this._showNotification('{yellow-fg}No API trace events to save{/yellow-fg}');
                return;
            }
            await this._promptSaveTrace();
        });

        // Subscription info : snapshot + status pour chaque souscription active
        this.screen.key('i', async () => {
            if (!this.helpBox.hidden) return;
            // Fermer l'overlay s'il est deja ouvert
            if (!this.subscriptionInfoBox.hidden) {
                this.subscriptionInfoBox.hide();
                if (this.focusedPanel === 'events') this.eventList.focus();
                else this.detailPanel.focus();
                this.screen.render();
                return;
            }
            if (!this._onSubscriptionInfo) return;
            this._showNotification('{yellow-fg}Fetching subscription info...{/yellow-fg}');
            try {
                const results = await this._onSubscriptionInfo();
                this._showSubscriptionInfoOverlay(results);
            } catch (err) {
                this._showNotification(`{red-fg}Subscription info failed: ${err.message}{/red-fg}`);
            }
        });

        // Aide
        this.screen.key(['f1', '?'], () => {
            if (this.helpBox.hidden) {
                this.helpBox.show();
                this.helpBox.focus();
            } else {
                this.helpBox.hide();
                if (this.focusedPanel === 'events') this.eventList.focus();
                else this.detailPanel.focus();
            }
            this.screen.render();
        });

        // Quitter avec cleanup
        this.screen.key(['q'], () => {
            if (!this.helpBox.hidden) {
                this.helpBox.hide();
                this.screen.render();
                return;
            }
            this._confirmQuit();
        });

        // Escape : fermer les overlays
        this.screen.key(['escape'], () => {
            if (!this.subscriptionInfoBox.hidden) {
                this.subscriptionInfoBox.hide();
                if (this.focusedPanel === 'events') this.eventList.focus();
                else this.detailPanel.focus();
                this.screen.render();
                return;
            }
            if (!this.helpBox.hidden) {
                this.helpBox.hide();
                if (this.focusedPanel === 'events') this.eventList.focus();
                else this.detailPanel.focus();
                this.screen.render();
            }
        });
    }

    /**
     * Construit un resume bref a partir du premier sous-evenement selon la categorie.
     * Structure des payloads (cf. OpenAPI eventprovider.json) :
     *   presence    → { userId, presences:    [{ type, show, status, state, resource, ... }] }
     *   calllog     → { userId, calllogs:     [{ state, incoming, duration, peer, media, ... }] }
     *   device_state→ { userId, device_states:[{ type, state, deviceId, ... }] }
     *   authentication→{ userId, authentications:[{ eventType, deviceType, ... }] }
     *   security    → { userId, securities:   [{ eventType, ... }] }
     */
    _buildSummary(event) {
        const category = event._category || 'unknown';
        let inner = [];
        if (category === 'presence')          inner = event.presences       || [];
        else if (category === 'calllog')      inner = event.calllogs        || [];
        else if (category === 'device_state') inner = event.device_states   || [];
        else if (category === 'authentication') inner = event.authentications || [];
        else if (category === 'security')     inner = event.securities      || [];

        if (inner.length === 0) return event.userId ? event.userId.slice(-8) : '';

        const first = inner[0];
        let detail = '';
        if (category === 'presence') {
            // type: computer/mobile/phone/calendar/teams
            // computer: show (away/busy/dnd/offline/online/xa)
            // phone: state (out_of_service/busy/free)
            detail = first.show || first.state || first.presence || '';
            if (first.type) detail += ` [${first.type}]`;
        } else if (category === 'calllog') {
            const dir = first.incoming ? 'IN' : 'OUT';
            detail = `${first.state || ''} ${dir}`;
        } else if (category === 'device_state') {
            detail = `${first.state || ''} (${first.type || ''})`;
        } else {
            // authentication / security : eventType (login-succeed, logout, password-change...)
            detail = first.eventType || '';
        }

        let summary = event.userId ? `${event.userId.slice(-8)} ${detail}` : detail;
        if (inner.length > 1) summary += ` +${inner.length - 1}`;
        return summary;
    }

    /**
     * Ajouter un evenement webhook recu et mettre a jour l'affichage.
     * Chaque evenement correspond a un utilisateur dans un appel webhook Rainbow.
     */
    addEvent(event) {
        const time = new Date(event._receivedAt).toLocaleTimeString('fr-FR', { hour12: false });
        const category = event._category || 'unknown';
        const color = this.eventColors[category] || 'white';
        const summary = this._buildSummary(event);

        const line = ` {${color}-fg}${time}  ${category.toUpperCase().padEnd(15)}{/${color}-fg} ${summary}`;

        this.events.push(event);
        this.eventList.addItem(line);

        // Auto-scroll vers le dernier evenement
        this.eventList.select(this.events.length - 1);
        this._showEventDetail(this.events.length - 1);
        this._updateStatusBar();
        this.screen.render();
    }

    /**
     * Ajouter un evenement de trace API dans la liste.
     */
    addApiTraceEvent(data) {
        const ts = new Date().toISOString().substring(11, 19);
        this._traceEvents.push({ ts, ...data });
        if (this._traceStream) this._traceStream.write(_traceEventToText({ ts, ...data }));
        if (!this._showTrace) return;

        const method = data.method || '?';
        const ep = (data.endpoint || '').replace(/^https?:\/\/[^/]+/, '');
        const status = data.status ?? '?';
        const statusTag = (typeof status === 'number' && status < 300) ? 'green-fg' : 'red-fg';
        const line = ` {magenta-fg}[API] ${ts}  ${_esc(method.padEnd(6))}{/magenta-fg} {cyan-fg}${_esc(ep.substring(0, 50))}{/cyan-fg}  {${statusTag}}→ ${status}{/${statusTag}}`;

        this.events.push({ _isTrace: true, ts, ...data });
        this.eventList.addItem(line);
        this.eventList.select(this.events.length - 1);
        this._showEventDetail(this.events.length - 1);
        this._updateStatusBar();
        this.screen.render();
    }

    _showEventDetail(index) {
        if (index < 0 || index >= this.events.length) return;
        const event = this.events[index];
        if (event._isTrace) {
            const { _isTrace, ...data } = event;
            this.detailPanel.setContent(JSON.stringify(data, null, 2));
            this.detailPanel.scrollTo(0);
            return;
        }
        const json = JSON.stringify(event, null, 2);
        this.detailPanel.setContent(json);
        this.detailPanel.scrollTo(0);
    }

    /**
     * Copier du texte dans le presse-papiers.
     * WSL : clip.exe  |  Linux : xclip/xsel  |  macOS : pbcopy
     */
    _copyToClipboard(text) {
        let cmd, args;
        if (process.platform === 'darwin') {
            [cmd, args] = ['pbcopy', []];
        } else if (process.platform === 'win32') {
            [cmd, args] = ['clip', []];
        } else {
            // WSL ou Linux : essaie clip.exe (WSL) puis xclip puis xsel
            [cmd, args] = ['sh', ['-c', 'clip.exe 2>/dev/null || xclip -selection clipboard 2>/dev/null || xsel --clipboard --input 2>/dev/null']];
        }
        const proc = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore'] });
        proc.stdin.write(text);
        proc.stdin.end();
        proc.on('error', () => {}); // ignore si l'outil est absent
    }

    /**
     * Exporter les evenements webhook vers ~/rainbow-webhook-<timestamp>.json
     * Retourne le chemin du fichier cree.
     */
    async _exportEvents(webhookEvents) {
        const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
        const filePath = join(homedir(), `rainbow-webhook-${ts}.json`);
        await writeFile(filePath, JSON.stringify(webhookEvents, null, 2), 'utf-8');
        return filePath;
    }

    /**
     * Prompt pour sauvegarder la trace API dans un fichier.
     */
    async _promptSaveTrace() {
        const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
        const defaultName = `webhook-trace-${ts}.txt`;

        return new Promise((resolve) => {
            const form = blessed.form({
                parent: this.screen,
                top: 'center',
                left: 'center',
                width: '60%',
                height: 7,
                keys: true,
                tags: true,
                border: { type: 'line' },
                style: { fg: 'white', bg: 'black', border: { fg: 'cyan' } },
                label: ' Save API Trace '
            });

            blessed.text({
                parent: form,
                top: 0,
                left: 1,
                tags: true,
                content: '{cyan-fg}File:{/cyan-fg} (Enter to confirm, Escape to cancel)'
            });

            const input = blessed.textbox({
                parent: form,
                top: 1,
                left: 1,
                width: '100%-4',
                height: 3,
                keys: true,
                mouse: true,
                inputOnFocus: true,
                border: { type: 'line' },
                style: {
                    fg: 'white', bg: 'black',
                    border: { fg: 'yellow' },
                    focus: { border: { fg: 'green' } }
                },
                value: defaultName
            });

            const close = (saved) => {
                form.destroy();
                if (this.focusedPanel === 'events') this.eventList.focus();
                else this.detailPanel.focus();
                this.screen.render();
                resolve(saved);
            };

            input.key('enter', async () => {
                const filename = input.getValue().trim() || defaultName;
                const filePath = filename.startsWith('/') ? filename : join(homedir(), filename);
                try {
                    let content = _traceFileHeader(this.publicUrl, '');
                    for (const ev of this._traceEvents) content += _traceEventToText(ev);
                    await writeFile(filePath, content, 'utf-8');
                    close(true);
                    this._showNotification(`{green-fg}Trace saved → ${filePath}{/green-fg}`);
                } catch (err) {
                    close(false);
                    this._showNotification(`{red-fg}Save failed: ${err.message}{/red-fg}`);
                }
            });

            input.key('escape', () => close(false));

            form.focus();
            input.focus();
            this.screen.render();
        });
    }

    _confirmQuit() {
        const box = blessed.box({
            parent:  this.screen,
            top:     'center',
            left:    'center',
            width:   46,
            height:  5,
            tags:    true,
            border:  { type: 'line' },
            style:   { fg: 'white', bg: 'black', border: { fg: 'yellow' } },
            label:   ' Quitter ',
            content: '\n  Quitter et supprimer le webhook ?  {bold}o{/bold} / {bold}n{/bold}',
        });

        const close = () => { box.destroy(); this.screen.render(); };

        box.key(['o', 'O', 'y', 'Y', 'enter'], async () => {
            close();
            this.screen.destroy();
            await this.onExit();
        });
        box.key(['n', 'N', 'escape', 'q'], () => close());

        box.focus();
        this.screen.render();
    }

    /**
     * Afficher le snapshot et le status de chaque souscription dans un overlay.
     */
    _showSubscriptionInfoOverlay(results) {
        const SEP = '─'.repeat(64);
        const lines = [];

        if (!results || results.length === 0) {
            lines.push('  No active subscriptions.');
        } else {
            for (const { id, eventType, snapshot, status } of results) {
                lines.push(SEP);
                lines.push(`  Subscription: ${id}  [${eventType}]`);
                lines.push('');
                lines.push('  STATUS:');
                const statusText = JSON.stringify(status, null, 2);
                for (const l of statusText.split('\n')) lines.push('    ' + l);
                lines.push('');
                lines.push('  SNAPSHOT:');
                const snapshotText = JSON.stringify(snapshot, null, 2);
                for (const l of snapshotText.split('\n')) lines.push('    ' + l);
                lines.push('');
            }
            lines.push(SEP);
        }

        this.subscriptionInfoBox.setContent(lines.join('\n'));
        this.subscriptionInfoBox.scrollTo(0);
        this.subscriptionInfoBox.show();
        this.subscriptionInfoBox.focus();
        this.screen.render();
    }

    /**
     * Afficher une notification temporaire dans le footer (3 secondes).
     */
    _showNotification(message) {
        this.footer.setContent(` ${message}`);
        this.screen.render();
        if (this._notifTimer) clearTimeout(this._notifTimer);
        this._notifTimer = setTimeout(() => {
            this.footer.setContent(this._footerContent);
            this.screen.render();
        }, 3000);
    }

    _updateStatusBar() {
        const count = this.events.filter(e => !e._isTrace).length;
        const content = this.statusBar.content;
        this.statusBar.setContent(content.replace(/Events: \d+/, `Events: ${count}`));
    }
}
