import blessed from 'blessed';

/**
 * TUI pour UCS S2S
 *
 * Layout :
 *   ┌─ header ─────────────────────────────────────────────────────────┐
 *   │ Events (50%)                │ Conversations (50%)                │
 *   │                             │                                    │
 *   ├─────────────────────────────┴────────────────────────────────────┤
 *   │ ↑↓ select conv  [c] new  [r] refresh  [Enter] send  [q] quit    │
 *   │ > <input>                                                        │
 *   │ <status>                                                         │
 *   └──────────────────────────────────────────────────────────────────┘
 *
 * Clés :
 *   ↑ / ↓      naviguer dans la liste des conversations (toujours actif)
 *   c          ouvre une invite pour créer une conversation (peer ID / email)
 *   r          recharge la liste des conversations
 *   Enter      dans la liste : focus le champ de saisie
 *              dans le champ  : envoyer le message
 *   Escape     depuis le champ : revenir à la liste
 *   q / Ctrl+C quitter + cleanup
 */
export class UCSDisplay {
    constructor({ screen, cnxId, publicUrl, traceMode = false, traceFile = null, onCreateConversation, onSendMessage, onLoadConversations, onExit }) {
        this._traceMode = traceMode;
        this._traceEvents = [];   // accumulateur pour la sauvegarde
        this._traceStream = null;
        this._cnxId = cnxId;
        this._publicUrl = publicUrl;

        if (traceFile) {
            import('node:fs').then(({ createWriteStream }) => {
                this._traceStream = createWriteStream(traceFile, { flags: 'a' });
                this._traceStream.write(_traceFileHeader(cnxId, publicUrl));
            }).catch(() => {});
        }
        this.screen = screen;
        this.cnxId = cnxId;
        this.conversations = [];
        this.selectedConvIndex = 0;

        this._onCreateConversation = onCreateConversation;
        this._onSendMessage = onSendMessage;
        this._onLoadConversations = onLoadConversations;
        this._onExit = onExit;

        this._build(publicUrl);
        this._bindKeys();
        this._loadConversations();
    }

    _build(publicUrl) {
        const screen = this.screen;

        this.header = blessed.box({
            parent: screen,
            top: 0, left: 0, width: '100%', height: 1,
            content: ` {bold}Rainbow UCS S2S{/bold}  cnx: ${this.cnxId}  |  ${publicUrl}`,
            tags: true,
            style: { bg: 'blue', fg: 'white' }
        });

        this.eventsBox = blessed.box({
            parent: screen,
            label: ' Events ',
            top: 1, left: 0, width: '50%', height: screen.height - 4,
            border: { type: 'line' },
            scrollable: true, alwaysScroll: true, mouse: true,
            keys: true, vi: true,
            tags: true,
            style: { border: { fg: 'cyan' }, focus: { border: { fg: 'yellow' } } }
        });

        this.convList = blessed.list({
            parent: screen,
            label: ' Conversations ',
            top: 1, left: '50%', width: '50%', height: screen.height - 4,
            border: { type: 'line' },
            scrollable: true, alwaysScroll: true, mouse: true,
            keys: true, vi: true,
            tags: true,
            style: {
                border: { fg: 'cyan' },
                focus: { border: { fg: 'yellow' } },
                selected: { bg: 'blue', fg: 'white' }
            },
            items: ['(no conversations)']
        });

        const traceHint = this._traceMode ? '  {bold}t{/bold} toggle api  {bold}s{/bold} save trace' : '';
        this.hints = blessed.box({
            parent: screen,
            top: screen.height - 3, left: 0, width: '100%', height: 1,
            content: ` {bold}↑↓{/bold} select conv  {bold}c{/bold} new conv  {bold}r{/bold} refresh  {bold}Enter{/bold} send msg  {bold}Esc{/bold} back to list${traceHint}  {bold}q{/bold} quit`,
            tags: true,
            style: { bg: 'black', fg: 'white' }
        });

        this.inputBox = blessed.textbox({
            parent: screen,
            top: screen.height - 2, left: 0, width: '100%', height: 1,
            mouse: true,
            style: { fg: 'white', bg: 'black', focus: { bg: '#111' } }
        });

        this.statusBar = blessed.box({
            parent: screen,
            top: screen.height - 1, left: 0, width: '100%', height: 1,
            content: ' Ready',
            tags: true,
            style: { bg: 'black', fg: 'green' }
        });

        // Sélectionner une conversation via Enter dans la liste → focus inputBox
        this.convList.on('select', (_item, index) => {
            this.selectedConvIndex = index;
            this._focusInput();
        });

        // Mise à jour de l'index lors du changement de sélection avec les flèches
        this.convList.on('select item', (_item, index) => {
            this.selectedConvIndex = index;
        });

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

    _focusInput() {
        this.inputBox.focus();
        this.inputBox.readInput(() => {});
        this.screen.render();
    }

    _bindKeys() {
        const screen = this.screen;

        screen.key('C-c', () => this._onExit());
        screen.key('q',   () => {
            if (screen.focused === this.inputBox) return;
            this._confirmQuit();
        });

        // ↑ / ↓ : naviguer dans la liste depuis n'importe où sauf depuis l'inputBox
        screen.key('up', () => {
            if (screen.focused === this.inputBox) return;
            this.convList.up(1);
            screen.render();
        });

        screen.key('down', () => {
            if (screen.focused === this.inputBox) return;
            this.convList.down(1);
            screen.render();
        });

        screen.key('c', () => {
            if (screen.focused === this.inputBox) return;
            this._promptNewConversation();
        });

        if (this._traceMode) {
            this._showTrace = true;
            screen.key('t', () => {
                if (screen.focused === this.inputBox) return;
                this._showTrace = !this._showTrace;
                this._setStatus(`API trace: ${this._showTrace ? 'visible' : 'hidden'}`);
            });
            screen.key('s', () => {
                if (screen.focused === this.inputBox) return;
                this._promptSaveTrace();
            });
        }

        screen.key('r', async () => {
            if (screen.focused === this.inputBox) return;
            this._setStatus('Refreshing...');
            await this._loadConversations();
        });

        // Escape depuis l'inputBox : revenir à la liste
        this.inputBox.key('escape', () => {
            this.inputBox.clearValue();
            this.convList.focus();
            screen.render();
        });

        this.inputBox.on('submit', async (text) => {
            text = (text || '').trim();
            this.inputBox.clearValue();
            screen.render();
            if (text) await this._sendToSelected(text);
            this.inputBox.readInput(() => {});
        });
    }

    async _promptNewConversation() {
        const screen = this.screen;

        const form = blessed.form({
            parent: screen,
            top: 'center', left: 'center', width: 62, height: 7,
            label: ' New Conversation ',
            border: { type: 'line' },
            keys: true, mouse: true,
            style: { border: { fg: 'yellow' }, label: { fg: 'yellow' } }
        });

        blessed.text({
            parent: form, top: 1, left: 2,
            content: 'Peer user ID or email:',
            style: { fg: 'white' }
        });

        const input = blessed.textbox({
            parent: form,
            top: 2, left: 2, width: form.width - 6, height: 1,
            mouse: true,
            style: { fg: 'white', bg: '#222', focus: { bg: '#333' } }
        });

        blessed.text({
            parent: form, top: 4, left: 2,
            content: '[Enter] confirm  [Esc] cancel',
            style: { fg: 'gray' }
        });

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

        input.on('submit', async (peer) => {
            peer = (peer || '').trim();
            close();
            if (!peer) return;
            this._setStatus(`Creating conversation with ${peer}...`);
            try {
                const conv = await this._onCreateConversation(peer);
                if (conv?.id) {
                    this._setStatus(`Conversation created: ${conv.id}`);
                    await this._loadConversations();
                } else {
                    this._setStatus('Failed: no ID in response');
                }
            } catch (e) {
                this._setStatus(`Error: ${e}`);
            }
        });

        input.on('cancel', close);

        form.append(input);
        input.focus();
        input.readInput(() => {});
        screen.render();
    }

    async _sendToSelected(text) {
        const conv = this.conversations[this.selectedConvIndex];
        if (!conv) {
            this._setStatus('No conversation selected — use [c] to create one');
            return;
        }
        try {
            await this._onSendMessage(conv.id, text);
            const preview = text.length > 50 ? text.substring(0, 50) + '…' : text;
            this._setStatus(`Sent to ${conv.peer || conv.id}: ${preview}`);
        } catch (e) {
            this._setStatus(`Send failed: ${e}`);
        }
    }

    async _loadConversations() {
        try {
            const data = await this._onLoadConversations();
            this.conversations = Array.isArray(data) ? data : [];
            const items = this.conversations.length === 0
                ? ['(no conversations)']
                : this.conversations.map((c, i) => {
                    const peer = c.peer || c.peerId || '?';
                    const type = c.type ? ` [${c.type}]` : '';
                    return ` ${i + 1}. ${peer}${type}`;
                });
            this.convList.setItems(items);
            this._setStatus(`${this.conversations.length} conversation(s)`);
            this.screen.render();
        } catch (e) {
            this._setStatus(`Failed to load conversations: ${e}`);
        }
    }

    addEvent(event) {
        const { _receivedAt, _path, ...data } = event;
        const ts = _receivedAt
            ? new Date(_receivedAt).toLocaleTimeString()
            : new Date().toLocaleTimeString();

        // Événement de trace API : accumulation + écriture fichier + rendu TUI
        if (data.type === 'api-trace') {
            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 headersLine = data.headers
                ? `\n   {cyan-fg}hdr:{/cyan-fg}  {white-fg}${_esc(Object.entries(data.headers).map(([k, v]) => `${k}: ${v}`).join('  |  ').substring(0, 300))}{/white-fg}`
                : '';
            const reqLine = data.requestBody
                ? `\n   {cyan-fg}req:{/cyan-fg}  {white-fg}${_esc(data.requestBody.substring(0, 200))}{/white-fg}`
                : '';
            const snippetLine = data.snippet
                ? `\n   {cyan-fg}res:{/cyan-fg}  {white-fg}${_esc(data.snippet.substring(0, 200))}{/white-fg}`
                : '';
            const line = `{yellow-fg}[${ts}]{/yellow-fg}  {magenta-fg}${_esc(method)}{/magenta-fg}  {cyan-fg}${_esc(ep)}{/cyan-fg}  {${statusTag}}→ ${status}{/${statusTag}}${headersLine}${reqLine}${snippetLine}`;
            this.eventsBox.pushLine(line);
            this.eventsBox.pushLine('');
            this.eventsBox.setScrollPerc(100);
            this.screen.render();
            return;
        }

        // Le path HTTP donne le type directement (/message, /presence, /receipt…)
        // Pour les événements internes (sse-connected, warning…) on utilise data.type
        const kind = _path || data.type || 'event';

        // Les données spécifiques à l'événement sont imbriquées sous la clé = path
        // Ex: /presence → data.presence.{from,show,…}, /room-state → data['room-state'].{…}
        const sub = data[kind] || {};

        let summary = '';
        switch (kind) {
            case 'message': {
                const m = sub.body !== undefined ? sub : data;
                if (m.from || m.body)
                    summary = `from:${m.from || m.fromId || '?'}  ${_esc(String(m.body || ''))}`;
                break;
            }
            case 'presence': {
                const p = sub;
                if (p.from || p.show)
                    summary = `from:${p.from || '?'}${p.show ? `  show:${p.show}` : ''}${p.resource ? `  res:${_esc(p.resource)}` : ''}`;
                break;
            }
            case 'receipt': {
                const r = sub;
                if (r.event)
                    summary = `${r.event}  msg:${r.msg_id || '?'}`;
                break;
            }
            case 'chat-state':
            case 'chat_state': {
                const cs = sub.state ? sub : (data.chatstate || {});
                if (cs.state)
                    summary = `${cs.state}  peer:${cs.peer || cs.from || '?'}`;
                break;
            }
            case 'conversation': {
                const c = sub;
                if (c.action || c.id)
                    summary = `${c.action || '?'}  id:${c.id || '?'}`;
                break;
            }
            case 'room-state': {
                const r = sub;
                summary = `${r.event || '?'}  room:${r.id || '?'}  role:${r.role || '?'}`;
                break;
            }
            case 'room-member':
            case 'room': {
                const r = sub;
                if (r.id || r.user)
                    summary = `room:${r.id || '?'}  user:${r.user || '?'}  ${r.status || ''}`;
                break;
            }
            case 'user': {
                const c = sub.state ? sub : (data.connection || {});
                if (c.state)
                    summary = `state:${c.state}${c.error ? `  ${_esc(c.error)}` : ''}`;
                break;
            }
            case 'conference': {
                const e = sub.code ? sub : (data.error || {});
                if (e.code)
                    summary = `${e.code} ${_esc(e.reason || '')}`;
                break;
            }
            case 'tel': {
                const e = sub.cause ? sub : (data.event || {});
                if (e.cause)
                    summary = `cause:${e.cause}`;
                break;
            }
            default:
                if (data.message)
                    summary = _esc(String(data.message));
                break;
        }
        // Fallback : JSON brut si aucun champ connu trouvé
        if (!summary) {
            const raw = JSON.stringify(data);
            if (raw !== '{}') summary = _esc(raw.substring(0, 200));
        }

        const lines = [
            `{yellow-fg}[${ts}]{/yellow-fg}  {cyan-fg}${_esc(kind)}{/cyan-fg}${summary ? `  ${summary}` : ''}`,
            ''
        ];
        for (const line of lines) this.eventsBox.pushLine(line);
        this.eventsBox.setScrollPerc(100);
        this.screen.render();
    }

    async _promptSaveTrace() {
        if (this._traceEvents.length === 0) {
            this._setStatus('No API trace events to save.');
            return;
        }
        const screen = this.screen;

        const form = blessed.form({
            parent: screen,
            top: 'center', left: 'center', width: 62, height: 7,
            label: ' Save API Trace ',
            border: { type: 'line' },
            keys: true, mouse: true,
            style: { border: { fg: 'yellow' }, label: { fg: 'yellow' } }
        });

        blessed.text({
            parent: form, top: 1, left: 2,
            content: `Save ${this._traceEvents.length} call(s) to file:`,
            style: { fg: 'white' }
        });

        const defaultName = `ucs-trace-${new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19)}.txt`;
        const input = blessed.textbox({
            parent: form,
            top: 2, left: 2, width: form.width - 6, height: 1,
            value: defaultName,
            mouse: true,
            style: { fg: 'white', bg: '#222', focus: { bg: '#333' } }
        });

        blessed.text({
            parent: form, top: 4, left: 2,
            content: '[Enter] save  [Esc] cancel',
            style: { fg: 'gray' }
        });

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

        input.on('submit', async (filename) => {
            filename = (filename || '').trim() || defaultName;
            close();
            try {
                const { createWriteStream } = await import('node:fs');
                const stream = createWriteStream(filename, { flags: 'w' });
                stream.write(_traceFileHeader(this._cnxId, this._publicUrl));
                for (const ev of this._traceEvents) stream.write(_traceEventToText(ev));
                stream.end();
                await new Promise((res, rej) => { stream.on('finish', res); stream.on('error', rej); });
                this._setStatus(`Trace saved → ${filename}  (${this._traceEvents.length} call(s))`);
            } catch (e) {
                this._setStatus(`Save failed: ${e.message}`);
            }
        });

        input.on('cancel', close);

        form.append(input);
        input.focus();
        input.readInput(() => {});
        screen.render();
    }

    _setStatus(msg) {
        this.statusBar.setContent(` ${_esc(msg)}`);
        this.screen.render();
    }

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

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

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

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

function _esc(str) {
    return String(str).replace(/[{}]/g, c => c === '{' ? '\\{' : '\\}');
}

function _traceFileHeader(cnxId, publicUrl) {
    const now = new Date().toLocaleString('fr-FR');
    return [
        '# Rainbow UCS S2S — API Trace',
        `# Date       : ${now}`,
        `# Connection : ${cnxId}`,
        `# 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';
}
