/**
 * Cloudflare 挑战统一处理：检测 → 会话快速通道 → 内置等待 → Captcha API 兜底
 *
 * 当 antiBot.postCfReadyUrlContains 已配置时，session_reuse 仅在业务 URL 已到达时才走快速通道；
 * 否则清除可能过期的 cf_clearance 并重新触发 Turnstile（Whatshub 短链 → work-order-sharing 场景）。
 */
const { detectCloudflare } = require('./cf-detector');
const { waitForTurnstile } = require('./turnstile-handler');
const { isCaptchaConfigured } = require('./captcha-solver');
const { runCaptchaApiFallback } = require('./cf-api-fallback');
const { isSessionLikelyValid } = require('./session-store');

/**
 * 是否配置了 CF 后必须到达的业务 URL
 * @param {object} antiBot
 */
function needsPostCfBusinessUrl(antiBot) {
    return !!(antiBot?.postCfReadyUrlContains || '').trim();
}

/**
 * 当前页面 URL 是否已包含业务路径片段
 * @param {import('puppeteer').Page} page
 * @param {object} antiBot
 */
function isPostCfBusinessUrlReady(page, antiBot) {
    const needle = (antiBot?.postCfReadyUrlContains || '').trim();
    if (!needle) {
        return true;
    }
    return page.url().includes(needle);
}

/**
 * 清除 cf_clearance，避免「有 cookie 但 SPA 未触发跳转」的假过关
 * @param {import('puppeteer').Page} page
 */
async function clearCfClearanceCookies(page) {
    const cookies = await page.cookies();
    for (const c of cookies) {
        if (c.name !== 'cf_clearance') {
            continue;
        }
        const host = (c.domain || '').replace(/^\./, '');
        if (host) {
            await page.deleteCookie({ name: c.name, url: `https://${host}/` }).catch(() => {});
        }
        await page.deleteCookie({
            name: c.name,
            domain: c.domain,
            path: c.path || '/',
        }).catch(() => {});
    }
}

/**
 * reload 后检查业务 URL 是否就绪
 * @param {import('puppeteer').Page} page
 * @param {(page: import('puppeteer').Page) => Promise<string>} [waitForUrlSettled]
 * @param {number} timeoutMs
 * @param {object} antiBot
 */
async function tryPostCfRecoverViaReload(page, waitForUrlSettled, timeoutMs, antiBot) {
    if (!needsPostCfBusinessUrl(antiBot) || isPostCfBusinessUrlReady(page, antiBot)) {
        return isPostCfBusinessUrlReady(page, antiBot);
    }

    console.log(`[CF] 业务 URL 未就绪 current=${page.url()}，尝试 reload`);
    try {
        await page.reload({ waitUntil: 'domcontentloaded', timeout: Math.min(timeoutMs, 45000) });
        if (waitForUrlSettled) {
            await waitForUrlSettled(page).catch(() => {});
        }
        return isPostCfBusinessUrlReady(page, antiBot);
    } catch (reloadErr) {
        console.warn('[CF] reload 失败:', reloadErr.message);
        return false;
    }
}

/**
 * 清除 cf_clearance 并 reload，使 Turnstile 重新出现
 * @param {import('puppeteer').Page} page
 * @param {(page: import('puppeteer').Page) => Promise<string>} [waitForUrlSettled]
 * @param {number} timeoutMs
 */
async function invalidateStaleCfAndReload(page, waitForUrlSettled, timeoutMs, antiBot = null) {
    console.log('[CF] 清除 cf_clearance 并 reload，重新触发 Turnstile');
    await clearCfClearanceCookies(page);
    await page.reload({ waitUntil: 'domcontentloaded', timeout: Math.min(timeoutMs, 45000) });
    if (waitForUrlSettled) {
        await waitForUrlSettled(page).catch(() => {});
    }
    return detectCloudflare(page, antiBot);
}

/**
 * session_reuse 快速返回前校验：有 postCf 要求时必须已到业务页
 * @param {import('puppeteer').Page} page
 * @param {object} antiBot
 * @param {(page: import('puppeteer').Page) => Promise<string>} [waitForUrlSettled]
 * @param {number} timeoutMs
 * @param {number} started
 * @param {boolean} cfDetected
 */
async function trySessionReuseFastPath(page, antiBot, waitForUrlSettled, timeoutMs, started, cfDetected) {
    if (waitForUrlSettled) {
        await waitForUrlSettled(page).catch(() => {});
    }

    if (!needsPostCfBusinessUrl(antiBot) || isPostCfBusinessUrlReady(page, antiBot)) {
        return {
            success: true,
            code: null,
            challengeType: null,
            stage: 'session_reuse',
            solverUsed: false,
            elapsedMs: Date.now() - started,
            cfDetected,
        };
    }

    console.log(`[CF] cf_clearance/无挑战但业务页未就绪 url=${page.url()}，降级重试`);

    if (await tryPostCfRecoverViaReload(page, waitForUrlSettled, timeoutMs, antiBot)) {
        return {
            success: true,
            code: null,
            challengeType: null,
            stage: 'session_reuse_reload',
            solverUsed: false,
            elapsedMs: Date.now() - started,
            cfDetected,
        };
    }

    return null;
}

/**
 * @param {import('puppeteer').Page} page
 * @param {{ turnstile?: boolean, solverFallback?: boolean, challengeTimeoutMs?: number, postCfReadyUrlContains?: string }} antiBot
 * @param {(page: import('puppeteer').Page) => Promise<string>} [waitForUrlSettled]
 * @param {import('./session-store').StoredSession|null} [storedSession]
 */
async function handleCloudflareChallenge(page, antiBot, waitForUrlSettled, storedSession = null) {
    const started = Date.now();
    const timeoutMs = antiBot.challengeTimeoutMs || 60000;

    let cfState = await detectCloudflare(page, antiBot);

    if (!cfState.blocked && cfState.hasCfClearance) {
        const fast = await trySessionReuseFastPath(
            page, antiBot, waitForUrlSettled, timeoutMs, started, false
        );
        if (fast) {
            return fast;
        }
        cfState = await invalidateStaleCfAndReload(page, waitForUrlSettled, timeoutMs, antiBot);
    }

    if (!cfState.blocked) {
        if (waitForUrlSettled) {
            await waitForUrlSettled(page).catch(() => {});
        }

        if (!needsPostCfBusinessUrl(antiBot) || isPostCfBusinessUrlReady(page, antiBot)) {
            return {
                success: true,
                code: null,
                challengeType: null,
                stage: storedSession && isSessionLikelyValid(storedSession) ? 'session_reuse' : null,
                solverUsed: false,
                elapsedMs: Date.now() - started,
                cfDetected: false,
            };
        }

        if (await tryPostCfRecoverViaReload(page, waitForUrlSettled, timeoutMs, antiBot)) {
            return {
                success: true,
                code: null,
                challengeType: null,
                stage: 'post_nav_reload',
                solverUsed: false,
                elapsedMs: Date.now() - started,
                cfDetected: false,
            };
        }

        if (cfState.hasCfClearance) {
            cfState = await invalidateStaleCfAndReload(page, waitForUrlSettled, timeoutMs, antiBot);
        } else {
            return {
                success: true,
                code: null,
                challengeType: null,
                stage: 'pending_business_nav',
                solverUsed: false,
                elapsedMs: Date.now() - started,
                cfDetected: false,
            };
        }
    }

    if (storedSession && isSessionLikelyValid(storedSession)) {
        console.log('[CF] 会话有效但仍被拦截，尝试 reload 一次');
        try {
            await page.reload({ waitUntil: 'domcontentloaded', timeout: Math.min(timeoutMs, 45000) });
            if (waitForUrlSettled) {
                await waitForUrlSettled(page).catch(() => {});
            }
            cfState = await detectCloudflare(page, antiBot);
            if (!cfState.blocked) {
                if (waitForUrlSettled) {
                    await waitForUrlSettled(page).catch(() => {});
                }
                return {
                    success: true,
                    code: null,
                    challengeType: cfState.challengeType,
                    stage: 'session_reload',
                    solverUsed: false,
                    elapsedMs: Date.now() - started,
                    cfDetected: true,
                };
            }
        } catch (reloadErr) {
            console.warn('[CF] reload 失败:', reloadErr.message);
        }
    }

    console.log(`[CF] 检测到挑战 type=${cfState.challengeType} url=${cfState.url}`);

    if (antiBot.turnstile !== false) {
        const builtIn = await waitForTurnstile(page, { timeoutMs, useBuiltInClick: true });
        if (builtIn.success) {
            if (waitForUrlSettled) {
                await waitForUrlSettled(page).catch(() => {});
            }
            cfState = await detectCloudflare(page, antiBot);
            if (!cfState.blocked) {
                return {
                    success: true,
                    code: null,
                    challengeType: cfState.challengeType,
                    stage: 'built_in',
                    solverUsed: false,
                    elapsedMs: Date.now() - started,
                    cfDetected: true,
                };
            }
        }
    }

    if (antiBot.solverFallback !== false && isCaptchaConfigured()) {
        try {
            if (waitForUrlSettled) {
                await waitForUrlSettled(page).catch(() => {});
            }
            await runCaptchaApiFallback(page, {
                antiBot,
                navUrl: page.url(),
                waitForUrlSettled,
                timeoutMs,
            });

            const apiWaitMs = Math.min(timeoutMs, 30000);
            await waitForTurnstile(page, { timeoutMs: apiWaitMs });

            if (waitForUrlSettled) {
                await waitForUrlSettled(page).catch(() => {});
            }

            cfState = await detectCloudflare(page, antiBot);
            if (!cfState.blocked) {
                return {
                    success: true,
                    code: null,
                    challengeType: 'turnstile',
                    stage: 'api',
                    solverUsed: true,
                    elapsedMs: Date.now() - started,
                    cfDetected: true,
                };
            }
        } catch (apiErr) {
            console.error('[CF] Captcha API 兜底失败:', apiErr.message);
            return {
                success: false,
                code: 'CF_TURNSTILE_FAILED',
                challengeType: cfState.challengeType || 'turnstile',
                stage: 'api',
                solverUsed: true,
                elapsedMs: Date.now() - started,
                cfDetected: true,
            };
        }
    }

    return {
        success: false,
        code: 'CF_TURNSTILE_FAILED',
        challengeType: cfState.challengeType || 'unknown',
        stage: antiBot.solverFallback !== false && !isCaptchaConfigured() ? 'built_in' : 'timeout',
        solverUsed: false,
        elapsedMs: Date.now() - started,
        cfDetected: true,
    };
}

module.exports = {
    handleCloudflareChallenge,
    isCaptchaConfigured,
    needsPostCfBusinessUrl,
    isPostCfBusinessUrlReady,
    clearCfClearanceCookies,
};
