/**
 * useTakeover — Token management + dev command dispatch
 *
 * Single owner of takeover token lifecycle and command registry.
 * Delegates capture/breakout commands to their composables.
 */

import { ref } from 'vue';
import { useCapture } from './useCapture';
import { useBreakout } from './useBreakout';
import { useHermes } from './useHermes';

const TAKEOVER_KEY = 'hermes_takeover_token';

// ── Module-level (survives HMR) ──
let currentToken = sessionStorage.getItem(TAKEOVER_KEY) || '';

export function useTakeover(wsSend: (msg: any) => void) {
    const token = ref(currentToken);
    const capture = useCapture();
    const breakout = useBreakout();

    // ── Token lifecycle (single place) ──

    function init(): string {
        const t = crypto.randomUUID();
        token.value = t;
        currentToken = t;
        sessionStorage.setItem(TAKEOVER_KEY, t);
        wsSend({ type: 'dev_takeover', token: t });
        return t;
    }

    function revoke() {
        token.value = '';
        currentToken = '';
        sessionStorage.removeItem(TAKEOVER_KEY);
    }

    /** Called once from ws.ts onopen — single re-registration point */
    function reregister() {
        // Also handle breakout token from URL (popup window startup)
        const urlParams = new URLSearchParams(window.location.search);
        const breakoutToken = urlParams.get('breakout_token');
        if (breakoutToken) {
            sessionStorage.setItem(TAKEOVER_KEY, breakoutToken);
            urlParams.delete('breakout_token');
            const clean = urlParams.toString();
            const newUrl = window.location.pathname + (clean ? '?' + clean : '') + window.location.hash;
            window.history.replaceState(null, '', newUrl);
        }
        const t = sessionStorage.getItem(TAKEOVER_KEY);
        if (t) {
            token.value = t;
            currentToken = t;
            wsSend({ type: 'dev_takeover', token: t });
        }
    }

    // ── DOM command implementations ──

    function boxChain(args: { selector: string }) {
        const el = document.querySelector(args.selector);
        if (!el) return { error: `No element: ${args.selector}` };
        const chain: any[] = [];
        let node: HTMLElement | null = el as HTMLElement;
        while (node && node !== document.documentElement) {
            const cs = getComputedStyle(node);
            const rect = node.getBoundingClientRect();
            chain.push({
                tag: node.tagName.toLowerCase(),
                cls: (node.className?.toString() || '').split(' ').filter(Boolean).slice(0, 5).join(' '),
                w: Math.round(rect.width), h: Math.round(rect.height),
                l: Math.round(rect.left), r: Math.round(rect.right),
                pl: cs.paddingLeft, pr: cs.paddingRight,
                ml: cs.marginLeft, mr: cs.marginRight,
            });
            node = node.parentElement;
        }
        return chain;
    }

    function getStyles(args: { selector: string; props?: string[] }) {
        const el = document.querySelector(args.selector) as HTMLElement | null;
        if (!el) return { error: `No element: ${args.selector}` };
        const cs = getComputedStyle(el);
        const rect = el.getBoundingClientRect();
        const props = args.props || ['padding', 'margin', 'width', 'height', 'display', 'flexDirection', 'overflow', 'gap'];
        const styles: Record<string, string> = {};
        for (const p of props) styles[p] = cs.getPropertyValue(p.replace(/[A-Z]/g, m => '-' + m.toLowerCase()));
        return { ...styles, boundingRect: { w: Math.round(rect.width), h: Math.round(rect.height), l: Math.round(rect.left), r: Math.round(rect.right), t: Math.round(rect.top), b: Math.round(rect.bottom) } };
    }

    function viewport() {
        return { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio, hash: window.location.hash };
    }

    function navigate(args: { hash: string }) {
        window.location.hash = args.hash;
        return { navigated: args.hash };
    }

    function reload() {
        setTimeout(() => window.location.reload(), 100);
        return { reloading: true };
    }

    function resize(args: { w: number; h: number }) {
        window.resizeTo(args.w, args.h);
        return { resized: `${args.w}x${args.h}` };
    }

    function querySelector(args: { selector: string; limit?: number }) {
        const els = document.querySelectorAll(args.selector);
        const limit = args.limit || 10;
        return Array.from(els).slice(0, limit).map((el, i) => {
            const rect = (el as HTMLElement).getBoundingClientRect();
            return {
                i, tag: el.tagName.toLowerCase(),
                cls: (el.className?.toString() || '').split(' ').filter(Boolean).slice(0, 5).join(' '),
                text: (el.textContent || '').slice(0, 60),
                w: Math.round(rect.width), h: Math.round(rect.height),
                l: Math.round(rect.left), t: Math.round(rect.top),
            };
        });
    }

    function click(args: { selector: string; index?: number }) {
        const els = document.querySelectorAll(args.selector);
        const idx = args.index || 0;
        const el = els[idx] as HTMLElement | null;
        if (!el) return { error: `No element: ${args.selector}[${idx}]` };
        el.click();
        return { clicked: args.selector, index: idx };
    }

    function getValue(args: { selector: string }) {
        const el = document.querySelector(args.selector) as HTMLInputElement | HTMLSelectElement | null;
        if (!el) return { error: `No element: ${args.selector}` };
        return { value: el.value, tag: el.tagName.toLowerCase() };
    }

    function setValue(args: { selector: string; value: string }) {
        const el = document.querySelector(args.selector) as HTMLInputElement | HTMLSelectElement | null;
        if (!el) return { error: `No element: ${args.selector}` };
        const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
            el.tagName === 'SELECT' ? HTMLSelectElement.prototype : HTMLInputElement.prototype, 'value'
        )?.set;
        nativeInputValueSetter?.call(el, args.value);
        el.dispatchEvent(new Event('input', { bubbles: true }));
        el.dispatchEvent(new Event('change', { bubbles: true }));
        return { set: args.value, selector: args.selector };
    }

    function typeText(args: { selector: string; text: string }) {
        const el = document.querySelector(args.selector) as HTMLInputElement | HTMLTextAreaElement | null;
        if (!el) return { error: `No element: ${args.selector}` };
        el.focus();
        el.value = args.text;
        el.dispatchEvent(new Event('input', { bubbles: true }));
        return { typed: args.text, selector: args.selector };
    }

    function screenshot() {
        return {
            url: window.location.hash,
            title: document.title,
            viewport: { w: window.innerWidth, h: window.innerHeight },
            body: document.body.innerText.slice(0, 2000),
        };
    }

    function scroll(args: { selector: string; to?: number | 'top' | 'bottom' | 'middle' }) {
        const el = document.querySelector(args.selector) as HTMLElement | null;
        if (!el) return { error: `No element: ${args.selector}` };
        const before = el.scrollTop;
        if (args.to !== undefined) {
            if (args.to === 'top') el.scrollTop = 0;
            else if (args.to === 'bottom') el.scrollTop = el.scrollHeight;
            else if (args.to === 'middle') el.scrollTop = (el.scrollHeight - el.clientHeight) / 2;
            else el.scrollTop = args.to;
        }
        return {
            scrollTop: el.scrollTop,
            scrollHeight: el.scrollHeight,
            clientHeight: el.clientHeight,
            before,
        };
    }

    // ── Command registry ──

    function getConsole(args: { last?: number; level?: string; pattern?: string; clear?: boolean }) {
        const h = useHermes();
        if (!h.console?.length && !h._origConsole) return { error: 'Console hook not initialized' };
        let entries = h.console as any[];
        if (args.level) entries = entries.filter((e: any) => e.l === args.level);
        if (args.pattern) {
            const re = new RegExp(args.pattern, 'i');
            entries = entries.filter((e: any) => re.test(e.m));
        }
        const result = entries.slice(-(args.last || 50));
        if (args.clear) h.console.length = 0;
        return result;
    }

    const autoCommands: Record<string, (args: any) => any> = {
        boxChain, getStyles, viewport, navigate, reload, resize, querySelector, click, screenshot,
        getValue, setValue, typeText, scroll, getConsole,
        listBreakouts: () => breakout.list(),
        closeBreakout: (args: any) => breakout.close(args),
    };

    const asyncCommands: Record<string, (args: any) => Promise<any>> = {
        captureScreen: (args: any) => capture.capture(args.quality),
        enableCapture: () => capture.enable(),
        openBreakout: (args: any) => breakout.open(args),
    };

    /** Dispatch a dev command — called by ws.ts on dev_cmd message */
    function dispatch(cmdId: string, cmd: string, args: any, sendResult: (msg: any) => void) {
        const fn = autoCommands[cmd];
        if (fn) {
            try {
                const result = fn(args);
                sendResult({ type: 'dev_cmd_result', cmdId, result: JSON.parse(JSON.stringify(result ?? null)) });
            } catch (err: any) {
                sendResult({ type: 'dev_cmd_result', cmdId, error: err.message });
            }
            return;
        }

        const asyncFn = asyncCommands[cmd];
        if (asyncFn) {
            asyncFn(args).then(result => {
                sendResult({ type: 'dev_cmd_result', cmdId, result: JSON.parse(JSON.stringify(result ?? null)) });
            }).catch((err: any) => {
                sendResult({ type: 'dev_cmd_result', cmdId, error: err.message });
            });
            return;
        }

        // eval — requires user confirmation
        if (cmd === 'eval') {
            const js = args.js;
            if (!js) { sendResult({ type: 'dev_cmd_result', cmdId, error: 'js required' }); return; }
            const ok = window.confirm(`Dev takeover eval request:\n\n${js.slice(0, 500)}\n\nAllow?`);
            if (!ok) { sendResult({ type: 'dev_cmd_result', cmdId, error: 'rejected by user' }); return; }
            try {
                const result = new Function('return (' + js + ')')();
                const serialized = result instanceof Element
                    ? result.outerHTML.slice(0, 500)
                    : JSON.parse(JSON.stringify(result ?? null));
                sendResult({ type: 'dev_cmd_result', cmdId, result: serialized });
            } catch (err: any) {
                sendResult({ type: 'dev_cmd_result', cmdId, error: err.message });
            }
            return;
        }

        sendResult({ type: 'dev_cmd_result', cmdId, error: `Unknown command: ${cmd}` });
    }

    return {
        token, capture, breakout,
        init, revoke, reregister, dispatch,
    };
}
