/**
 * 会话 Cookie 持久化：按 sessionKey 保存完整 cookie jar，降低重复过盾概率
 */
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { RUNTIME_DIR, SESSION_TTL_MS } = require('./constants');

const SESSIONS_DIR = path.join(RUNTIME_DIR, 'sessions');

function ensureSessionsDir() {
    fs.mkdirSync(SESSIONS_DIR, { recursive: true, mode: 0o777 });
}

function keyToFilename(sessionKey) {
    const hash = crypto.createHash('md5').update(sessionKey).digest('hex');
    return `${hash}.json`;
}

/**
 * @param {{name:string,domain?:string,path?:string}} cookie
 * @returns {string}
 */
function cookieIdentity(cookie) {
    return `${cookie.name}|${cookie.domain || ''}|${cookie.path || '/'}`;
}

function mergeCookies(requestCookies, sessionCookies) {
    const map = new Map();
    for (const cookie of sessionCookies || []) {
        if (cookie && cookie.name) map.set(cookieIdentity(cookie), cookie);
    }
    for (const cookie of requestCookies || []) {
        if (cookie && cookie.name) map.set(cookieIdentity(cookie), cookie);
    }
    return Array.from(map.values());
}

function isSessionLikelyValid(session) {
    if (!session || !Array.isArray(session.cookies)) return false;
    if (session.expiresAt && Date.now() > session.expiresAt) return false;
    return session.cookies.some((c) => c.name === 'cf_clearance' && c.value);
}

function hasCfClearance(session) {
    if (!session || !Array.isArray(session.cookies)) return false;
    return session.cookies.some((c) => c.name === 'cf_clearance' && c.value);
}

function loadSession(sessionKey) {
    if (!sessionKey) return null;
    ensureSessionsDir();
    const filePath = path.join(SESSIONS_DIR, keyToFilename(sessionKey));
    if (!fs.existsSync(filePath)) return null;
    try {
        const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
        if (!raw || !Array.isArray(raw.cookies)) return null;
        if (raw.expiresAt && Date.now() > raw.expiresAt) {
            fs.unlinkSync(filePath);
            return null;
        }
        return raw;
    } catch (_) {
        return null;
    }
}

function touchSession(sessionKey, ttlMs = SESSION_TTL_MS) {
    const session = loadSession(sessionKey);
    if (!session) return;
    const now = Date.now();
    session.savedAt = now;
    session.expiresAt = now + ttlMs;
    fs.writeFileSync(path.join(SESSIONS_DIR, keyToFilename(sessionKey)), JSON.stringify(session, null, 0), { mode: 0o666 });
}

function saveSession(sessionKey, cookies, ttlMs = SESSION_TTL_MS, meta = {}) {
    if (!sessionKey || !Array.isArray(cookies) || cookies.length === 0) return;
    ensureSessionsDir();
    const now = Date.now();
    const payload = {
        sessionKey,
        savedAt: now,
        lastSuccessAt: now,
        expiresAt: now + ttlMs,
        lastHost: meta.lastHost || '',
        savedProfile: meta.savedProfile || '',
        cookies: cookies.map((c) => ({
            name: c.name,
            value: c.value,
            domain: c.domain,
            path: c.path || '/',
        })),
    };
    fs.writeFileSync(path.join(SESSIONS_DIR, keyToFilename(sessionKey)), JSON.stringify(payload, null, 0), { mode: 0o666 });
}

function sessionCookiesForPage(session, pageUrl) {
    if (!session || !Array.isArray(session.cookies)) return [];
    let hostname = '';
    try {
        hostname = new URL(pageUrl).hostname;
    } catch (_) {
        return session.cookies;
    }
    return session.cookies.map((c) => {
        const cookie = { ...c };
        if (!cookie.domain && hostname) cookie.url = `https://${hostname}/`;
        return cookie;
    });
}

function getSessionDebugInfo(sessionKey) {
    if (!sessionKey) return null;
    const filePath = path.join(SESSIONS_DIR, keyToFilename(sessionKey));
    const session = loadSession(sessionKey);
    if (!session) {
        return { sessionKey, exists: false, filePath };
    }
    let mtime = null;
    try {
        mtime = fs.statSync(filePath).mtime.toISOString();
    } catch (_) { /* ignore */ }
    return {
        sessionKey,
        exists: true,
        filePath,
        mtime,
        savedAt: session.savedAt,
        expiresAt: session.expiresAt,
        lastSuccessAt: session.lastSuccessAt,
        lastHost: session.lastHost,
        savedProfile: session.savedProfile,
        cookieCount: session.cookies.length,
        hasCfClearance: hasCfClearance(session),
        likelyValid: isSessionLikelyValid(session),
    };
}

function getSessionStats() {
    ensureSessionsDir();
    const count = fs.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith('.json')).length;
    return { sessionsDir: SESSIONS_DIR, count };
}

module.exports = {
    loadSession,
    saveSession,
    touchSession,
    sessionCookiesForPage,
    mergeCookies,
    isSessionLikelyValid,
    hasCfClearance,
    getSessionDebugInfo,
    getSessionStats,
};
