<?php
/**
 * calcular_regalias.php
 * Calcula las regalías de TODOS los distribuidores para un mes/año dado
 * e inserta los registros en la tabla regs2.
 *
 * Método: POST
 * Body JSON: { "mes": 2, "anio": 2026 }
 * Auth: Bearer token (admin_sessions)
 *
 * Campos insertados en regs2:
 *   id_inver   → distribuidor
 *   calificados → número de directos con 624+ puntos
 *   total      → total de puntos generados en la red (todos los niveles)
 *   _real      → ganancia total a recibir (niveles abiertos)
 *   apagar     → igual que _real (se modifica después al pagar)
 *   fechap     → fecha de cálculo (hoy)
 *   cd         → 0 (admin)
 *   status     → 0 (pendiente de pago)
 *   impuestos  → apagar * 0.07
 *   pagado     → apagar - impuestos
 */

header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }

// Aumentar tiempo de ejecución para redes grandes
set_time_limit(300);
ini_set('memory_limit', '256M');

require_once __DIR__ . '/../../config.php';

// ── Autenticación admin ─────────────────────────────────────────────────────
$headers    = function_exists('getallheaders') ? getallheaders() : [];
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
if (!$authHeader && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'];
} elseif (!$authHeader && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
    $authHeader = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
$adminToken = str_replace('Bearer ', '', $authHeader);

if (empty($adminToken)) {
    http_response_code(401);
    echo json_encode(['success' => false, 'message' => 'Token requerido']);
    exit;
}

$db  = getDB();
$now = date('Y-m-d H:i:s');

$stmtSess = $db->prepare(
    "SELECT s.idusuarios_sist FROM admin_sessions s
     WHERE s.token = ? AND s.expira > ? LIMIT 1"
);
$stmtSess->execute([$adminToken, $now]);
if (!$stmtSess->fetch()) {
    http_response_code(401);
    echo json_encode(['success' => false, 'message' => 'Sesión expirada o inválida']);
    exit;
}

// ── Parámetros ──────────────────────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['success' => false, 'message' => 'Método no permitido']);
    exit;
}

$body = json_decode(file_get_contents('php://input'), true);
$mes  = isset($body['mes'])  ? (int)$body['mes']  : 0;
$anio = isset($body['anio']) ? (int)$body['anio'] : 0;

if ($mes < 1 || $mes > 12 || $anio < 2020 || $anio > 2100) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'Mes o año inválido']);
    exit;
}

$mesInicio = sprintf('%04d-%02d-01', $anio, $mes);
$mesFin    = date('Y-m-t', strtotime($mesInicio));
$fechap    = date('Y-m-d');

$MESES_ES = [
    1=>'Enero',2=>'Febrero',3=>'Marzo',4=>'Abril',5=>'Mayo',6=>'Junio',
    7=>'Julio',8=>'Agosto',9=>'Septiembre',10=>'Octubre',11=>'Noviembre',12=>'Diciembre'
];
$periodo = ($MESES_ES[$mes] ?? $mes) . ' ' . $anio;

// ── Constantes del plan de compensación ────────────────────────────────────
$MAX_NIVELES = 18;
$PORCENTAJES = [
    1=>0.10, 2=>0.17, 3=>0.17, 4=>0.17, 5=>0.01,
    6=>0.01, 7=>0.01, 8=>0.01, 9=>0.01, 10=>0.01,
    11=>0.01,12=>0.01,13=>0.01,14=>0.01,15=>0.01,
    16=>0.01,17=>0.01,18=>0.01,
];

// ── Obtener todos los distribuidores activos ─────────────────────────────────
$stmtAll = $db->query("SELECT id_inver FROM inversionista ORDER BY id_inver ASC");
$todosIds = $stmtAll->fetchAll(PDO::FETCH_COLUMN);

// ── Precarga: puntos propios de todos los distribuidores en el período ──────
// Esto evita N+1 queries: una sola query para todos
$stmtPuntos = $db->prepare(
    "SELECT t.inversionista_id_inver AS id_inver,
            COALESCE(SUM(d.puntos), 0) AS puntos
     FROM transacciones t
     JOIN depositos d ON d.transacciones_idtransacciones = t.idtransacciones
     WHERE t.fechat BETWEEN ? AND ?
     GROUP BY t.inversionista_id_inver"
);
$stmtPuntos->execute([$mesInicio, $mesFin]);
$puntosPorId = [];
foreach ($stmtPuntos->fetchAll(PDO::FETCH_ASSOC) as $row) {
    $puntosPorId[(int)$row['id_inver']] = (float)$row['puntos'];
}

// ── Precarga: árbol de patrocinadores (idprom) ──────────────────────────────
// Una sola query para construir el árbol completo
$stmtArbol = $db->query("SELECT id_inver, idprom FROM inversionista");
$hijosDeId  = []; // idprom → [id_inver, ...]
foreach ($stmtArbol->fetchAll(PDO::FETCH_ASSOC) as $row) {
    $id    = (int)$row['id_inver'];
    $prom  = (int)($row['idprom'] ?? 0);
    if ($prom > 0) {
        $hijosDeId[$prom][] = $id;
    }
}

// ── Función: calcular regalías de un distribuidor usando el árbol precargado ─
function calcularRegaliasDistribuidor(
    int $idinv,
    array &$hijosDeId,
    array &$puntosPorId,
    int $MAX_NIVELES,
    array $PORCENTAJES
): array {
    $puntosPropios = $puntosPorId[$idinv] ?? 0.0;

    // Calificación propia
    if ($puntosPropios < 624) {
        $calificacion = 'no_calificado';
    } elseif ($puntosPropios < 1500) {
        $calificacion = 'parcial';
    } else {
        $calificacion = 'calificado';
    }

    // BFS para obtener puntos por nivel
    $currentLevel = [$idinv];
    $visitados    = [$idinv => true];
    $puntosNivel  = []; // nivel → total puntos
    $directos624  = 0;
    $directos1500 = 0;

    for ($nivel = 1; $nivel <= $MAX_NIVELES; $nivel++) {
        if (empty($currentLevel)) break;
        $nextLevel   = [];
        $totalPuntos = 0.0;

        foreach ($currentLevel as $pid) {
            if (empty($hijosDeId[$pid])) continue;
            foreach ($hijosDeId[$pid] as $hijo) {
                if (isset($visitados[$hijo])) continue;
                $visitados[$hijo] = true;
                $nextLevel[]      = $hijo;
                $pts              = $puntosPorId[$hijo] ?? 0.0;
                $totalPuntos     += $pts;

                if ($nivel === 1) {
                    if ($pts >= 624)  $directos624++;
                    if ($pts >= 1500) $directos1500++;
                }
            }
        }

        if ($totalPuntos > 0) {
            $puntosNivel[$nivel] = $totalPuntos;
        }
        $currentLevel = $nextLevel;
    }

    // Niveles abiertos
    $nivelesAbiertos = 0;
    if ($calificacion === 'no_calificado') {
        $nivelesAbiertos = 0;
    } elseif ($calificacion === 'parcial') {
        $nivelesAbiertos = ($directos624 >= 1) ? 1 : 0;
    } else {
        $nivelesBase = $directos624;
        if ($directos1500 >= 3) {
            $nivelesEspecial = 5;
        } elseif ($directos1500 >= 2) {
            $nivelesEspecial = 3;
        } else {
            $nivelesEspecial = 0;
        }
        $nivelesAbiertos = min(max($nivelesBase, $nivelesEspecial), $MAX_NIVELES);
    }

    // Calcular ganancias
    $totalGenerado = 0.0;
    $totalRecibir  = 0.0;
    for ($n = 1; $n <= $MAX_NIVELES; $n++) {
        $pts       = $puntosNivel[$n] ?? 0.0;
        $pct       = $PORCENTAJES[$n] ?? 0.01;
        $generado  = $pts * $pct;
        $recibir   = ($n <= $nivelesAbiertos) ? $generado : 0.0;
        $totalGenerado += $generado;
        $totalRecibir  += $recibir;
    }

    return [
        'calificados'  => $directos624,
        'total'        => round($totalGenerado, 2),
        '_real'        => round($totalRecibir, 2),
    ];
}

// ── Calcular para todos los distribuidores ──────────────────────────────────
$registros    = [];
$totalAPagar  = 0.0;

foreach ($todosIds as $idinv) {
    $idinv = (int)$idinv;
    $res   = calcularRegaliasDistribuidor(
        $idinv, $hijosDeId, $puntosPorId, $MAX_NIVELES, $PORCENTAJES
    );

    // Solo insertar si tiene ganancia > 0
    if ($res['_real'] <= 0) continue;

    $apagar    = $res['_real'];
    $impuestos = round($apagar * 0.07, 2);
    $pagado    = round($apagar - $impuestos, 2);

    $registros[] = [
        'id_inver'   => $idinv,
        'calificados'=> $res['calificados'],
        'total'      => $res['total'],
        '_real'      => $res['_real'],
        'apagar'     => $apagar,
        'fechap'     => $fechap,
        'cd'         => 0,
        'status'     => 0,
        'impuestos'  => $impuestos,
        'pagado'     => $pagado,
    ];
    $totalAPagar += $apagar;
}

// ── Insertar en regs2 ───────────────────────────────────────────────────────
// Eliminar registros del mismo período y fecha para evitar duplicados
$db->prepare(
    "DELETE FROM regs2 WHERE fechap = ? AND cd = 0"
)->execute([$fechap]);

$insertados = 0;
if (!empty($registros)) {
    $stmtIns = $db->prepare(
        "INSERT INTO regs2 (id_inver, calificados, total, _real, apagar, fechap, cd, status, impuestos, pagado)
         VALUES (:id_inver, :calificados, :total, :_real, :apagar, :fechap, :cd, :status, :impuestos, :pagado)"
    );
    foreach ($registros as $reg) {
        $stmtIns->execute([
            ':id_inver'   => $reg['id_inver'],
            ':calificados'=> $reg['calificados'],
            ':total'      => $reg['total'],
            ':_real'      => $reg['_real'],
            ':apagar'     => $reg['apagar'],
            ':fechap'     => $reg['fechap'],
            ':cd'         => $reg['cd'],
            ':status'     => $reg['status'],
            ':impuestos'  => $reg['impuestos'],
            ':pagado'     => $reg['pagado'],
        ]);
        $insertados++;
    }
}

echo json_encode([
    'success'       => true,
    'periodo'       => $periodo,
    'mes'           => $mes,
    'anio'          => $anio,
    'insertados'    => $insertados,
    'total_a_pagar' => round($totalAPagar, 2),
    'fechap'        => $fechap,
], JSON_UNESCAPED_UNICODE);
