#!/usr/bin/env node
/**
 * Headless Playwright login на crm.bp-gr.ru.
 *
 * Input (JSON через stdin):
 *   {login, password, url}
 *
 * Output (JSON через stdout):
 *   {phpsessid, csrf, refreshed_at}
 *
 * Exit codes:
 *   0 — success
 *   1 — auth failed (login/password rejected, или session cookie missing)
 *   2 — DOM не найден (CSRF token не найден)
 *   3 — timeout (60s)
 *   4 — invalid input или другая ошибка
 */
const { chromium } = require('playwright');

const TIMEOUT_MS = 60_000;

async function refresh(args) {
    // browser.launch ВНУТРИ try: отказ запуска (нет исполняемого файла браузера,
    // sandbox, нехватка libs) должен классифицироваться как exit 4 + JSON stderr,
    // а не уходить unhandled-rejection'ом в опасный exit 1 (FN-SESSION, приёмка 22.06).
    let browser = null;
    try {
        browser = await chromium.launch({ headless: true });
        const context = await browser.newContext();
        const page = await context.newPage();

        await page.goto(args.url, { waitUntil: 'load', timeout: TIMEOUT_MS });

        // DOM-селекторы crm.bp-gr.ru/login (Yii2 LoginForm) — verified live 2026-05-19 через Playwright MCP.
        const loginSelector = '#loginform-username';
        const passwordSelector = '#loginform-password';
        const submitSelector = 'button[type=submit]';

        await page.fill(loginSelector, args.login);
        await page.fill(passwordSelector, args.password);

        // Сабмит + ОЖИДАНИЕ пост-логин перехода.
        // Старый Promise.all([waitForLoadState('networkidle'), click]) — гонка:
        // логин-страница уже в состоянии networkidle, поэтому waitForLoadState
        // резолвился мгновенно (ДО редиректа), и скрипт хватал PHPSESSID
        // неаутентифицированной логин-страницы. Ждём, пока логин-форма исчезнет
        // из DOM — waitForFunction опрашивает и переживает навигацию.
        await page.click(submitSelector);
        await page
            .waitForFunction(
                (sel) => !document.querySelector(sel),
                loginSelector,
                { timeout: TIMEOUT_MS },
            )
            .catch(() => { /* форма осталась — логин отклонён, ловится guard'ом ниже */ });
        await page.waitForLoadState('networkidle', { timeout: TIMEOUT_MS }).catch(() => {});

        // Verify: логин-форма всё ещё на странице → вход НЕ удался. Не возвращаем
        // мусорную (неаутентифицированную) сессию как «успех» (exit 0).
        if ((await page.locator(loginSelector).count()) > 0) {
            process.stderr.write(JSON.stringify({ error: 'login rejected: still on login page after submit' }));
            process.exit(1);
        }

        let csrf = null;
        try {
            csrf = await page.locator('meta[name=csrf-token]').first().getAttribute('content', { timeout: 5000 });
        } catch (e) {
            // CSRF meta tag not found — try other patterns в Task 1 discovery
        }

        const cookies = await context.cookies();
        const sessionCookie = cookies.find(c => c.name === 'PHPSESSID' || c.name === 'JSESSIONID');

        if (!sessionCookie) {
            process.stderr.write(JSON.stringify({ error: 'session cookie not found in response' }));
            process.exit(1);
        }

        if (!csrf) {
            process.stderr.write(JSON.stringify({ error: 'CSRF token not found in DOM' }));
            process.exit(2);
        }

        process.stdout.write(JSON.stringify({
            phpsessid: sessionCookie.value,
            csrf: csrf,
            refreshed_at: new Date().toISOString(),
        }));
        process.exit(0);
    } catch (err) {
        process.stderr.write(JSON.stringify({ error: err.message }));
        process.exit(err.message.includes('Timeout') ? 3 : 4);
    } finally {
        if (browser) {
            await browser.close();
        }
    }
}

// Read stdin
let input = '';
process.stdin.on('data', chunk => { input += chunk; });
process.stdin.on('end', () => {
    let args;
    try {
        args = JSON.parse(input);
    } catch (e) {
        process.stderr.write(JSON.stringify({ error: 'invalid JSON on stdin' }));
        process.exit(4);
    }
    if (!args.login || !args.password || !args.url) {
        process.stderr.write(JSON.stringify({ error: 'missing required keys: login, password, url' }));
        process.exit(4);
    }
    // Top-level guard: любая rejection, не пойманная внутри refresh(), всё равно
    // даёт чистый exit 4 + JSON stderr, а не unhandled-rejection exit 1.
    refresh(args).catch((err) => {
        const message = err && err.message ? err.message : String(err);
        process.stderr.write(JSON.stringify({ error: message }));
        process.exit(4);
    });
});
