import { ArgumentError, AuthRequiredError, CliError, CommandExecutionError } from '@jackwener/opencli/errors';
import { NOTEBOOKLM_DOMAIN, NOTEBOOKLM_HOME_URL, } from './shared.js';
import { callNotebooklmRpc, getNotebooklmPageAuth, unwrapNotebooklmEvaluateResult, } from './rpc.js';
export { buildNotebooklmRpcBody, extractNotebooklmRpcResult, fetchNotebooklmInPage, getNotebooklmPageAuth, parseNotebooklmChunkedResponse, stripNotebooklmAntiXssi, } from './rpc.js';
const NOTEBOOKLM_LIST_RPC_ID = 'wXbhsf';
const NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID = 'rLM1Ne';
const NOTEBOOKLM_HISTORY_THREADS_RPC_ID = 'hPTbtc';
const NOTEBOOKLM_HISTORY_DETAIL_RPC_ID = 'khqZz';
function unwrapNotebooklmSingletonResult(result) {
    let current = result;
    while (Array.isArray(current) && current.length === 1 && Array.isArray(current[0])) {
        current = current[0];
    }
    return current;
}
export function isPlainObject(value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
}
export function parseNotebooklmIdFromUrl(url) {
    const match = url.match(/\/notebook\/([^/?#]+)/);
    return match?.[1] ?? '';
}
const NOTEBOOK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function ensureNotebookUuid(candidate) {
    if (!NOTEBOOK_UUID_RE.test(candidate)) {
        throw new CliError('NOTEBOOKLM_INVALID_NOTEBOOK', `NotebookLM notebook id "${candidate}" is not a valid UUID`, 'Pass a notebook id from `opencli notebooklm list` or a full notebook URL like https://notebooklm.google.com/notebook/<uuid>.');
    }
    return candidate;
}
export function parseNotebooklmNotebookTarget(value) {
    const normalized = value.trim();
    if (!normalized) {
        throw new CliError('NOTEBOOKLM_INVALID_NOTEBOOK', 'NotebookLM notebook id is required', 'Pass a notebook id from `opencli notebooklm list` or a full notebook URL.');
    }
    if (/^https?:\/\//i.test(normalized)) {
        let parsed;
        try {
            parsed = new URL(normalized);
        }
        catch {
            throw new CliError('NOTEBOOKLM_INVALID_NOTEBOOK', 'NotebookLM notebook URL is invalid', 'Pass a full NotebookLM notebook URL like https://notebooklm.google.com/notebook/<uuid>.');
        }
        if (parsed.protocol !== 'https:' || parsed.hostname !== NOTEBOOKLM_DOMAIN || parsed.username || parsed.password || parsed.port) {
            throw new CliError('NOTEBOOKLM_INVALID_NOTEBOOK', 'NotebookLM notebook URL must be a canonical https://notebooklm.google.com URL', 'Pass a notebook id from `opencli notebooklm list` or a full NotebookLM notebook URL.');
        }
        const notebookId = parseNotebooklmIdFromUrl(normalized);
        if (!notebookId) {
            throw new CliError('NOTEBOOKLM_INVALID_NOTEBOOK', 'NotebookLM notebook URL is invalid', 'Pass a full NotebookLM notebook URL like https://notebooklm.google.com/notebook/<uuid>.');
        }
        return ensureNotebookUuid(notebookId);
    }
    const pathMatch = normalized.match(/(?:^|\/)notebook\/([^/?#]+)/);
    if (pathMatch?.[1])
        return ensureNotebookUuid(pathMatch[1]);
    return ensureNotebookUuid(normalized);
}
export function getNotebooklmAuthuser() {
    const v = process.env.OPENCLI_NOTEBOOKLM_AUTHUSER;
    return typeof v === 'string' && /^\d+$/.test(v) ? v : '';
}
export function requireNotebooklmExecute(value, action) {
    if (value !== true) {
        throw new ArgumentError(`Refusing to ${action}: pass --execute to perform this NotebookLM write`);
    }
}
export function buildNotebooklmNotebookUrl(notebookId) {
    const u = new URL(`/notebook/${encodeURIComponent(notebookId)}`, NOTEBOOKLM_HOME_URL);
    const authuser = getNotebooklmAuthuser();
    if (authuser) u.searchParams.set('authuser', authuser);
    return u.toString();
}
export function classifyNotebooklmPage(url) {
    try {
        const parsed = new URL(url);
        if (parsed.hostname !== NOTEBOOKLM_DOMAIN)
            return 'unknown';
        if (/\/notebook\/[^/?#]+/.test(parsed.pathname))
            return 'notebook';
        return 'home';
    }
    catch {
        return 'unknown';
    }
}
export function normalizeNotebooklmTitle(value, fallback = '') {
    if (typeof value !== 'string')
        return fallback;
    let normalized = value.replace(/\s+/g, ' ').trim();
    if (/^Untitled\b/i.test(normalized) && /otebook$/i.test(normalized) && normalized !== 'Untitled notebook') {
        normalized = 'Untitled notebook';
    }
    return normalized || fallback;
}
function normalizeNotebooklmCreatedAt(value) {
    if (typeof value !== 'string')
        return null;
    const trimmed = value.trim();
    if (!trimmed)
        return null;
    const parsed = Date.parse(trimmed);
    if (Number.isNaN(parsed))
        return trimmed;
    return new Date(parsed).toISOString();
}
function toNotebooklmIsoTimestamp(epochSeconds) {
    if (typeof epochSeconds === 'number' && Number.isFinite(epochSeconds)) {
        try {
            return new Date(epochSeconds * 1000).toISOString();
        }
        catch {
            return null;
        }
    }
    if (Array.isArray(epochSeconds) && typeof epochSeconds[0] === 'number' && Number.isFinite(epochSeconds[0])) {
        const seconds = epochSeconds[0];
        const nanos = typeof epochSeconds[1] === 'number' && Number.isFinite(epochSeconds[1]) ? epochSeconds[1] : 0;
        try {
            return new Date(seconds * 1000 + Math.floor(nanos / 1_000_000)).toISOString();
        }
        catch {
            return null;
        }
    }
    return null;
}
function parseNotebooklmSourceTypeCode(value) {
    if (typeof value === 'number' && Number.isFinite(value))
        return value;
    if (!Array.isArray(value) || typeof value[1] !== 'number' || !Number.isFinite(value[1]))
        return null;
    return value[1];
}
function parseNotebooklmSourceType(value) {
    const code = parseNotebooklmSourceTypeCode(value);
    if (code === 8)
        return 'pasted-text';
    if (code === 9)
        return 'youtube';
    if (code === 2)
        return 'generated-text';
    if (code === 3)
        return 'pdf';
    if (code === 4)
        return 'audio';
    if (code === 5)
        return 'web';
    if (code === 6)
        return 'video';
    return code == null ? null : `type-${code}`;
}
function findFirstNotebooklmString(value) {
    if (typeof value === 'string' && value.trim())
        return value.trim();
    if (!Array.isArray(value))
        return null;
    for (const item of value) {
        const found = findFirstNotebooklmString(item);
        if (found)
            return found;
    }
    return null;
}
function isNotebooklmUuid(value) {
    return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
}
function collectNotebooklmStrings(value, results) {
    if (typeof value === 'string') {
        const normalized = normalizeNotebooklmTitle(value);
        if (!normalized)
            return results;
        if (isNotebooklmUuid(normalized))
            return results;
        if (/^[\d\s]+$/.test(normalized))
            return results;
        if (/^(null|undefined)$/i.test(normalized))
            return results;
        results.push(normalized);
        return results;
    }
    if (!Array.isArray(value))
        return results;
    for (const item of value)
        collectNotebooklmStrings(item, results);
    return results;
}
function collectNotebooklmLeafStrings(value, results) {
    if (typeof value === 'string') {
        const normalized = value.trim();
        if (normalized)
            results.push(normalized);
        return results;
    }
    if (!Array.isArray(value))
        return results;
    for (const item of value)
        collectNotebooklmLeafStrings(item, results);
    return results;
}
function collectNotebooklmThreadIds(value, results, seen) {
    if (typeof value === 'string') {
        const normalized = value.trim();
        if (isNotebooklmUuid(normalized) && !seen.has(normalized)) {
            seen.add(normalized);
            results.push(normalized);
        }
        return results;
    }
    if (!Array.isArray(value))
        return results;
    for (const item of value)
        collectNotebooklmThreadIds(item, results, seen);
    return results;
}
export function parseNotebooklmHistoryThreadIdsResult(result) {
    return collectNotebooklmThreadIds(result, [], new Set());
}
export function extractNotebooklmHistoryPreview(result) {
    const strings = collectNotebooklmStrings(result, []);
    return strings.length > 0 ? strings[0] : null;
}
export function parseNotebooklmNoteListRawRows(rows, notebookId, url) {
    const parsed = rows.map((row) => {
        const title = normalizeNotebooklmTitle(row.title, '');
        const text = String(row.text ?? '')
            .replace(/\bsticky_note_2\b/g, ' ')
            .replace(/\bmore_vert\b/g, ' ')
            .replace(/\s+/g, ' ')
            .trim();
        if (!title)
            return null;
        const suffix = text.startsWith(title)
            ? text.slice(title.length).trim()
            : text.replace(title, '').trim();
        return {
            notebook_id: notebookId,
            title,
            created_at: suffix || null,
            url,
            source: 'studio-list',
        };
    });
    return parsed.filter((row) => row !== null);
}
function parseNotebooklmSummaryRawRow(row, notebookId, url) {
    const title = normalizeNotebooklmTitle(row?.title, 'Untitled Notebook');
    const summary = String(row?.summary ?? '').trim();
    if (!summary)
        return null;
    return {
        notebook_id: notebookId,
        title,
        summary,
        url,
        source: 'summary-dom',
    };
}
function parseNotebooklmVisibleNoteRawRow(row, notebookId, url) {
    const title = normalizeNotebooklmTitle(row?.title, '');
    const content = String(row?.content ?? '').replace(/\r\n/g, '\n').trim();
    if (!title)
        return null;
    return {
        notebook_id: notebookId,
        id: null,
        title,
        content,
        url,
        source: 'studio-editor',
    };
}
export function parseNotebooklmListResult(result) {
    if (!Array.isArray(result) || result.length === 0)
        return [];
    const rawNotebooks = Array.isArray(result[0]) ? result[0] : result;
    if (!Array.isArray(rawNotebooks))
        return [];
    return rawNotebooks
        .filter((item) => Array.isArray(item))
        .map((item) => {
        const meta = Array.isArray(item[5]) ? item[5] : [];
        const timestamps = Array.isArray(meta[5]) ? meta[5] : [];
        const id = typeof item[2] === 'string' ? item[2] : '';
        const title = typeof item[0] === 'string'
            ? item[0].replace(/^thought\s*\n/, '')
            : '';
        return {
            id,
            title: normalizeNotebooklmTitle(title, 'Untitled Notebook'),
            url: `https://${NOTEBOOKLM_DOMAIN}/notebook/${id}`,
            source: 'rpc',
            is_owner: meta.length > 1 ? meta[1] === false : true,
            created_at: timestamps.length > 0 ? toNotebooklmIsoTimestamp(timestamps[0]) : null,
        };
    })
        .filter((row) => row.id);
}
export function parseNotebooklmNotebookDetailResult(result) {
    const detail = unwrapNotebooklmSingletonResult(result);
    if (!Array.isArray(detail) || detail.length < 3)
        return null;
    const id = typeof detail[2] === 'string' ? detail[2] : '';
    if (!id)
        return null;
    const title = normalizeNotebooklmTitle(detail[0], 'Untitled Notebook');
    const emoji = typeof detail[3] === 'string' ? detail[3] : null;
    const meta = Array.isArray(detail[5]) ? detail[5] : [];
    const sources = Array.isArray(detail[1]) ? detail[1] : [];
    return {
        id,
        title,
        url: `https://${NOTEBOOKLM_DOMAIN}/notebook/${id}`,
        source: 'rpc',
        is_owner: meta.length > 1 ? meta[1] === false : true,
        created_at: toNotebooklmIsoTimestamp(meta[8]),
        updated_at: toNotebooklmIsoTimestamp(meta[5]),
        emoji,
        source_count: sources.length,
    };
}
export function parseNotebooklmSourceListResult(result) {
    const detail = unwrapNotebooklmSingletonResult(result);
    const notebook = parseNotebooklmNotebookDetailResult(detail);
    if (!notebook || !Array.isArray(detail))
        return [];
    const rawSources = Array.isArray(detail[1]) ? detail[1] : [];
    return rawSources
        .filter((entry) => Array.isArray(entry))
        .map((entry) => {
        const id = findFirstNotebooklmString(entry[0]) ?? '';
        const title = normalizeNotebooklmTitle(entry[1], 'Untitled source');
        const meta = Array.isArray(entry[2]) ? entry[2] : [];
        const typeInfo = typeof meta[4] === 'number' ? meta[4] : entry[3];
        return {
            id,
            notebook_id: notebook.id,
            title,
            url: notebook.url,
            source: 'rpc',
            type: parseNotebooklmSourceType(typeInfo),
            type_code: parseNotebooklmSourceTypeCode(typeInfo),
            size: typeof meta[1] === 'number' && Number.isFinite(meta[1]) ? meta[1] : null,
            created_at: toNotebooklmIsoTimestamp(meta[2]),
            updated_at: toNotebooklmIsoTimestamp(meta[14]),
        };
    })
        .filter((row) => row.id);
}
export function parseNotebooklmSourceGuideResult(result, source) {
    if (!Array.isArray(result) || result.length === 0 || !Array.isArray(result[0]))
        return null;
    const outer = result[0];
    const guide = Array.isArray(outer) && outer.length > 0 && Array.isArray(outer[0])
        ? outer[0]
        : outer;
    if (!Array.isArray(guide))
        return null;
    const summary = Array.isArray(guide[1]) && typeof guide[1][0] === 'string'
        ? guide[1][0].trim()
        : '';
    const keywords = Array.isArray(guide[2]) && Array.isArray(guide[2][0])
        ? guide[2][0].filter((item) => typeof item === 'string' && item.trim().length > 0)
        : [];
    if (!summary)
        return null;
    return {
        source_id: source.id,
        notebook_id: source.notebook_id,
        title: source.title,
        type: source.type ?? null,
        summary,
        keywords,
        source: 'rpc',
    };
}
export function parseNotebooklmSourceFulltextResult(result, notebookId, fallbackUrl) {
    if (!Array.isArray(result) || result.length === 0 || !Array.isArray(result[0]))
        return null;
    const source = result[0];
    const sourceId = findFirstNotebooklmString(source[0]) ?? '';
    const title = normalizeNotebooklmTitle(source[1], 'Untitled source');
    const meta = Array.isArray(source[2]) ? source[2] : [];
    const url = Array.isArray(meta[7]) && typeof meta[7][0] === 'string' ? meta[7][0] : fallbackUrl;
    const kind = parseNotebooklmSourceType([null, meta[4]]);
    const contentRoot = Array.isArray(result[3]) && result[3].length > 0 ? result[3][0] : [];
    const content = collectNotebooklmLeafStrings(contentRoot, []).join('\n').trim();
    if (!sourceId || !content)
        return null;
    return {
        source_id: sourceId,
        notebook_id: notebookId,
        title,
        kind,
        content,
        char_count: content.length,
        url,
        source: 'rpc',
    };
}
export function findNotebooklmSourceRow(rows, query) {
    const needle = query.trim().toLowerCase();
    if (!needle)
        return null;
    const exactId = rows.find((row) => row.id.trim().toLowerCase() === needle);
    if (exactId)
        return exactId;
    const exactTitle = rows.find((row) => row.title.trim().toLowerCase() === needle);
    if (exactTitle)
        return exactTitle;
    const partialMatches = rows.filter((row) => row.title.trim().toLowerCase().includes(needle));
    if (partialMatches.length === 1)
        return partialMatches[0];
    return null;
}
export function findNotebooklmNoteRow(rows, query) {
    const needle = query.trim().toLowerCase();
    if (!needle)
        return null;
    const exactTitle = rows.find((row) => row.title.trim().toLowerCase() === needle);
    if (exactTitle)
        return exactTitle;
    const partialMatches = rows.filter((row) => row.title.trim().toLowerCase().includes(needle));
    if (partialMatches.length === 1)
        return partialMatches[0];
    return null;
}
export async function listNotebooklmViaRpc(page) {
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_LIST_RPC_ID, [null, 1, null, [2]]);
    return parseNotebooklmListResult(rpc.result);
}
export async function getNotebooklmDetailViaRpc(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return null;
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [state.notebookId, null, [2], null, 0]);
    return parseNotebooklmNotebookDetailResult(rpc.result);
}
export async function getNotebooklmNotebookDetailById(page, notebookId) {
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [notebookId, null, [2], null, 0]);
    return { detail: parseNotebooklmNotebookDetailResult(rpc.result), sources: parseNotebooklmSourceListResult(rpc.result) };
}
export async function verifyNotebooklmNotebookExists(page, notebookId, action) {
    try {
        const { detail } = await getNotebooklmNotebookDetailById(page, notebookId);
        if (!detail || detail.id !== notebookId) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but the notebook ${notebookId} was not found in the post-write verification`);
        }
        return detail;
    }
    catch (error) {
        if (error instanceof AuthRequiredError || error instanceof CommandExecutionError)
            throw error;
        throw new CommandExecutionError(`NotebookLM ${action} post-write verification failed: ${error?.message || error}`);
    }
}
export async function verifyNotebooklmSourceAdded(page, notebookId, sourceId, action) {
    try {
        const { detail, sources } = await getNotebooklmNotebookDetailById(page, notebookId);
        if (!detail || detail.id !== notebookId) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but the notebook ${notebookId} was not found in the post-write verification`);
        }
        const matched = sources.find((s) => s.id === sourceId);
        if (!matched) {
            throw new CommandExecutionError(`NotebookLM ${action} succeeded but source ${sourceId} did not appear in the notebook's source list`);
        }
        return matched;
    }
    catch (error) {
        if (error instanceof AuthRequiredError || error instanceof CommandExecutionError)
            throw error;
        throw new CommandExecutionError(`NotebookLM ${action} post-write verification failed: ${error?.message || error}`);
    }
}
export async function listNotebooklmSourcesViaRpc(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return [];
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [state.notebookId, null, [2], null, 0]);
    return parseNotebooklmSourceListResult(rpc.result);
}
export async function listNotebooklmHistoryViaRpc(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return [];
    const threadsRpc = await callNotebooklmRpc(page, NOTEBOOKLM_HISTORY_THREADS_RPC_ID, [[], null, state.notebookId, 20]);
    const threadIds = parseNotebooklmHistoryThreadIdsResult(threadsRpc.result);
    if (threadIds.length === 0)
        return [];
    const rows = [];
    for (const threadId of threadIds) {
        const detailRpc = await callNotebooklmRpc(page, NOTEBOOKLM_HISTORY_DETAIL_RPC_ID, [[], null, null, threadId, 20]);
        rows.push({
            notebook_id: state.notebookId,
            thread_id: threadId,
            item_count: Array.isArray(detailRpc.result) ? detailRpc.result.length : 0,
            preview: extractNotebooklmHistoryPreview(detailRpc.result),
            url: state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`,
            source: 'rpc',
        });
    }
    return rows;
}
export async function listNotebooklmNotesFromPage(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return [];
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    return Array.from(document.querySelectorAll('artifact-library-note')).map((node) => {
      const titleNode = node.querySelector('.artifact-title');
      return {
        title: (titleNode?.textContent || '').trim(),
        text: (node.innerText || node.textContent || '').replace(/\\s+/g, ' ').trim(),
      };
    });
  })()`));
    if (!Array.isArray(raw) || raw.length === 0)
        return [];
    return parseNotebooklmNoteListRawRows(raw, state.notebookId, state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`);
}
export async function readNotebooklmSummaryFromPage(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return null;
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
    const title = normalize(document.querySelector('.notebook-title, h1, [data-testid="notebook-title"]')?.textContent || document.title || '');
    const summaryNode = document.querySelector('.notebook-summary, .summary-content, [class*="summary"]');
    const summary = normalize(summaryNode?.textContent || '');
    return { title, summary };
  })()`));
    return parseNotebooklmSummaryRawRow(raw, state.notebookId, state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`);
}
export async function getNotebooklmSummaryViaRpc(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return null;
    const rpc = await callNotebooklmRpc(page, NOTEBOOKLM_NOTEBOOK_DETAIL_RPC_ID, [state.notebookId, null, [2], null, 0]);
    const detail = unwrapNotebooklmSingletonResult(rpc.result);
    if (!Array.isArray(detail))
        return null;
    const title = normalizeNotebooklmTitle(detail[0], 'Untitled Notebook');
    const summary = detail
        .filter((value, index) => index !== 0 && index !== 2 && index !== 3)
        .find((value) => typeof value === 'string' && value.trim().length >= 80);
    if (typeof summary !== 'string')
        return null;
    return {
        notebook_id: state.notebookId,
        title,
        summary: summary.trim(),
        url: state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`,
        source: 'rpc',
    };
}
export async function getNotebooklmSourceFulltextViaRpc(page, sourceId) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId || !sourceId)
        return null;
    const rpc = await callNotebooklmRpc(page, 'hizoJc', [[sourceId], [2], [2]]);
    return parseNotebooklmSourceFulltextResult(rpc.result, state.notebookId, state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`);
}
export async function getNotebooklmSourceGuideViaRpc(page, source) {
    if (!source.id)
        return null;
    const rpc = await callNotebooklmRpc(page, 'tr032e', [[[[source.id]]]]);
    return parseNotebooklmSourceGuideResult(rpc.result, source);
}
export async function readNotebooklmVisibleNoteFromPage(page) {
    const state = await getNotebooklmPageState(page);
    if (state.kind !== 'notebook' || !state.notebookId)
        return null;
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const normalizeText = (value) => (value || '').replace(/\\u00a0/g, ' ').replace(/\\r\\n/g, '\\n').trim();
    const titleNode = document.querySelector('.note-header__editable-title');
    const title = titleNode instanceof HTMLInputElement || titleNode instanceof HTMLTextAreaElement
      ? titleNode.value
      : (titleNode?.textContent || '');
    const editor = document.querySelector('.note-editor .ql-editor, .note-editor [contenteditable="true"], .note-editor textarea');
    let content = '';
    if (editor instanceof HTMLTextAreaElement || editor instanceof HTMLInputElement) {
      content = editor.value || '';
    } else if (editor) {
      content = editor.innerText || editor.textContent || '';
    }
    return {
      title: normalizeText(title),
      content: normalizeText(content),
    };
  })()`));
    return parseNotebooklmVisibleNoteRawRow(raw, state.notebookId, state.url || `https://${NOTEBOOKLM_DOMAIN}/notebook/${state.notebookId}`);
}
export async function ensureNotebooklmHome(page) {
    const currentUrl = page.getCurrentUrl
        ? await page.getCurrentUrl().catch(() => null)
        : null;
    const currentKind = currentUrl ? classifyNotebooklmPage(currentUrl) : 'unknown';
    if (currentKind === 'home')
        return;
    const authuser = getNotebooklmAuthuser();
    const target = authuser ? `${NOTEBOOKLM_HOME_URL}?authuser=${encodeURIComponent(authuser)}` : NOTEBOOKLM_HOME_URL;
    try {
        await page.goto(target);
        await page.wait(2);
    }
    catch (error) {
        throw new CommandExecutionError(`Failed to open NotebookLM home: ${error?.message || error}`);
    }
}
export async function getNotebooklmPageState(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const url = window.location.href;
    const title = document.title || '';
    const hostname = window.location.hostname || '';
    const notebookMatch = url.match(/\\/notebook\\/([^/?#]+)/);
    const notebookId = notebookMatch ? notebookMatch[1] : '';
    const path = window.location.pathname || '/';
    const kind = notebookId
      ? 'notebook'
      : (hostname === 'notebooklm.google.com' ? 'home' : 'unknown');

    const textNodes = Array.from(document.querySelectorAll('a, button, [role="button"], h1, h2'))
      .map(node => (node.textContent || '').trim().toLowerCase())
      .filter(Boolean);
    const loginRequired = textNodes.some(text =>
      text.includes('sign in') ||
      text.includes('log in') ||
      text.includes('登录') ||
      text.includes('登入')
    );

    const notebookCount = Array.from(document.querySelectorAll('a[href*="/notebook/"]'))
      .map(node => node instanceof HTMLAnchorElement ? node.href : '')
      .filter(Boolean)
      .reduce((count, href, index, list) => list.indexOf(href) === index ? count + 1 : count, 0);

    return { url, title, hostname, kind, notebookId, loginRequired, notebookCount, path };
  })()`));
    const state = {
        url: String(raw?.url ?? ''),
        title: normalizeNotebooklmTitle(raw?.title, 'NotebookLM'),
        hostname: String(raw?.hostname ?? ''),
        kind: raw?.kind === 'notebook' || raw?.kind === 'home' ? raw.kind : 'unknown',
        notebookId: String(raw?.notebookId ?? ''),
        loginRequired: Boolean(raw?.loginRequired),
        notebookCount: Number(raw?.notebookCount ?? 0),
    };
    // Notebook pages can still contain "sign in" or login-related text fragments
    // even when the active Google session is valid. Prefer the real page tokens
    // as the stronger auth signal before declaring the session unauthenticated.
    if (state.hostname === NOTEBOOKLM_DOMAIN && state.loginRequired) {
        try {
            await getNotebooklmPageAuth(page);
            state.loginRequired = false;
        }
        catch {
            // Keep the heuristic result when page auth tokens are genuinely unavailable.
        }
    }
    return state;
}
export async function readCurrentNotebooklm(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const url = window.location.href;
    const match = url.match(/\\/notebook\\/([^/?#]+)/);
    if (!match) return null;

    const titleNode = document.querySelector('h1, [data-testid="notebook-title"], [role="heading"]');
    const title = (titleNode?.textContent || document.title || '').trim();
    return {
      id: match[1],
      title,
      url,
      source: 'current-page',
    };
  })()`));
    if (!raw)
        return null;
    return {
        id: String(raw.id ?? ''),
        title: normalizeNotebooklmTitle(raw.title, 'Untitled Notebook'),
        url: String(raw.url ?? ''),
        source: 'current-page',
        is_owner: true,
        created_at: null,
    };
}
export async function listNotebooklmLinks(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const rows = [];
    const seen = new Set();

    for (const node of Array.from(document.querySelectorAll('a[href*="/notebook/"]'))) {
      if (!(node instanceof HTMLAnchorElement)) continue;
      const href = node.href || '';
      const match = href.match(/\\/notebook\\/([^/?#]+)/);
      if (!match) continue;
      const id = match[1];
      if (seen.has(id)) continue;
      seen.add(id);

      const parentCard = node.closest('mat-card, [role="listitem"], article, div');
      const titleNode = parentCard?.querySelector('.project-button-title, [id$="-title"]');
      const subtitleTitleNode = parentCard?.querySelector('.project-button-subtitle-part[title]');
      const subtitleTextNode = parentCard?.querySelector('.project-button-subtitle-part, .project-button-subtitle');
      const parentText = (parentCard?.textContent || '').trim();
      const parentLines = parentText
        .split(/\\n+/)
        .map((value) => value.trim())
        .filter(Boolean);

      const title = (
        titleNode?.textContent ||
        node.getAttribute('aria-label') ||
        node.getAttribute('title') ||
        parentLines.find((line) => !line.includes('个来源') && !line.includes('sources') && !line.includes('more_vert')) ||
        node.textContent ||
        ''
      ).trim();
      const createdAtHint = (
        subtitleTitleNode?.getAttribute?.('title') ||
        subtitleTextNode?.textContent ||
        ''
      ).trim();

      rows.push({
        id,
        title,
        url: href,
        source: 'home-links',
        is_owner: true,
        created_at: createdAtHint || null,
      });
    }

    return rows;
  })()`));
    if (!Array.isArray(raw))
        return [];
    return raw
        .map((row) => ({
        id: String(row.id ?? ''),
        title: normalizeNotebooklmTitle(row.title, 'Untitled Notebook'),
        url: String(row.url ?? ''),
        source: 'home-links',
        is_owner: row.is_owner === false ? false : true,
        created_at: normalizeNotebooklmCreatedAt(row.created_at),
    }))
        .filter((row) => row.id && row.url);
}
export async function listNotebooklmSourcesFromPage(page) {
    const raw = unwrapNotebooklmEvaluateResult(await page.evaluate(`(() => {
    const notebookMatch = window.location.href.match(/\\/notebook\\/([^/?#]+)/);
    const notebookId = notebookMatch ? notebookMatch[1] : '';
    if (!notebookId) return [];

    const skip = new Set([
      '选择所有来源',
      '添加来源',
      '收起来源面板',
      '更多',
      'Web',
      'Fast Research',
      '提交',
      '创建笔记本',
      '分享笔记本',
      '设置',
      '对话选项',
      '配置笔记本',
      '音频概览',
      '演示文稿',
      '视频概览',
      '思维导图',
      '报告',
      '闪卡',
      '测验',
      '信息图',
      '数据表格',
      '添加笔记',
      '保存到笔记',
      '复制摘要',
      '摘要很棒',
      '摘要欠佳',
    ]);

    const rows = [];
    const seen = new Set();
    for (const node of Array.from(document.querySelectorAll('button, [role="button"], input[type="checkbox"]'))) {
      const text = (node.getAttribute?.('aria-label') || node.textContent || '').trim();
      if (!text || skip.has(text) || seen.has(text)) continue;
      if (text.includes('个来源') || text.includes('来源') && text.length < 5) continue;
      seen.add(text);
      rows.push({
        id: text,
        notebook_id: notebookId,
        title: text,
        url: window.location.href,
        source: 'current-page',
      });
    }
    return rows;
  })()`));
    if (!Array.isArray(raw))
        return [];
    return raw.filter((row) => row.id && row.title);
}
export async function requireNotebooklmSession(page) {
    const state = await getNotebooklmPageState(page);
    if (state.hostname !== NOTEBOOKLM_DOMAIN) {
        throw new CliError('NOTEBOOKLM_UNAVAILABLE', 'NotebookLM page is not available in the current browser session', `Open Chrome and navigate to ${NOTEBOOKLM_HOME_URL}`);
    }
    if (state.loginRequired) {
        throw new AuthRequiredError(NOTEBOOKLM_DOMAIN, 'NotebookLM requires a logged-in Google session');
    }
    return state;
}
