import { AuthRequiredError, CliError } from '@jackwener/opencli/errors';
import { NOTEBOOKLM_DOMAIN } from './shared.js';

export function unwrapNotebooklmEvaluateResult(payload) {
    if (payload && typeof payload === 'object' && !Array.isArray(payload) && 'session' in payload && 'data' in payload) {
        return payload.data;
    }
    return payload;
}

export function extractNotebooklmPageAuthFromHtml(html, sourcePath = '/', preferredTokens) {
    const csrfMatch = html.match(/"SNlM0e":"([^"]+)"/);
    const sessionMatch = html.match(/"FdrFJe":"([^"]+)"/);
    const csrfToken = preferredTokens?.csrfToken?.trim() || (csrfMatch ? csrfMatch[1] : '');
    const sessionId = preferredTokens?.sessionId?.trim() || (sessionMatch ? sessionMatch[1] : '');
    if (!csrfToken || !sessionId) {
        throw new CliError('NOTEBOOKLM_TOKENS', 'NotebookLM page tokens were not found in the current page HTML', 'Open the NotebookLM notebook page in Chrome, wait for it to finish loading, then retry with --verbose if it still fails.');
    }
    return { csrfToken, sessionId, sourcePath: sourcePath || '/', authuser: preferredTokens?.authuser ?? '' };
}
async function probeNotebooklmPageAuth(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const wiz = window.WIZ_global_data || {};
    const html = document.documentElement.innerHTML;
    const authMatch = (location.search || '').match(/[?&]authuser=(\\d+)/);
    const pathMatch = (location.pathname || '').match(/^\\/u\\/(\\d+)\\//);
    return {
      html,
      sourcePath: location.pathname || '/',
      readyState: document.readyState || '',
      csrfToken: typeof wiz.SNlM0e === 'string' ? wiz.SNlM0e : '',
      sessionId: typeof wiz.FdrFJe === 'string' ? wiz.FdrFJe : '',
      authuser: authMatch ? authMatch[1] : (pathMatch ? pathMatch[1] : ''),
    };
  })()`));
    return {
        html: String(raw?.html ?? ''),
        sourcePath: String(raw?.sourcePath ?? '/'),
        readyState: String(raw?.readyState ?? ''),
        csrfToken: String(raw?.csrfToken ?? ''),
        sessionId: String(raw?.sessionId ?? ''),
        authuser: String(raw?.authuser ?? ''),
    };
}
export async function getNotebooklmPageAuth(page) {
    let lastError;
    for (let attempt = 0; attempt < 2; attempt += 1) {
        const probe = await probeNotebooklmPageAuth(page);
        try {
            return extractNotebooklmPageAuthFromHtml(probe.html, probe.sourcePath, { csrfToken: probe.csrfToken, sessionId: probe.sessionId, authuser: probe.authuser });
        }
        catch (error) {
            lastError = error;
            if (attempt === 0 && typeof page.wait === 'function') {
                await page.wait(0.5).catch(() => undefined);
                continue;
            }
        }
    }
    throw lastError;
}
export function buildNotebooklmRpcBody(rpcId, params, csrfToken) {
    const rpcRequest = [[[rpcId, JSON.stringify(params), null, 'generic']]];
    return `f.req=${encodeURIComponent(JSON.stringify(rpcRequest))}&at=${encodeURIComponent(csrfToken)}&`;
}
export function stripNotebooklmAntiXssi(rawBody) {
    if (!rawBody.startsWith(")]}'"))
        return rawBody;
    return rawBody.replace(/^\)\]\}'\r?\n/, '');
}
export function parseNotebooklmChunkedResponse(rawBody) {
    const cleaned = stripNotebooklmAntiXssi(rawBody).trim();
    if (!cleaned)
        return [];
    const lines = cleaned.split('\n');
    const chunks = [];
    for (let i = 0; i < lines.length; i += 1) {
        const line = lines[i].trim();
        if (!line)
            continue;
        if (/^\d+$/.test(line)) {
            const nextLine = lines[i + 1];
            if (!nextLine)
                continue;
            try {
                chunks.push(JSON.parse(nextLine));
            }
            catch {
                // Ignore malformed chunks and keep scanning.
            }
            i += 1;
            continue;
        }
        if (line.startsWith('[')) {
            try {
                chunks.push(JSON.parse(line));
            }
            catch {
                // Ignore malformed chunks and keep scanning.
            }
        }
    }
    return chunks;
}
export function extractNotebooklmRpcResult(rawBody, rpcId) {
    const chunks = parseNotebooklmChunkedResponse(rawBody);
    for (const chunk of chunks) {
        if (!Array.isArray(chunk))
            continue;
        const items = Array.isArray(chunk[0]) ? chunk : [chunk];
        for (const item of items) {
            if (!Array.isArray(item) || item.length < 1)
                continue;
            if (item[0] === 'er') {
                const errorCode = typeof item[2] === 'number'
                    ? item[2]
                    : typeof item[5] === 'number'
                        ? item[5]
                        : null;
                if (errorCode === 401 || errorCode === 403) {
                    throw new AuthRequiredError(NOTEBOOKLM_DOMAIN, `NotebookLM RPC returned auth error (${errorCode})`);
                }
                throw new CliError('NOTEBOOKLM_RPC', `NotebookLM RPC failed${errorCode ? ` (code=${errorCode})` : ''}`, 'Retry from an already logged-in NotebookLM session, or inspect the raw response with debug logging.');
            }
            if (item[0] === 'wrb.fr' && item[1] === rpcId) {
                const payload = item[2];
                if (typeof payload === 'string') {
                    try {
                        return JSON.parse(payload);
                    }
                    catch {
                        return payload;
                    }
                }
                return payload;
            }
        }
    }
    return null;
}
export async function fetchNotebooklmInPage(page, url, options = {}) {
    const method = options.method ?? 'GET';
    const headers = options.headers ?? {};
    const body = options.body ?? '';
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(async () => {
    const request = {
      url: ${JSON.stringify(url)},
      method: ${JSON.stringify(method)},
      headers: ${JSON.stringify(headers)},
      body: ${JSON.stringify(body)},
    };

    const response = await fetch(request.url, {
      method: request.method,
      headers: request.headers,
      body: request.method === 'GET' ? undefined : request.body,
      credentials: 'include',
    });

    return {
      ok: response.ok,
      status: response.status,
      body: await response.text(),
      finalUrl: response.url,
    };
  })()`));
    return {
        ok: Boolean(raw?.ok),
        status: Number(raw?.status ?? 0),
        body: String(raw?.body ?? ''),
        finalUrl: String(raw?.finalUrl ?? url),
    };
}
export async function callNotebooklmRpc(page, rpcId, params, options = {}) {
    const auth = await getNotebooklmPageAuth(page);
    const requestBody = buildNotebooklmRpcBody(rpcId, params, auth.csrfToken);
    const authuser = auth.authuser || '';
    const url = `https://${NOTEBOOKLM_DOMAIN}/_/LabsTailwindUi/data/batchexecute` +
        `?rpcids=${rpcId}&source-path=${encodeURIComponent(auth.sourcePath)}` +
        (authuser ? `&authuser=${encodeURIComponent(authuser)}` : '') +
        `&hl=${encodeURIComponent(options.hl ?? 'en')}` +
        `&f.sid=${encodeURIComponent(auth.sessionId)}&rt=c`;
    const response = await fetchNotebooklmInPage(page, url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        },
        body: requestBody,
    });
    if (response.status === 401 || response.status === 403) {
        throw new AuthRequiredError(NOTEBOOKLM_DOMAIN, `NotebookLM RPC returned auth error (${response.status})`);
    }
    if (!response.ok) {
        throw new CliError('NOTEBOOKLM_RPC', `NotebookLM RPC request failed with HTTP ${response.status}`, 'Retry from the NotebookLM home page in an already logged-in Chrome session.');
    }
    return {
        auth,
        url,
        requestBody,
        response,
        result: extractNotebooklmRpcResult(response.body, rpcId),
    };
}
