<?php
namespace app\models;

require_once __DIR__ . '/conexion.php';

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

/**
 * votacion.php — Sprint 9A
 * Agrega campo 'pregunta' (texto de la pregunta/nota por votación)
 */
class votacion extends conexion
{
    private function horaColombia()
    {
        return (new DateTime('now', new DateTimeZone('America/Bogota')))->format('Y-m-d H:i:s');
    }

    // ─── VOTACIONES ──────────────────────────────────────────

    public function listar()
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT * FROM votaciones ORDER BY fecha_creacion DESC");
        $q->execute();
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    public function obtener($id)
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT * FROM votaciones WHERE id = :id");
        $q->execute([':id' => $id]);
        return $q->fetch(PDO::FETCH_OBJ);
    }

    public function crear($titulo, $tipo = 'eleccion', $descripcion = '', $min = 1, $max = 1, $creadoPor = null, $pregunta = '')
    {
        $db = $this->DB();
        $q = $db->prepare("
            INSERT INTO votaciones (titulo, tipo, descripcion, pregunta, min_candidatos, max_candidatos, creado_por, fecha_creacion)
            VALUES (:titulo, :tipo, :desc, :pregunta, :min, :max, :creado, :fecha)
        ");
        $q->execute([
            ':titulo'   => $titulo,    ':tipo'    => $tipo,
            ':desc'     => $descripcion, ':pregunta' => $pregunta,
            ':min'      => $min,       ':max'     => $max,
            ':creado'   => $creadoPor, ':fecha'   => $this->horaColombia(),
        ]);
        return $db->lastInsertId();
    }

    public function actualizar($id, $titulo, $tipo, $descripcion, $min, $max, $pregunta = '')
    {
        $db = $this->DB();
        $q = $db->prepare("
            UPDATE votaciones
            SET titulo=:titulo, tipo=:tipo, descripcion=:desc, pregunta=:pregunta,
                min_candidatos=:min, max_candidatos=:max
            WHERE id=:id
        ");
        return $q->execute([
            ':id' => $id, ':titulo' => $titulo, ':tipo' => $tipo,
            ':desc' => $descripcion, ':pregunta' => $pregunta, ':min' => $min, ':max' => $max,
        ]);
    }

    public function cambiarEstado($id, $nuevoEstado)
    {
        $db = $this->DB();
        $ahora    = $this->horaColombia();
        $votacion = $this->obtener($id);
        if (!$votacion) return false;

        $transiciones = [
            'configuracion' => ['abierta'],
            'abierta'       => ['cerrada'],
            'cerrada'       => ['finalizada', 'abierta'],
            'finalizada'    => [],
        ];
        if (!in_array($nuevoEstado, $transiciones[$votacion->estado] ?? [])) return false;

        $campos = "estado = :estado";
        $params = [':id' => $id, ':estado' => $nuevoEstado];

        if ($nuevoEstado === 'abierta' && !$votacion->fecha_apertura) {
            $campos .= ", fecha_apertura = :fecha"; $params[':fecha'] = $ahora;
        }
        if ($nuevoEstado === 'cerrada') {
            $campos .= ", fecha_cierre = :fecha"; $params[':fecha'] = $ahora;
        }
        $q = $db->prepare("UPDATE votaciones SET $campos WHERE id = :id");
        return $q->execute($params);
    }

    public function eliminar($id)
    {
        $db = $this->DB();
        $q = $db->prepare("DELETE FROM votaciones WHERE id=:id");
        $q->execute([':id' => $id]);
        return $q->rowCount() > 0;
    }

    // ─── CANDIDATOS ──────────────────────────────────────────

    public function listarCandidatos($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT * FROM votacion_candidatos WHERE votacion_id=:vid ORDER BY zona, orden, id");
        $q->execute([':vid' => $votacionId]);
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    public function agregarCandidato($votacionId, $numero, $nombre, $posicion = '', $cedula = '', $zona = '', $descripcion = 'NO')
    {
        $db = $this->DB();
        $q = $db->prepare("
            INSERT INTO votacion_candidatos (votacion_id, numero, nombre, posicion, cedula, zona, descripcion)
            VALUES (:vid, :num, :nombre, :posicion, :cedula, :zona, :desc)
        ");
        return $q->execute([
            ':vid' => $votacionId, ':num' => $numero, ':nombre' => $nombre,
            ':posicion' => $posicion, ':cedula' => $cedula, ':zona' => $zona, ':desc' => $descripcion,
        ]);
    }

    public function eliminarCandidato($candidatoId, $votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            DELETE vc FROM votacion_candidatos vc
            INNER JOIN votaciones v ON v.id = vc.votacion_id
            WHERE vc.id=:cid AND vc.votacion_id=:vid AND v.estado='configuracion'
        ");
        $q->execute([':cid' => $candidatoId, ':vid' => $votacionId]);
        return $q->rowCount() > 0;
    }

    public function limpiarCandidatosVotacion($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            DELETE vc FROM votacion_candidatos vc
            INNER JOIN votaciones v ON v.id=vc.votacion_id
            WHERE vc.votacion_id=:vid AND v.estado='configuracion'
        ");
        $q->execute([':vid' => $votacionId]);
        return $q->rowCount();
    }

    public function subirCandidatosBatchExcel($votacionId, $filas, $zona)
    {
        $db = $this->DB();
        $q = $db->prepare("
            INSERT INTO votacion_candidatos
                (votacion_id, numero, nombre, posicion, cedula, zona, descripcion, orden)
            VALUES (:vid, :num, :nombre, :posicion, :cedula, :zona, :desc, :orden)
        ");
        $count = 0;
        foreach ($filas as $i => $fila) {
            $numero   = isset($fila[0]) ? trim($fila[0]) : '';
            $nombre   = isset($fila[1]) ? trim($fila[1]) : '';
            $posicion = isset($fila[2]) ? trim($fila[2]) : '';
            $cedula   = isset($fila[3]) ? trim($fila[3]) : '';
            if ($numero === '' || $nombre === '') continue;
            $tienefoto = ($cedula !== '') ? 'SI' : 'NO';
            $q->execute([
                ':vid' => $votacionId, ':num' => $numero, ':nombre' => $nombre,
                ':posicion' => $posicion, ':cedula' => $cedula, ':zona' => $zona,
                ':desc' => $tienefoto, ':orden' => ($i + 1),
            ]);
            $count++;
        }
        return $count;
    }

    // ─── RESULTADOS (batch, sin N+1) ─────────────────────────

    public function resultadosVotacion($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            SELECT vc.id, vc.numero, vc.nombre, vc.posicion, vc.cedula, vc.zona, vc.descripcion,
                   COALESCE(SUM(vv.cantidad), 0) AS total_votos
            FROM votacion_candidatos vc
            LEFT JOIN votacion_votos vv ON vv.candidato_id=vc.id AND vv.votacion_id=vc.votacion_id
            WHERE vc.votacion_id = :vid
            GROUP BY vc.id, vc.numero, vc.nombre, vc.posicion, vc.cedula, vc.zona, vc.descripcion
            ORDER BY vc.zona ASC, total_votos DESC, vc.orden ASC
        ");
        $q->execute([':vid' => $votacionId]);
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    public function resultadosPorZona($votacionId, $zona)
    {
        $db = $this->DB();
        $q = $db->prepare("
            SELECT vc.id, vc.numero, vc.nombre, vc.posicion, vc.cedula, vc.zona, vc.descripcion,
                   COALESCE(SUM(vv.cantidad), 0) AS total_votos
            FROM votacion_candidatos vc
            LEFT JOIN votacion_votos vv ON vv.candidato_id=vc.id AND vv.votacion_id=vc.votacion_id
            WHERE vc.votacion_id=:vid AND vc.zona=:zona
            GROUP BY vc.id, vc.numero, vc.nombre, vc.posicion, vc.cedula, vc.zona, vc.descripcion
            ORDER BY total_votos DESC, vc.orden ASC
        ");
        $q->execute([':vid' => $votacionId, ':zona' => $zona]);
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    public function totalVotosPorZona($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            SELECT vc.zona, COALESCE(SUM(vv.cantidad), 0) AS total_votos
            FROM votacion_candidatos vc
            LEFT JOIN votacion_votos vv ON vv.candidato_id=vc.id AND vv.votacion_id=vc.votacion_id
            WHERE vc.votacion_id=:vid
            GROUP BY vc.zona
        ");
        $q->execute([':vid' => $votacionId]);
        $r = [];
        while ($row = $q->fetch(PDO::FETCH_OBJ)) { $r[$row->zona] = (int)$row->total_votos; }
        return $r;
    }

    public function totalVotosVotacion($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT COALESCE(SUM(cantidad),0) AS total FROM votacion_votos WHERE votacion_id=:vid");
        $q->execute([':vid' => $votacionId]);
        return (int)$q->fetch(PDO::FETCH_OBJ)->total;
    }

    public function censoPorZonas()
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT zona, SUM(poder) AS censo FROM users WHERE activo=1 GROUP BY zona");
        $q->execute();
        $r = []; $total = 0;
        while ($row = $q->fetch(PDO::FETCH_OBJ)) {
            $r[$row->zona] = (int)$row->censo;
            $total += (int)$row->censo;
        }
        $r['_total'] = $total;
        return $r;
    }

    public function zonasVotacion($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            SELECT DISTINCT zona FROM votacion_candidatos
            WHERE votacion_id=:vid AND zona IS NOT NULL AND zona != ''
            ORDER BY zona
        ");
        $q->execute([':vid' => $votacionId]);
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    public function participantesVotacion($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("
            SELECT COUNT(*) AS total,
                   SUM(CASE WHEN voto_registrado=1 THEN 1 ELSE 0 END) AS votaron
            FROM votacion_participantes WHERE votacion_id=:vid
        ");
        $q->execute([':vid' => $votacionId]);
        return $q->fetch(PDO::FETCH_OBJ);
    }

    public function contarCandidatos($votacionId)
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT COUNT(*) AS total FROM votacion_candidatos WHERE votacion_id=:vid");
        $q->execute([':vid' => $votacionId]);
        return (int)$q->fetch(PDO::FETCH_OBJ)->total;
    }

    // ─── FLUJO USUARIO ───────────────────────────────────────

    public function obtenerAbierta()
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT * FROM votaciones WHERE estado='abierta' ORDER BY fecha_apertura DESC LIMIT 1");
        $q->execute();
        return $q->fetch(PDO::FETCH_OBJ);
    }

    public function usuarioYaVoto($votacionId, $userId)
    {
        $db = $this->DB();
        $q = $db->prepare("SELECT voto_registrado FROM votacion_participantes WHERE votacion_id=:vid AND user_id=:uid");
        $q->execute([':vid' => $votacionId, ':uid' => $userId]);
        $row = $q->fetch(PDO::FETCH_OBJ);
        return $row && $row->voto_registrado == 1;
    }

    public function candidatosPorZona($votacionId, $zona = null)
    {
        $db = $this->DB();
        if ($zona) {
            $q = $db->prepare("SELECT * FROM votacion_candidatos WHERE votacion_id=:vid AND zona=:zona ORDER BY orden, id");
            $q->execute([':vid' => $votacionId, ':zona' => $zona]);
        } else {
            $q = $db->prepare("SELECT * FROM votacion_candidatos WHERE votacion_id=:vid ORDER BY orden, id");
            $q->execute([':vid' => $votacionId]);
        }
        return $q->fetchAll(PDO::FETCH_OBJ);
    }

    // ─── VOTO ATÓMICO ────────────────────────────────────────

    public function registrarVotoDinamico($votacionId, $candidatosIds, $usuario)
    {
        $db = $this->DB();
        try {
            $db->beginTransaction();
            $vot = $db->prepare("SELECT estado FROM votaciones WHERE id=:vid FOR UPDATE");
            $vot->execute([':vid' => $votacionId]);
            $v = $vot->fetch(PDO::FETCH_OBJ);
            if (!$v || $v->estado !== 'abierta') {
                $db->rollBack(); return ['success' => false, 'error' => 'Votación no abierta'];
            }

            $db->prepare("INSERT IGNORE INTO votacion_participantes (votacion_id, user_id) VALUES (:vid,:uid)")
               ->execute([':vid' => $votacionId, ':uid' => $usuario->user_id]);

            $part = $db->prepare("SELECT voto_registrado FROM votacion_participantes WHERE votacion_id=:vid AND user_id=:uid FOR UPDATE");
            $part->execute([':vid' => $votacionId, ':uid' => $usuario->user_id]);
            $p = $part->fetch(PDO::FETCH_OBJ);
            if ($p->voto_registrado == 1) {
                $db->rollBack(); return ['success' => false, 'error' => 'Ya votó'];
            }

            $poder  = isset($usuario->poder) ? max(1, (int)$usuario->poder) : 1;
            $ins    = $db->prepare("INSERT INTO votacion_votos (votacion_id,user_id,candidato_id,cantidad,zona,fecha_voto) VALUES (:vid,:uid,:cid,:cant,:zona,:fecha)");
            $ahora  = $this->horaColombia();
            foreach ($candidatosIds as $cid) {
                $ins->execute([':vid' => $votacionId, ':uid' => $usuario->user_id, ':cid' => (int)$cid,
                               ':cant' => $poder, ':zona' => $usuario->zona, ':fecha' => $ahora]);
            }
            $db->prepare("UPDATE votacion_participantes SET voto_registrado=1, fecha_voto=:fecha WHERE votacion_id=:vid AND user_id=:uid")
               ->execute([':fecha' => $ahora, ':vid' => $votacionId, ':uid' => $usuario->user_id]);

            // Sprint 10A: marcar users.voto para que flujo legacy y votar.php detecten voto realizado
            $db->prepare("UPDATE users SET voto = '1', registro = NOW() WHERE user_id = :uid")
               ->execute([':uid' => $usuario->user_id]);

            $db->commit();
            return ['success' => true, 'error' => null, 'votos' => count($candidatosIds) * $poder];
        } catch (Exception $e) {
            if ($db->inTransaction()) $db->rollBack();
            error_log('[registrarVotoDinamico] ' . $e->getMessage());
            return ['success' => false, 'error' => 'Error interno'];
        }
    }

    /**
     * Sprint 10B: Totales de votos, participantes y candidatos en 3 queries para N votaciones
     * Elimina el N+1 de resultados.php y votaciones.php
     */
    public function totalesVotacionesBatch(array $ids): array
    {
        if (empty($ids)) return [];
        $db   = $this->DB();
        $in   = implode(',', array_fill(0, count($ids), '?'));
        $vals = array_values($ids);

        // Total votos por votación
        $stmt = $db->prepare("SELECT votacion_id, SUM(cantidad) AS total FROM votacion_votos WHERE votacion_id IN ($in) GROUP BY votacion_id");
        $stmt->execute($vals);
        $votos = [];
        foreach ($stmt->fetchAll(\PDO::FETCH_OBJ) as $r) { $votos[$r->votacion_id] = (int)$r->total; }

        // Candidatos por votación
        $stmt2 = $db->prepare("SELECT votacion_id, COUNT(*) AS total FROM votacion_candidatos WHERE votacion_id IN ($in) GROUP BY votacion_id");
        $stmt2->execute($vals);
        $cands = [];
        foreach ($stmt2->fetchAll(\PDO::FETCH_OBJ) as $r) { $cands[$r->votacion_id] = (int)$r->total; }

        // Participantes por votación
        $stmt3 = $db->prepare("SELECT votacion_id, COUNT(*) AS total, SUM(voto_registrado) AS votaron FROM votacion_participantes WHERE votacion_id IN ($in) GROUP BY votacion_id");
        $stmt3->execute($vals);
        $parts = [];
        foreach ($stmt3->fetchAll(\PDO::FETCH_OBJ) as $r) {
            $parts[$r->votacion_id] = (object)['total' => (int)$r->total, 'votaron' => (int)$r->votaron];
        }

        $result = [];
        foreach ($ids as $id) {
            $result[$id] = (object)[
                'total_votos'    => $votos[$id]  ?? 0,
                'num_candidatos' => $cands[$id]  ?? 0,
                'participantes'  => $parts[$id]  ?? (object)['total' => 0, 'votaron' => 0],
            ];
        }
        return $result;
    }

}
