<?php

namespace App\Controllers\Admin;

use App\Controllers\BaseController;
use App\Models\FuncionModel;
use App\Models\PeliculaModel;
use App\Models\LocalModel;
use App\Models\SalaModel;
use App\Models\AsientoModel;
use App\Entities\Funcion;

class Funciones extends BaseController
{
    /**
     * Listado de funciones con filtros y paginación
     */
    public function index()
    {
        $funcionModel = new FuncionModel();
        $peliculaModel = new PeliculaModel();
        $localModel = new LocalModel();
        $salaModel = new SalaModel();

        $terminoBusqueda = $this->request->getGet('q'); // 'q' es el parametro GET que viene del formulario de búsqueda
        $filtros = [
            'id_pelicula' => $this->request->getGet('id_pelicula'),
            'id_local'    => $this->request->getGet('id_local'),
            'id_sala'     => $this->request->getGet('id_sala'),
            'fecha'       => $this->request->getGet('fecha'),
        ];

        $builder = $funcionModel->getFuncionesConDetalles($terminoBusqueda, $filtros);
        $funciones = $builder->paginate(10, 'default');

        // Cargar datos para los select de filtros
        $peliculas = $peliculaModel->where('estado', 1)->orderBy('titulo', 'ASC')->findAll();
        $locales = $localModel->where('estado', 1)->orderBy('nombre', 'ASC')->findAll();
        
        $salas = [];
        if (!empty($filtros['id_local'])) {
            $salas = $salaModel->where('id_local', $filtros['id_local'])->where('estado', 1)->orderBy('nombre', 'ASC')->findAll();
        }

        return view('admin/funciones/index', [
            'funciones'       => $funciones,
            'pager'           => $funcionModel->pager,
            'terminoBusqueda' => $terminoBusqueda,
            'filtros'         => $filtros,
            'peliculas'       => $peliculas,
            'locales'         => $locales,
            'salas'           => $salas,
        ]);
    }

    /**
     * Formulario de creación
     */
    public function crear()
    {
        $peliculaModel = new PeliculaModel();
        $localModel = new LocalModel();

        return view('admin/funciones/crear', [
            'peliculas' => $peliculaModel->where('estado', 1)->orderBy('titulo', 'ASC')->findAll(),
            'locales'   => $localModel->where('estado', 1)->orderBy('nombre', 'ASC')->findAll(),
        ]);
    }

    /**
     * Endpoint JSON para retornar las salas de un local.
     * Si se pasan los parámetros opcionales id_pelicula, fecha, hora_inicio,
     * cada sala retorna también los campos 'disponible' (bool) y 'conflicto' (string).
     */
    public function getSalasByLocal($id_local)
    {
        $salaModel    = new SalaModel();
        $funcionModel = new FuncionModel();

        $salas = $salaModel->where('id_local', $id_local)
                           ->where('estado', 1)
                           ->orderBy('nombre', 'ASC')
                           ->findAll();

        // Parámetros opcionales para verificar conflictos en tiempo real
        $id_pelicula = $this->request->getGet('id_pelicula');
        $fecha       = $this->request->getGet('fecha');
        $hora_inicio = $this->request->getGet('hora_inicio');
        $id_funcion  = $this->request->getGet('id_funcion'); // para excluir en edición

        // Si se tienen todos los datos necesarios, calculamos disponibilidad por sala
        if ($id_pelicula && $fecha && $hora_inicio) {
            $peliculaModel = new PeliculaModel();
            $pelicula = $peliculaModel->find($id_pelicula);

            if ($pelicula) {
                $duracion = (int)$pelicula->duracion_min;
                $hora_fin = date('H:i:s', strtotime($hora_inicio) + ($duracion + 15) * 60);

                foreach ($salas as &$sala) {
                    $builder = $funcionModel->select('funcion.*, pelicula.titulo as pelicula_titulo')
                                            ->join('pelicula', 'pelicula.id_pelicula = funcion.id_pelicula')
                                            ->where('funcion.id_sala', $sala->id_sala)
                                            ->where('funcion.fecha', $fecha)
                                            ->where('funcion.hora_inicio <', $hora_fin)
                                            ->where('funcion.hora_fin >', $hora_inicio);

                    if (!empty($id_funcion)) {
                        $builder->where('funcion.id_funcion !=', $id_funcion);
                    }

                    $conflict = $builder->first();

                    if ($conflict) {
                        $sala->disponible = false;
                        $sala->conflicto  = "Ocupada: '{$conflict->pelicula_titulo}' de {$conflict->hora_inicio} a {$conflict->hora_fin}";
                    } else {
                        $sala->disponible = true;
                        $sala->conflicto  = null;
                    }
                }
                unset($sala); // limpiar referencia
            }
        }

        return $this->response->setJSON($salas);
    }

    /**
     * Endpoint JSON para retornar los asientos de una sala
     */
    public function getAsientosSala($id_sala)
    {
        $asientoModel = new AsientoModel();
        $asientos = $asientoModel->where('id_sala', $id_sala)
                                 ->orderBy('fila', 'ASC')
                                 ->orderBy('numero', 'ASC')
                                 ->findAll();

        return $this->response->setJSON($asientos);
    }

    /**
     * Endpoint JSON para retornar la ocupación de asientos en tiempo real
     */
    public function getOcupacion($id_funcion)
    {
        $funcionModel = new FuncionModel();
        $funcion = $funcionModel->find($id_funcion);
        
        if (!$funcion) {
            return $this->response->setJSON(['status' => 'error', 'message' => 'Función no encontrada.'])->setStatusCode(404);
        }

        $asientoModel = new AsientoModel();
        $asientos = $asientoModel->getAsientosConOcupacion($id_funcion, $funcion->id_sala);

        return $this->response->setJSON([
            'status' => 'success',
            'asientos' => $asientos
        ]);
    }

    /**
     * Guardar nueva función con validación de traslapes y cálculo automático de hora_fin
     */
    public function guardar()
    {
        $funcionModel = new FuncionModel();
        $peliculaModel = new PeliculaModel();

        // Validaciones iniciales del formulario (se excluye hora_fin porque se calcula)
        $rules = [
            'id_pelicula' => 'required|integer',
            'id_sala'     => 'required|integer',
            'fecha'       => 'required|valid_date[Y-m-d]',
            'hora_inicio' => 'required',
            'precio_base' => 'required|numeric|greater_than[0]',
            'estado'      => 'required|in_list[programada,en_curso,finalizada,cancelada]',
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $id_pelicula = $this->request->getPost('id_pelicula');
        $id_sala = $this->request->getPost('id_sala');
        $fecha = $this->request->getPost('fecha');
        $hora_inicio = $this->request->getPost('hora_inicio');
        $precio_base = $this->request->getPost('precio_base');
        $estado = $this->request->getPost('estado');

        // Restricción: la función debe programarse con al menos 1 día de anticipación (a partir de mañana)
        $fecha_minima = date('Y-m-d', strtotime('+1 day'));
        if ($fecha < $fecha_minima) {
            return redirect()->back()->withInput()->with('error', 'La función se debe programar con al menos 1 día de anticipación (a partir de mañana).');
        }

        // Buscar película para calcular duración
        $pelicula = $peliculaModel->find($id_pelicula);
        if (!$pelicula) {
            return redirect()->back()->withInput()->with('error', 'Película seleccionada no válida.');
        }

        // Calcular hora_fin: inicio + duración (mins) + 15 mins de limpieza
        $duracion = (int)$pelicula->duracion_min;
        $hora_fin = date('H:i:s', strtotime($hora_inicio) + ($duracion + 15) * 60);

        // Validar traslapes de horarios en la misma sala y fecha
        // Condición: (hora_inicio < :proposed_hora_fin AND hora_fin > :proposed_hora_inicio)
        $conflict = $funcionModel->where('id_sala', $id_sala)
                                 ->where('fecha', $fecha)
                                 ->where('hora_inicio <', $hora_fin)
                                 ->where('hora_fin >', $hora_inicio)
                                 ->first();

        if ($conflict) {
            return redirect()->back()->withInput()->with('error', "Conflicto de horario: La sala seleccionada ya cuenta con una función programada ({$conflict->hora_inicio} - {$conflict->hora_fin}) que se traslapa con el horario propuesto.");
        }

        // Crear la entidad y guardar
        $funcion = new Funcion();
        $funcion->id_pelicula = $id_pelicula;
        $funcion->id_sala = $id_sala;
        $funcion->fecha = $fecha;
        $funcion->hora_inicio = $hora_inicio;
        $funcion->hora_fin = $hora_fin;
        $funcion->precio_base = $precio_base;
        $funcion->estado = $estado;

        if ($funcionModel->save($funcion)) {
            return redirect()->to(base_url('admin/funciones'))->with('success', 'Función creada exitosamente.');
        }

        return redirect()->back()->withInput()->with('error', 'No se pudo guardar la función.');
    }

    /**
     * Formulario de edición
     */
    public function editar($id)
    {
        $funcionModel = new FuncionModel();
        $funcion = $funcionModel->find($id);

        if (!$funcion) {
            return redirect()->to(base_url('admin/funciones'))->with('error', 'Función no encontrada.');
        }

        $peliculaModel = new PeliculaModel();
        $localModel = new LocalModel();
        $salaModel = new SalaModel();

        // Obtener local actual de la sala
        $salaActual = $salaModel->find($funcion->id_sala);
        $id_local_actual = $salaActual ? $salaActual->id_local : null;

        return view('admin/funciones/editar', [
            'funcion'          => $funcion,
            'peliculas'        => $peliculaModel->where('estado', 1)->orderBy('titulo', 'ASC')->findAll(),
            'locales'          => $localModel->where('estado', 1)->orderBy('nombre', 'ASC')->findAll(),
            'salas'            => $id_local_actual ? $salaModel->where('id_local', $id_local_actual)->where('estado', 1)->orderBy('nombre', 'ASC')->findAll() : [],
            'id_local_actual'  => $id_local_actual
        ]);
    }

    /**
     * Actualizar función con validación de traslapes y cálculo automático de hora_fin
     */
    public function actualizar($id)
    {
        $funcionModel = new FuncionModel();
        $funcion = $funcionModel->find($id);

        if (!$funcion) {
            return redirect()->to(base_url('admin/funciones'))->with('error', 'Función no encontrada.');
        }

        $rules = [
            'id_pelicula' => 'required|integer',
            'id_sala'     => 'required|integer',
            'fecha'       => 'required|valid_date[Y-m-d]',
            'hora_inicio' => 'required',
            'precio_base' => 'required|numeric|greater_than[0]',
            'estado'      => 'required|in_list[programada,en_curso,finalizada,cancelada]',
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $id_pelicula = $this->request->getPost('id_pelicula');
        $id_sala = $this->request->getPost('id_sala');
        $fecha = $this->request->getPost('fecha');
        $hora_inicio = $this->request->getPost('hora_inicio');
        $precio_base = $this->request->getPost('precio_base');
        $estado = $this->request->getPost('estado');

        // Restricción: la función debe programarse con al menos 1 día de anticipación (a partir de mañana)
        $fecha_minima = date('Y-m-d', strtotime('+1 day'));
        if ($fecha < $fecha_minima) {
            return redirect()->back()->withInput()->with('error', 'La función se debe programar con al menos 1 día de anticipación (a partir de mañana).');
        }

        $peliculaModel = new PeliculaModel();
        $pelicula = $peliculaModel->find($id_pelicula);
        if (!$pelicula) {
            return redirect()->back()->withInput()->with('error', 'Película seleccionada no válida.');
        }

        // Calcular hora_fin
        $duracion = (int)$pelicula->duracion_min;
        $hora_fin = date('H:i:s', strtotime($hora_inicio) + ($duracion + 15) * 60);

        // Validar traslapes de horarios (excluyendo la función actual)
        $conflict = $funcionModel->where('id_sala', $id_sala)
                                 ->where('fecha', $fecha)
                                 ->where('hora_inicio <', $hora_fin)
                                 ->where('hora_fin >', $hora_inicio)
                                 ->where('id_funcion !=', $id)
                                 ->first();

        if ($conflict) {
            return redirect()->back()->withInput()->with('error', "Conflicto de horario: La sala seleccionada ya cuenta con una función programada ({$conflict->hora_inicio} - {$conflict->hora_fin}) que se traslapa con el horario propuesto.");
        }

        $funcion->id_pelicula = $id_pelicula;
        $funcion->id_sala = $id_sala;
        $funcion->fecha = $fecha;
        $funcion->hora_inicio = $hora_inicio;
        $funcion->hora_fin = $hora_fin;
        $funcion->precio_base = $precio_base;
        $funcion->estado = $estado;

        if ($funcionModel->save($funcion)) {
            return redirect()->to(base_url('admin/funciones'))->with('success', 'Función actualizada correctamente.');
        }

        return redirect()->back()->withInput()->with('error', 'No se pudo actualizar la función.');
    }

    /**
     * Eliminar función si no tiene ventas o reservas
     */
    public function eliminar($id)
    {
        $funcionModel = new FuncionModel();
        
        try {
            if ($funcionModel->delete($id)) {
                return redirect()->to(base_url('admin/funciones'))->with('success', 'Función eliminada correctamente.');
            }
        } catch (\Exception $e) {
            return redirect()->to(base_url('admin/funciones'))->with('error', 'No se puede eliminar esta función porque ya contiene asientos vendidos o reservados.');
        }

        return redirect()->to(base_url('admin/funciones'))->with('error', 'No se pudo eliminar la función.');
    }

    /**
     * Ver ocupación y detalles de la función (Mapa de Asientos SVG)
     */
    public function ver($id)
    {
        $funcionModel = new FuncionModel();
        
        // Obtener detalles completos de la función
        $funcion = $funcionModel->getFuncionesConDetalles(null, [])
                                ->where('funcion.id_funcion', $id)
                                ->first();

        if (!$funcion) {
            return redirect()->to(base_url('admin/funciones'))->with('error', 'Función no encontrada.');
        }

        $asientoModel = new AsientoModel();
        // Verificar si la sala de la función tiene asientos configurados
        $asientosCount = $asientoModel->where('id_sala', $funcion->id_sala)->countAllResults();

        return view('admin/funciones/ver', [
            'funcion'       => $funcion,
            'tieneAsientos' => $asientosCount > 0,
        ]);
    }

    /**
     * Autogenerar asientos para una sala que no tiene
     */
    public function generarAsientos($id_sala)
    {
        $salaModel = new SalaModel();
        $sala = $salaModel->find($id_sala);
        if (!$sala) {
            return $this->response->setJSON(['status' => 'error', 'message' => 'Sala no encontrada.'])->setStatusCode(404);
        }

        $asientoModel = new AsientoModel();
        
        // Verificar si los asientos de esta sala tienen reservas asociadas
        $hasReservations = $asientoModel->db->table('detalle_reserva')
            ->join('asiento', 'asiento.id_asiento = detalle_reserva.id_asiento')
            ->where('asiento.id_sala', $id_sala)
            ->countAllResults();

        if ($hasReservations > 0) {
            return $this->response->setJSON([
                'status' => 'error', 
                'message' => 'No se pueden regenerar los asientos porque la sala ya cuenta con reservas activas asociadas.'
            ])->setStatusCode(400);
        }

        // Si existen asientos pero no tienen reservas, los eliminamos para regenerar la distribución
        $existentes = $asientoModel->where('id_sala', $id_sala)->countAllResults();
        if ($existentes > 0) {
            $asientoModel->where('id_sala', $id_sala)->delete();
        }

        $capacidad = (int)$sala->capacidad;
        $cols = 10;
        $rowsCount = (int)ceil($capacidad / $cols);
        $inserted = 0;

        for ($r = 0; $r < $rowsCount; $r++) {
            $fila = chr(65 + $r); // A, B, C...
            for ($c = 1; $c <= $cols; $c++) {
                if ($inserted >= $capacidad) {
                    break;
                }

                $tipo = 'estandar';
                
                // Fila A (primera fila/parte inicial) contiene los preferenciales y accesibles
                if ($r === 0) {
                    if ($c <= 5) {
                        $tipo = 'preferencial';  // exactos 5 asientos preferenciales al inicio
                    } elseif ($c >= 9) {
                        $tipo = 'discapacitado';  // asientos accesibles
                    }
                }

                $asiento = new \App\Entities\Asiento();
                $asiento->id_sala = $id_sala;
                $asiento->fila = $fila;
                $asiento->numero = $c;
                $asiento->tipo = $tipo;

                $asientoModel->save($asiento);
                $inserted++;
            }
        }

        return $this->response->setJSON([
            'status' => 'success',
            'message' => "Se han generado automáticamente {$inserted} asientos para la sala {$sala->nombre}."
        ]);
    }

    /**
     * Valida dinámicamente mediante AJAX la disponibilidad de la sala y horario.
     * Verifica que se cumpla la anticipación de 1 día y que no existan traslapes de horario.
     */
    public function validarDisponibilidad()
    {
        $id_pelicula = $this->request->getGet('id_pelicula');
        $id_sala = $this->request->getGet('id_sala');
        $fecha = $this->request->getGet('fecha');
        $hora_inicio = $this->request->getGet('hora_inicio');
        $id_funcion = $this->request->getGet('id_funcion');

        // Si faltan parámetros obligatorios, retornamos disponible provisionalmente
        if (empty($id_pelicula) || empty($id_sala) || empty($fecha) || empty($hora_inicio)) {
            return $this->response->setJSON([
                'disponible' => true,
                'mensaje' => 'Complete todos los campos para verificar disponibilidad.',
                'motivo' => 'incompleto'
            ]);
        }

        // 1. Validar anticipación de 1 día
        $fecha_minima = date('Y-m-d', strtotime('+1 day'));
        if ($fecha < $fecha_minima) {
            return $this->response->setJSON([
                'disponible' => false,
                'mensaje' => 'La función se debe programar con al menos 1 día de anticipación (a partir de mañana).',
                'motivo' => 'anticipacion'
            ]);
        }

        // 2. Obtener duración de la película para calcular hora_fin
        $peliculaModel = new PeliculaModel();
        $pelicula = $peliculaModel->find($id_pelicula);
        if (!$pelicula) {
            return $this->response->setJSON([
                'disponible' => false,
                'mensaje' => 'Película no encontrada.',
                'motivo' => 'pelicula'
            ]);
        }

        $duracion = (int)$pelicula->duracion_min;
        $hora_fin = date('H:i:s', strtotime($hora_inicio) + ($duracion + 15) * 60);

        // 3. Validar traslapes de horarios en la misma sala y fecha
        $funcionModel = new FuncionModel();
        $builder = $funcionModel->select('funcion.*, pelicula.titulo as pelicula_titulo')
                                 ->join('pelicula', 'pelicula.id_pelicula = funcion.id_pelicula')
                                 ->where('id_sala', $id_sala)
                                 ->where('fecha', $fecha)
                                 ->where('hora_inicio <', $hora_fin)
                                 ->where('hora_fin >', $hora_inicio);

        if (!empty($id_funcion)) {
            $builder->where('funcion.id_funcion !=', $id_funcion);
        }

        $conflict = $builder->first();

        if ($conflict) {
            return $this->response->setJSON([
                'disponible' => false,
                'mensaje' => "Esta sala ya está siendo utilizada en ese horario. No puede ser seleccionada para una función porque se cruza con la película '{$conflict->pelicula_titulo}' (programada de {$conflict->hora_inicio} a {$conflict->hora_fin}).",
                'motivo' => 'traslape'
            ]);
        }

        return $this->response->setJSON([
            'disponible' => true,
            'mensaje' => 'La sala y horario están disponibles.',
            'motivo' => null
        ]);
    }
}
