<?php

namespace App\Controllers;

use App\Models\LocalModel;
use App\Models\PeliculaModel;
use App\Models\ProductoModel;
use App\Models\PromocionModel;
use App\Models\PromocionProductoModel;
use App\Models\PromocionDiaModel;
use App\Models\MembresiaModel;
use App\Models\ClienteMembresiaModel;
use App\Libraries\PromoHelper;

class Home extends BaseController
{
    private const HORARIOS_DIAS_A_GENERAR = 6;
    private const HORARIOS_POSIBLES = ['13:00', '15:30', '18:00', '20:15', '22:30'];
    private const PRECIOS_POR_TIPO = [
        '2D'   => 18.00,
        '3D'   => 25.00,
        'IMAX' => 32.00,
        '4DX'  => 35.00,
    ];

    /**
     * Muestra la cartelera con películas activas
     */
    public function index()
    {
        $this->ensureHorariosGenerados();

        $model = new PeliculaModel();

        $peliculas = $model->select('pelicula.*, genero.nombre as genero_nombre')
                           ->join('genero', 'genero.id_genero = pelicula.id_genero')
                           ->where('pelicula.estado', 1)
                           ->findAll();

        $productoModel = new ProductoModel();
        $productos = $productoModel->where('disponible', 1)->findAll(8);

        $promocionModel = new PromocionModel();
        $promoProductoModel = new PromocionProductoModel();
        $promociones = $promocionModel->getVigentes();
        $promociones = array_slice($promociones, 0, 3);

        $promocionesTeaser = [];
        foreach ($promociones as $promocion) {
            $precioAnterior = null;
            $precioPromo = null;

            if (!$promocion->esTipoEntrada()) {
                $productosPromo = $promoProductoModel->getProductosDePromocion($promocion->id_promocion);
                if (!empty($productosPromo)) {
                    $bundle = PromoHelper::calcularBundle($promocion, $productosPromo);
                    $precioAnterior = $bundle['precio_normal_total'];
                    $precioPromo = $bundle['precio_promo_total'];
                }
            }

            $promocionesTeaser[] = [
                'promocion' => $promocion,
                'imagen_url' => $promocion->getImagenUrl(),
                'precio_anterior' => $precioAnterior,
                'precio_promo' => $precioPromo,
            ];
        }

        $membresiaModel = new MembresiaModel();
        $membresias = $membresiaModel->orderBy('precio_mensual', 'ASC')->findAll();

        return view('home/cartelera', [
            'peliculas' => $peliculas,
            'productos' => $productos,
            'promociones' => $promocionesTeaser,
            'membresias' => $membresias,
        ]);
    }

    /**
     * Muestra la lista de cines disponibles
     */
    public function cines()
    {
        $model = new LocalModel();
        $locales = $model->where('estado', 1)->findAll();

        return view('home/cines', [
            'locales' => $locales
        ]);
    }

    /**
     * Devuelve el listado de cines en JSON para el sidebar "Elegir cine"
     */
    public function localesJson()
    {
        $model = new LocalModel();
        $locales = $model->where('estado', 1)->orderBy('ciudad', 'ASC')->orderBy('nombre', 'ASC')->findAll();

        $data = array_map(function ($local) {
            return [
                'id_local'  => $local->id_local,
                'nombre'    => $local->nombre,
                'direccion' => $local->direccion,
                'distrito'  => $local->distrito,
                'ciudad'    => $local->ciudad,
                'url'       => base_url('cines/' . $local->id_local),
            ];
        }, $locales);

        return $this->response->setJSON(['locales' => $data]);
    }

    /**
     * Muestra las películas y horarios de un cine
     */
    public function cineDetalle($id_local)
    {
        $this->ensureHorariosGenerados((int) $id_local);

        $localModel = new LocalModel();
        $local = $localModel->find($id_local);

        if (!$local || !$local->estaActivo()) {
            return redirect()->to(base_url('cines'))->with('error', 'El local solicitado no está disponible.');
        }

        $db = \db_connect();
        $funciones = $db->query("SELECT f.id_funcion, f.fecha, f.hora_inicio, f.precio_base, p.id_pelicula, p.titulo, p.poster_url, p.duracion_min, p.idioma, p.clasificacion, g.nombre AS genero_nombre, s.nombre AS sala_nombre, s.tipo AS sala_tipo, s.capacidad,
            (SELECT COUNT(*) FROM detalle_reserva dr WHERE dr.id_funcion = f.id_funcion) AS vendidos
            FROM funcion f
            JOIN pelicula p ON p.id_pelicula = f.id_pelicula
            JOIN genero g ON g.id_genero = p.id_genero
            JOIN sala s ON s.id_sala = f.id_sala
            WHERE s.id_local = ? AND p.estado = 1 AND f.fecha >= CURDATE()
            ORDER BY p.titulo, f.fecha, f.hora_inicio", [$id_local])->getResult();

        // Si hay una promoción de tipo ENTRADA activa en sesión y exige un tipo de
        // sala específico (ej. "Domingo 3D" -> solo salas 3D), se filtran las
        // funciones para mostrar únicamente ese formato.
        $promoTicket = session()->get('promo_ticket');
        $promoTipoSalaFiltro = null;
        $promoNombreFiltro = null;

        if (!empty($promoTicket) && !empty($promoTicket['aplica_tipo_sala'])) {
            $promoTipoSalaFiltro = $promoTicket['aplica_tipo_sala'];
            $promoNombreFiltro = $promoTicket['nombre'];

            $funciones = array_values(array_filter($funciones, function ($f) use ($promoTipoSalaFiltro) {
                return $f->sala_tipo === $promoTipoSalaFiltro;
            }));
        }

        $peliculas = [];
        $fechasDisponibles = [];
        $formatosDisponibles = [];
        $idiomasDisponibles = [];
        $generosDisponibles = [];

        foreach ($funciones as $funcion) {
            $idPelicula = $funcion->id_pelicula;
            if (!isset($peliculas[$idPelicula])) {
                $peliculas[$idPelicula] = [
                    'id_pelicula' => $funcion->id_pelicula,
                    'titulo' => $funcion->titulo,
                    'poster_url' => $funcion->poster_url,
                    'duracion_min' => $funcion->duracion_min,
                    'genero_nombre' => $funcion->genero_nombre,
                    'idioma' => $funcion->idioma,
                    'clasificacion' => $funcion->clasificacion,
                    'horarios' => [],
                    'fechas' => [],
                    'formatos' => [],
                ];
            }

            $peliculas[$idPelicula]['horarios'][] = [
                'id_funcion' => $funcion->id_funcion,
                'fecha' => $funcion->fecha,
                'hora_inicio' => $funcion->hora_inicio,
                'sala_nombre' => $funcion->sala_nombre,
                'sala_tipo' => $funcion->sala_tipo,
                'precio_base' => $funcion->precio_base,
                'capacidad' => $funcion->capacidad,
                'vendidos' => $funcion->vendidos,
            ];

            if (!in_array($funcion->fecha, $peliculas[$idPelicula]['fechas'], true)) {
                $peliculas[$idPelicula]['fechas'][] = $funcion->fecha;
            }
            if (!in_array($funcion->sala_tipo, $peliculas[$idPelicula]['formatos'], true)) {
                $peliculas[$idPelicula]['formatos'][] = $funcion->sala_tipo;
            }

            if (!in_array($funcion->fecha, $fechasDisponibles, true)) {
                $fechasDisponibles[] = $funcion->fecha;
            }

            if (!in_array($funcion->sala_tipo, $formatosDisponibles, true)) {
                $formatosDisponibles[] = $funcion->sala_tipo;
            }

            if (!empty($funcion->idioma) && !in_array($funcion->idioma, $idiomasDisponibles, true)) {
                $idiomasDisponibles[] = $funcion->idioma;
            }

            if (!empty($funcion->genero_nombre) && !in_array($funcion->genero_nombre, $generosDisponibles, true)) {
                $generosDisponibles[] = $funcion->genero_nombre;
            }
        }

        sort($fechasDisponibles);

        return view('home/cine_detalle', [
            'local' => $local,
            'localName' => $local->nombre,
            'peliculas' => array_values($peliculas),
            'fechasDisponibles' => $fechasDisponibles,
            'formatosDisponibles' => $formatosDisponibles,
            'idiomasDisponibles' => $idiomasDisponibles,
            'generosDisponibles' => $generosDisponibles,
            'promoTipoSalaFiltro' => $promoTipoSalaFiltro,
            'promoNombreFiltro' => $promoNombreFiltro,
        ]);
    }

    /**
     * Muestra la página de alimentos y bebidas
     */
    public function alimentos()
    {
        $model = new ProductoModel();
        $productos = $model->where('disponible', 1)->findAll();

        // Si el cliente tiene una membresía vigente, se le muestra el precio ya con descuento.
        $descuentoPct = 0.0;
        if (session()->get('isLoggedIn')) {
            $cmModel = new ClienteMembresiaModel();
            $membresiaActiva = $cmModel->getMembresiaActiva(session()->get('user_id'));
            $descuentoPct = $membresiaActiva ? (float) $membresiaActiva->descuento_pct : 0.0;
        }

        $carrito = session()->get('carrito_temp');
        if (!is_array($carrito)) {
            $carrito = [
                'items' => [],
                'subtotal' => 0.0,
                'descuento_pct' => 0.0,
                'descuento_monto' => 0.0,
                'total' => 0.0,
            ];
        } else {
            $subtotalOriginal = 0.0;
            $subtotalFinal = 0.0;
            foreach ($carrito['items'] as &$item) {
                $item['precio_unitario'] = isset($item['precio_unitario']) ? (float)$item['precio_unitario'] : 0.0;
                $item['cantidad'] = isset($item['cantidad']) ? (int)$item['cantidad'] : 1;
                $item['precio_final'] = round($item['precio_unitario'] * (1 - $descuentoPct / 100), 2);
                $item['subtotal'] = round($item['precio_final'] * $item['cantidad'], 2);
                $subtotalOriginal += round($item['precio_unitario'] * $item['cantidad'], 2);
                $subtotalFinal += $item['subtotal'];
            }
            unset($item);
            $carrito['subtotal'] = $subtotalOriginal;
            $carrito['descuento_pct'] = $descuentoPct;
            $carrito['descuento_monto'] = round($subtotalOriginal - $subtotalFinal, 2);
            $carrito['total'] = round($subtotalFinal, 2);
            session()->set('carrito_temp', $carrito);
        }

        return view('home/alimentos', [
            'productos' => $productos,
            'descuentoPct' => $descuentoPct,
            'carrito' => $carrito
        ]);
    }

    /**
     * Muestra la página de promociones
     */
    public function promociones()
    {
        $promocionModel = new PromocionModel();
        $promoProductoModel = new PromocionProductoModel();
        $promoDiaModel = new PromocionDiaModel();

        $promociones = $promocionModel->orderBy('activo', 'DESC')->orderBy('fecha_inicio', 'DESC')->findAll();

        $data = [];
        foreach ($promociones as $promocion) {
            $dias = $promoDiaModel->getDiasDePromocion($promocion->id_promocion);
            $vigencia = PromoHelper::verificarVigencia($promocion, $dias);

            $item = [
                'promocion' => $promocion,
                'imagen_url' => $promocion->getImagenUrl(),
                'es_entrada' => $promocion->esTipoEntrada(),
                'tipo_sala' => $promocion->getTipoSalaRequerido(),
                'dias' => $dias,
                'vigente' => $vigencia['vigente'],
                'motivo_no_vigente' => $vigencia['motivo'],
                'bundle' => null,
            ];

            if (!$promocion->esTipoEntrada()) {
                // COMBO / PRODUCTO / GENERAL -> se arma el detalle de productos + precios
                $productos = $promoProductoModel->getProductosDePromocion($promocion->id_promocion);
                if (!empty($productos)) {
                    $item['bundle'] = PromoHelper::calcularBundle($promocion, $productos);
                }
            }

            $data[] = $item;
        }

        return view('home/promociones', [
            'promocionesData' => $data,
            'carrito' => carrito_snapshot(),
        ]);
    }

    /**
     * Punto de entrada del botón "Aprovechar promoción" para promociones de tipo ENTRADA
     * (ej. 2x1 Martes). Guarda la promoción en sesión y redirige a la cartelera para
     * que el cliente elija película/función; el descuento se valida y aplica
     * automáticamente al seleccionar los asientos (ver Reserva::seleccionarAsientos()).
     */
    public function aplicarPromocion($id_promocion)
    {
        $promocionModel = new PromocionModel();
        $promocion = $promocionModel->find($id_promocion);

        if (!$promocion) {
            return redirect()->to(base_url('promociones'))->with('error', 'La promoción solicitada no está disponible.');
        }

        if (!$promocion->esTipoEntrada()) {
            // Si por algún motivo se llama esta ruta para un combo, la tratamos como
            // "agregar al carrito" para no dejar al usuario sin acción posible.
            return redirect()->to(base_url('alimentos/promocion/' . $id_promocion));
        }

        $dias = PromoHelper::diasValidos($promocion->id_promocion);
        $vigencia = PromoHelper::verificarVigencia($promocion, $dias);

        if (!$vigencia['vigente']) {
            return redirect()->to(base_url('promociones'))->with('error', $vigencia['motivo']);
        }

        session()->set('promo_ticket', [
            'id_promocion' => $promocion->id_promocion,
            'nombre' => $promocion->nombre,
            'tipo_descuento' => $promocion->tipo_descuento,
            'valor_descuento' => (float) $promocion->valor_descuento,
            'cantidad_minima' => $promocion->getCantidadMinima(),
            'aplica_tipo_sala' => $promocion->getTipoSalaRequerido(),
            'fecha_inicio' => $promocion->fecha_inicio,
            'fecha_fin' => $promocion->fecha_fin,
            'dias' => $dias,
        ]);

        $mensaje = '"' . $promocion->nombre . '" aplicada.';
        $mensaje .= $promocion->getTipoSalaRequerido()
            ? ' Te mostraremos solo funciones ' . $promocion->getTipoSalaRequerido() . ': elige tu película y función, el descuento se validará automáticamente al seleccionar tus asientos.'
            : ' Elige tu película y función: el descuento se validará automáticamente al seleccionar tus asientos.';

        return redirect()->to(base_url('cines'))->with('success', $mensaje);
    }

    /**
     * Cancela la promoción de tipo ENTRADA guardada en sesión (ej. si el usuario
     * quiere ver la cartelera completa sin el filtro de "Domingo 3D").
     */
    public function cancelarPromocionTicket()
    {
        session()->remove('promo_ticket');
        return redirect()->back()->with('success', 'Promoción removida. Ya puedes ver toda la cartelera.');
    }

    /**
     * Muestra la página de membresía con opciones
     */
    public function membresia()
    {
        $model = new MembresiaModel();
        $membresias = $model->findAll();

        return view('home/membresia', [
            'membresias' => $membresias
        ]);
    }

    public function generarHorarios()
    {
        $insertadas = $this->ensureHorariosGenerados();

        return redirect()->back()->with('success', "Se generaron {$insertadas} horarios nuevos.");
    }

    /**
     * Muestra la ficha detallada de una película, con selector de fechas y
     * horarios agrupados por cine y formato.
     */
    public function pelicula($id)
    {
        $this->ensureHorariosGenerados(null, (int) $id);

        $model = new PeliculaModel();

        $pelicula = $model->getPeliculasConGenero($id);

        if (!$pelicula || !$pelicula->estaActiva()) {
            return redirect()->to(base_url('/'))->with('error', 'La película solicitada no está disponible.');
        }

        $db = \db_connect();

        // Fechas con funciones programadas desde hoy en adelante (máx. 7 pestañas)
        $fechasRows = $db->query("
            SELECT DISTINCT f.fecha
            FROM funcion f
            WHERE f.id_pelicula = ? AND f.estado = 'programada' AND f.fecha >= CURDATE()
            ORDER BY f.fecha ASC
            LIMIT 7
        ", [$id])->getResultArray();
        $fechasDisponibles = array_column($fechasRows, 'fecha');

        // Fecha seleccionada por el usuario (?fecha=YYYY-MM-DD), validada contra las disponibles
        $fechaSeleccionada = $this->request->getGet('fecha');
        if (!$fechaSeleccionada || !in_array($fechaSeleccionada, $fechasDisponibles, true)) {
            $fechaSeleccionada = $fechasDisponibles[0] ?? date('Y-m-d');
        }

        // Todos los formatos en los que se proyecta la película (para el resumen del lado izquierdo)
        $formatosRows = $db->query("
            SELECT DISTINCT s.tipo
            FROM funcion f JOIN sala s ON s.id_sala = f.id_sala
            WHERE f.id_pelicula = ? AND f.estado = 'programada'
            ORDER BY s.tipo ASC
        ", [$id])->getResultArray();
        $formatosDisponibles = array_column($formatosRows, 'tipo');

        // Funciones del día seleccionado, con ocupación de asientos para el indicador de disponibilidad
        $funciones = $db->query("
            SELECT f.id_funcion, f.fecha, f.hora_inicio, f.precio_base,
                   s.id_sala, s.nombre AS sala_nombre, s.tipo AS sala_tipo, s.capacidad,
                   l.id_local, l.nombre AS local_nombre, l.direccion,
                   (SELECT COUNT(*) FROM detalle_reserva dr WHERE dr.id_funcion = f.id_funcion) AS vendidos
            FROM funcion f
            JOIN sala s ON s.id_sala = f.id_sala
            JOIN local l ON l.id_local = s.id_local
            WHERE f.id_pelicula = ? AND f.estado = 'programada' AND f.fecha = ?
            ORDER BY l.nombre ASC, s.tipo ASC, f.hora_inicio ASC
        ", [$id, $fechaSeleccionada])->getResult();

        // Agrupar por formato, manteniendo la sala y el cine en cada función
        $porFormato = [];
        foreach ($funciones as $fn) {
            $keyFormato = $fn->sala_tipo;
            if (!isset($porFormato[$keyFormato])) {
                $porFormato[$keyFormato] = [
                    'tipo' => $fn->sala_tipo,
                    'funciones' => []
                ];
            }
            $porFormato[$keyFormato]['funciones'][] = $fn;
        }

        return view('home/detalle_pelicula', [
            'pelicula'            => $pelicula,
            'fechasDisponibles'   => $fechasDisponibles,
            'fechaSeleccionada'   => $fechaSeleccionada,
            'formatosDisponibles' => $formatosDisponibles,
            'porFormato'          => $porFormato
        ]);
    }

    private function ensureHorariosGenerados(?int $idLocal = null, ?int $idPelicula = null): int
    {
        $db = db_connect();

        $peliculasQuery = $db->table('pelicula')
            ->select('id_pelicula, duracion_min')
            ->where('estado', 1);

        if ($idPelicula !== null) {
            $peliculasQuery->where('id_pelicula', $idPelicula);
        }

        $peliculas = $peliculasQuery->get()->getResultArray();

        $salasQuery = $db->table('sala s')
            ->select('s.id_sala, s.tipo, s.capacidad, s.id_local, l.nombre AS local_nombre')
            ->join('local l', 'l.id_local = s.id_local')
            ->where('s.estado', 1);

        if ($idLocal !== null) {
            $salasQuery->where('s.id_local', $idLocal);
        }

        $salas = $salasQuery->get()->getResultArray();

        if (empty($peliculas) || empty($salas)) {
            return 0;
        }

        $salasPorLocal = [];
        foreach ($salas as $sala) {
            $salasPorLocal[$sala['id_local']][] = $sala;
        }

        $locales = array_keys($salasPorLocal);
        $insertadas = 0;
        $indiceLocal = 0;

        foreach ($peliculas as $pelicula) {
            $cantidadLocales = count($locales) <= 3 ? count($locales) : min(2, count($locales));
            $localesAsignados = [];

            if (count($locales) <= 3) {
                $localesAsignados = $locales;
            } else {
                for ($i = 0; $i < $cantidadLocales; $i++) {
                    $localesAsignados[] = $locales[($indiceLocal + $i) % count($locales)];
                }
                $indiceLocal++;
            }

            foreach ($localesAsignados as $idLocalAsignado) {
                $salasDelLocal = $salasPorLocal[$idLocalAsignado];
                $sala = $salasDelLocal[array_rand($salasDelLocal)];
                $precioBase = self::PRECIOS_POR_TIPO[$sala['tipo']] ?? 20.00;

                for ($dia = 0; $dia < self::HORARIOS_DIAS_A_GENERAR; $dia++) {
                    $fecha = date('Y-m-d', strtotime("+{$dia} day"));
                    $horariosDelDia = self::HORARIOS_POSIBLES;
                    shuffle($horariosDelDia);
                    $horariosDelDia = array_slice($horariosDelDia, 0, 2);

                    foreach ($horariosDelDia as $horaInicio) {
                        $horaInicioFormateada = $horaInicio . ':00';

                        $existe = $db->table('funcion')
                            ->where('id_sala', $sala['id_sala'])
                            ->where('fecha', $fecha)
                            ->where('hora_inicio', $horaInicioFormateada)
                            ->countAllResults();

                        if ($existe > 0) {
                            continue;
                        }

                        $horaFin = date('H:i:s', strtotime($horaInicioFormateada) + ($pelicula['duracion_min'] + 20) * 60);
                        $db->table('funcion')->insert([
                            'id_pelicula' => $pelicula['id_pelicula'],
                            'id_sala' => $sala['id_sala'],
                            'fecha' => $fecha,
                            'hora_inicio' => $horaInicioFormateada,
                            'hora_fin' => $horaFin,
                            'precio_base' => $precioBase,
                            'estado' => 'programada',
                        ]);

                        $insertadas++;
                    }
                }
            }
        }

        return $insertadas;
    }
}
