/**
 * garena.js – Versión sin Playwright.
 *
 * Usa la cookie completa guardada en `full-cookies.json` (generada una sola
 * vez con Playwright en el entorno local) para autenticarse contra Pagostore
 * y obtener nickname + paquetes con descuento.
 *
 * DataDome: si los flags de admin lo permiten (services/settings.json), un 403
 * con captcha dispara el solver de 2Captcha (services/datadome.js), renueva la
 * cookie `datadome` en disco y reintenta UNA vez de forma transparente.
 *
 * Exporta `fetchPlayerInfo(playerId)` con la misma firma que el resto del
 * proyecto. Devuelve un objeto:
 *   { status: 'ok'|'datadome'|'invalid_id'|'no_cookie', nickname?, packages? }
 */

import fs from 'fs';
import { GARENA, USER_AGENT } from '../config.js';
import { getPackageSize, normalizePackageId } from './pricing.js';
import { readSettings } from './settings.js';
import { solveDataDome, extractCaptchaUrl } from './datadome.js';
import { notifyCookieDown } from './notifier.js';
import { 
  getActiveCookie, 
  getCookiePool, 
  rotateCookie, 
  markCurrentCookieFailed, 
  resetCurrentCookieSuccess, 
  persistActiveCookieUpdate, 
  reloadCookiePool 
} from './cookieManager.js';

// Headers comunes a login y channels (todo igual salvo Cookie y Content-Type).
// Se construyen una sola vez en vez de por request, reduciendo GC pressure.
const __commonHeaders = {
  Origin: GARENA.baseUrl,
  Referer: `${GARENA.baseUrl}/app/${GARENA.appId}`,
  Accept: 'application/json, text/plain, */*',
  'User-Agent': USER_AGENT,
  'Sec-CH-UA': '"Not A;Brand";v="99", "Chromium";v="120", "Google Chrome";v="120"',
  'Sec-CH-UA-Mobile': '?0',
  'Sec-CH-UA-Platform': '"Windows"',
  'Sec-Fetch-Dest': 'empty',
  'Sec-Fetch-Mode': 'cors',
  'Sec-Fetch-Site': 'same-origin',
  'Accept-Language': 'es-ES,es;q=0.9',
};

// Mantenido por compatibilidad con tests
let __savedCookiesCache = null;
let __savedCookiesMtime = 0;
export function readSavedCookies() {
  const active = getActiveCookie();
  if (!active || !active.filePath) return [];
  try {
    const stat = fs.statSync(active.filePath);
    if (stat.mtimeMs === __savedCookiesMtime && __savedCookiesCache) {
      return __savedCookiesCache;
    }
    reloadCookiePool(true);
    const fresh = getActiveCookie();
    __savedCookiesCache = fresh ? fresh.cookies : [];
    __savedCookiesMtime = stat.mtimeMs;
    return __savedCookiesCache;
  } catch (_e) {
    return active.cookies;
  }
}

function cookieHeaderFromMap(map) {
  return [...map].map(([name, value]) => `${name}=${value}`).join('; ');
}

/**
 * Mete en `map` cada cookie que devuelve el login. IMPORTANTE: el login setea
 * VARIAS cookies (datadome nuevo + session_key del jugador) en cabeceras
 * Set-Cookie separadas. `headers.get('set-cookie')` las une por comas y no se
 * pueden partir de forma fiable, así que usamos `getSetCookie()` (Node 18.14+),
 * que las devuelve una por una. Sin el session_key la API de channels responde
 * precios genéricos SIN el descuento personalizado del jugador.
 */
function applySetCookies(loginResp, map) {
  const setCookies = typeof loginResp.headers.getSetCookie === 'function'
    ? loginResp.headers.getSetCookie()
    : (loginResp.headers.get('set-cookie') ? [loginResp.headers.get('set-cookie')] : []);
  for (const sc of setCookies) {
    const [pair] = sc.split(';');
    const idx = pair.indexOf('=');
    if (idx > 0) map.set(pair.slice(0, idx).trim(), pair.slice(idx + 1));
  }
}

/**
 * Un intento completo (login + channels) con el cookieMap dado.
 * Devuelve:
 *   - { status:'ok', nickname, packages }
 *   - { status:'invalid_id', error }
 *   - { status:'challenge', stage, bodyText }   → 403 DataDome real (con body)
 *   - { status:'datadome', error }              → error de red / otros fallos
 */
async function attempt(playerId, cookieMap) {
  // 1️⃣ POST login (static cookies + Chrome-like headers). Se quita el
  // session_key viejo para que el login genere uno fresco por jugador.
  let loginResp;
  try {
    loginResp = await fetch(GARENA.loginUrl, {
      method: 'POST',
      headers: {
        ...__commonHeaders,
        Cookie: cookieHeaderFromMap(cookieMap),
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ app_id: GARENA.appId, login_id: playerId, app_server_id: 0 }),
    });
  } catch (_e) {
    return { status: 'datadome', error: 'Error de red al login' };
  }

  if (!loginResp.ok) {
    if (loginResp.status === 403) {
      const bodyText = await loginResp.text().catch(() => '');
      return { status: 'challenge', stage: 'login', bodyText };
    }
    return { status: 'invalid_id', error: `Login falló con status ${loginResp.status}` };
  }

  const loginData = await loginResp.json();
  if (!loginData.nickname) {
    return { status: 'invalid_id', error: loginData.error || 'ID inválido' };
  }

  // Conservar TODAS las cookies del login (datadome nuevo + session_key del jugador).
  applySetCookies(loginResp, cookieMap);

  // 2️⃣ GET channels (region DO) usando el header fusionado
  const channelsUrl = `${GARENA.baseUrl}/api/shop/apps/channels?app_id=${GARENA.appId}&packed_role_id=0&region=${GARENA.region}&language=${GARENA.language}`;
  let channelsResp;
  try {
    channelsResp = await fetch(channelsUrl, {
      method: 'GET',
      headers: { ...__commonHeaders, Cookie: cookieHeaderFromMap(cookieMap) },
    });
  } catch (_e) {
    return { status: 'datadome', error: 'Error de red al obtener canales' };
  }

  if (!channelsResp.ok) {
    if (channelsResp.status === 403) {
      const bodyText = await channelsResp.text().catch(() => '');
      return { status: 'challenge', stage: 'channels', bodyText };
    }
    return { status: 'datadome', error: `Channels request failed ${channelsResp.status}` };
  }

  const channelsData = await channelsResp.json();
  const paypal = channelsData?.channels?.find(c => c.name?.toLowerCase().includes('paypal'));
  // Diamantes: valor FIJO del config. Descuento REAL: currency_amount difiere de
  // original_currency_amount en la región DO.
  const packages = (paypal?.items ?? []).map(item => {
    const id = normalizePackageId(`pkg-${item.app_point_amount}`);
    return {
      id,
      diamonds: getPackageSize(id) ?? item.app_point_amount,
      price: item.currency_amount,
      originalPrice: item.original_currency_amount,
      currency: item.currency,
      hasDiscount: item.currency_amount !== item.original_currency_amount,
    };
  });

  // 3?? GET channels región SV → detectar Paquetes de Aumento de Nivel disponibles
  const svUrl = `${GARENA.baseUrl}/api/shop/apps/channels?app_id=${GARENA.appId}&packed_role_id=0&region=SV&language=${GARENA.language}`;
  let availableLevels = [];
  try {
    const svResp = await fetch(svUrl, {
      method: 'GET',
      headers: { ...__commonHeaders, Cookie: cookieHeaderFromMap(cookieMap) },
    });
    if (svResp.ok) {
      const svData = await svResp.json();
      for (const ch of svData?.channels || []) {
        for (const item of ch.items || []) {
          const name = String(item.name || '');
          if (!name.includes('Aumento de Nivel') && !name.includes('aumento de nivel')) continue;
          const m = name.match(/Nivel\s*(\d+)/i);
          if (m) availableLevels.push(Number(m[1]));
        }
      }
      // También buscar en rebate_card a nivel raíz del JSON
      if (svData?.rebate_card) {
        const rcName = String(svData.rebate_card.name || '');
        const m = rcName.match(/Nivel\s*(\d+)/i);
        if (m && !availableLevels.includes(Number(m[1]))) availableLevels.push(Number(m[1]));
      }
    }
  } catch (_e) {
    // Si SV falla, simplemente no hay niveles disponibles — no rompe el flujo DO.
  }

  return { status: 'ok', nickname: loginData.nickname, packages, availableLevels };
}

// Core function – performs login and then fetches the PayPal channel packages
export async function fetchPlayerInfo(playerId) {
  reloadCookiePool();

  const settings = readSettings();
  const solverOn = Boolean(settings.datadomeEnabled);
  const autoRetry = Boolean(settings.datadomeAuto);

  const pool = getCookiePool();
  if (pool.length === 0) {
    return { status: 'no_cookie', error: 'Faltan cookies válidas en backend/cookie/' };
  }

  let attempts = 0;
  const maxAttempts = pool.length;

  while (attempts < maxAttempts) {
    const activeCookieData = getActiveCookie();
    if (!activeCookieData) {
      break;
    }

    const savedCookies = activeCookieData.cookies;
    const cookieMap = new Map(savedCookies.map(c => [c.name, c.value]));
    cookieMap.delete('session_key');

    let result = await attempt(playerId, cookieMap);

    if (result.status === 'ok' || result.status === 'invalid_id') {
      resetCurrentCookieSuccess();
      return result;
    }

    if (result.status === 'challenge' && solverOn) {
      const captchaUrl = extractCaptchaUrl(result.bodyText);
      const solved = await solveDataDome(captchaUrl);

      if (solved.ok) {
        const newMap = new Map();
        newMap.set('datadome', solved.value);
        persistActiveCookieUpdate(newMap);
        cookieMap.set('datadome', solved.value);

        if (autoRetry) {
          result = await attempt(playerId, cookieMap);
          if (result.status === 'ok' || result.status === 'invalid_id') {
            resetCurrentCookieSuccess();
            return result;
          }
        } else {
          return { status: 'datadome', error: 'DataDome challenge (cookie renovada)' };
        }
      }
    }

    // Si falló (DataDome no resuelto o la cookie está mal), rotamos
    markCurrentCookieFailed();
    notifyCookieDown(`Cookie bloqueada o inválida [${activeCookieData.id}], rotando a la siguiente.`);
    attempts++;
  }

  notifyCookieDown('Todas las cookies fallaron o se superó el límite de intentos.');
  return { status: 'datadome', error: 'DataDome challenge (cookies agotadas)' };
}

// Exportados para tests (no usados en producción)
export { __commonHeaders };
