<?php

namespace app\models;

require_once 'conexion.php';

use PDO;
use Exception;
use DateTime;
use DateTimeZone;

class votos extends conexion
{
    /**
     * Función privada para obtener la hora exacta de Colombia
     */
    private function obtenerHoraColombia() {
        $fecha = new DateTime('now', new DateTimeZone('America/Bogota'));
        return $fecha->format('Y-m-d H:i:s');
    }

    public function todos()
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT * FROM `votos`");
        $query->execute();
        return $query->fetchAll(PDO::FETCH_ASSOC);
    }

    public function votoRealizado($username)
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT * FROM users WHERE `username`=:username AND `voto`='1' LIMIT 1");
        $query->bindParam("username", $username);
        $query->execute();
        return $query->fetch(PDO::FETCH_OBJ);
    }

    /**
     * FUNCIÓN MODIFICADA: Guarda el voto multiplicado por el poder del usuario
     * 
     * @param string $voto - Número del candidato
     * @param object $users - Objeto del usuario con zona y poder
     * @param int $poder - Cantidad de votos a registrar (por defecto 1)
     * @return bool
     */
    public function guardarVoto($voto, $users, $poder = 1)
    {
        $db = $this->DB();
        
        // Insertamos tantos votos como indique el poder
        for ($i = 0; $i < $poder; $i++) {
            $query = $db->prepare("INSERT INTO `votos` (`id`, `numero`, `zona`) VALUES (NULL, :numero, :zona)");
            $query->bindParam("numero", $voto);
            $query->bindParam("zona", $users->zona);
            $query->execute();
        }
        
        return true;
    }


    public function guardarVotoUsuario($username)
    {
        $db = $this->DB();
        $ahora = $this->obtenerHoraColombia();
        
        $query = $db->prepare("UPDATE `users` SET `voto` = '1', `registro` = :registro WHERE `users`.`username` = :username ");
        $query->bindParam("username", $username);
        $query->bindParam("registro", $ahora);
        return $query->execute();
    }


    public function total()
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT COUNT(`numero`) as total FROM votos");
        $query->execute();
        $total = $query->fetch(PDO::FETCH_OBJ);
        return $total->total;
    }

    public function totalZona($zona)
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT COUNT(`numero`) as total FROM votos WHERE `zona`=:zona");
        $query->bindParam("zona", $zona);
        $query->execute();
        $total = $query->fetch(PDO::FETCH_OBJ);
        return $total->total;
    }

    public function votosCandidato($candidato)
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT COUNT(`numero`) as total FROM votos WHERE `numero`=:numero");
        $query->bindParam("numero", $candidato);
        $query->execute();
        $total = $query->fetch(PDO::FETCH_OBJ);
        return $total->total;
    }

    /**
     * FUNCIONES PARA CENSO ELECTORAL
     * Modificadas para considerar poderes
     */

    /**
     * Censo total: suma de todos los poderes (no cuenta de usuarios)
     */
    public function censoTotal()
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT SUM(poder) as total FROM users");
        $query->execute();
        $res = $query->fetch(PDO::FETCH_OBJ);
        return $res->total ?? 0;
    }

    /**
     * Censo por zona: suma de poderes en esa zona
     */
    public function censoPorZona($zona)
    {
        $db = $this->DB();
        $query = $db->prepare("SELECT SUM(poder) as total FROM users WHERE `zona`=:zona");
        $query->bindParam("zona", $zona);
        $query->execute();
        $res = $query->fetch(PDO::FETCH_OBJ);
        return $res->total ?? 0;
    }


    public function votosPorCandidatoYZona()
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT numero, zona, COUNT(*) as total FROM votos GROUP BY numero, zona");
        $q->execute();
        $r = [];
        while ($row = $q->fetch(\PDO::FETCH_OBJ)) {
            $r[$row->numero . "|" . $row->zona] = (int)$row->total;
            $k = "total_" . $row->numero;
            if (!isset($r[$k])) $r[$k] = 0;
            $r[$k] += (int)$row->total;
        }
        return $r;
    }



    public function datosQuorumConsolidado()
    {
        $db = $this->DB();
        /* Fix 15B: presentes = quorum=1 estricto
           Webhook pone NULL al salir → descuento inmediato del quorum
           Grace window solo aplica en la ruta de votacion (PHP controllers) */
        $q = $db->prepare("
            SELECT
                SUM(CASE WHEN quorum=1 THEN 1 ELSE 0 END) as presentes,
                SUM(CASE WHEN quorum=1 THEN poder ELSE 0 END) as poder_presentes,
                SUM(CASE WHEN quorum=1 THEN 1 ELSE 0 END) as registrados,
                SUM(poder) as poder_registrados
            FROM users WHERE activo=1
        ");
        $q->execute();
        $d = $q->fetch(\PDO::FETCH_OBJ);
        $d->presentes = (int)$d->presentes;
        $d->poder_presentes = (int)$d->poder_presentes;
        $d->registrados = (int)$d->registrados;
        $d->poder_registrados = (int)$d->poder_registrados;
        $d->porcentaje = $d->poder_registrados > 0
            ? round(($d->poder_presentes / $d->poder_registrados) * 100, 2) : 0;
        $d->quorum_alcanzado = $d->porcentaje > 50;
        return $d;
    }

    public function listaPresentes()
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT user_id, name, zona, poder, fecha_ingreso FROM users WHERE activo=1 AND (quorum=1 OR (quorum=0 AND fecha_salida >= NOW() - INTERVAL ".(int)env("GRACE_WINDOW_MINUTES", 5)." MINUTE)) ORDER BY fecha_ingreso DESC");
        $q->execute();
        return $q->fetchAll(\PDO::FETCH_OBJ);
    }



    /**
     * Sprint 3: Voto atómico con transacción real (InnoDB).
     * Verifica quórum + grace window, registra votos batch, marca usuario.
     * @return array ["success" => bool, "error" => string|null]
     */
    public function registrarVotoCompleto($candidatos, $usuario)
    {
        $db = $this->DB();

        try {
            $db->beginTransaction();

            // 1. Verificar que no haya votado (SELECT FOR UPDATE = lock de fila)
            $check = $db->prepare("SELECT voto, quorum, fecha_salida FROM users WHERE user_id = :uid FOR UPDATE");
            $check->execute([":uid" => $usuario->user_id]);
            $row = $check->fetch(\PDO::FETCH_OBJ);

            if (!$row) {
                $db->rollBack();
                return ["success" => false, "error" => "Usuario no encontrado"];
            }

            if ($row->voto == "1") {
                $db->rollBack();
                return ["success" => false, "error" => "Ya registró su voto"];
            }

            // 2. Verificar quórum con grace window
            $enQuorum = ($row->quorum == 1) ||
                        ($row->quorum == 0 && $row->fecha_salida &&
                         strtotime($row->fecha_salida) >= time() - (int)(env("GRACE_WINDOW_MINUTES", 5) * 60));

            if (!$enQuorum) {
                $db->rollBack();
                return ["success" => false, "error" => "No tiene quórum vigente"];
            }

            // 3. Insertar votos en batch
            $poder = isset($usuario->poder) ? max(1, (int)$usuario->poder) : 1;
            $zona  = $usuario->zona;
            $values = [];
            $params = [];
            $idx = 0;

            foreach ($candidatos as $numero) {
                $numero = strip_tags($numero);
                for ($i = 0; $i < $poder; $i++) {
                    $values[] = "(:n{$idx}, :z{$idx})";
                    $params[":n{$idx}"] = $numero;
                    $params[":z{$idx}"] = $zona;
                    $idx++;
                }
            }

            if (!empty($values)) {
                $sql = "INSERT INTO votos (numero, zona) VALUES " . implode(", ", $values);
                $ins = $db->prepare($sql);
                $ins->execute($params);
            }

            // 4. Marcar usuario como votó
            $mark = $db->prepare("UPDATE users SET voto = :v, registro = NOW() WHERE user_id = :uid");
            $mark->execute([":v" => "1", ":uid" => $usuario->user_id]);

            $db->commit();
            return ["success" => true, "error" => null];

        } catch (\Exception $e) {
            $db->rollBack();
            error_log("[registrarVotoCompleto] Error: " . $e->getMessage());
            return ["success" => false, "error" => "Error interno al registrar voto"];
        }
    }





    /**
     * Sprint Depuración: Método faltante — requerido por limpiarVotosController.php
     * Vacía la tabla de votos legacy.
     */
    public function limpiarVotos()
    {
        $db = $this->DB();
        return $db->prepare("TRUNCATE TABLE votos")->execute();
    }

}
