package jumapacelaya.gob.mx.vv.servicio;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.sql.DataSource;

import jakarta.inject.Singleton;
import io.micronaut.transaction.annotation.ReadOnly;
import io.micronaut.transaction.annotation.Transactional;
import jumapacelaya.gob.mx.vv.dto.citaDTO;
import jumapacelaya.gob.mx.vv.dto.horarioDisponibleDTO;
import jumapacelaya.gob.mx.vv.dto.tramiteDTO;
import jumapacelaya.gob.mx.vv.repositorio.VvRepositorio;

@Singleton
public class VvServicio {

    private final DataSource dataSource;

    public VvServicio(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Transactional
    public List<Map<String, Object>> obtenerDiasDisponibles(Integer punto, Integer dias) {
        List<Map<String, Object>> resultado = new ArrayList<>();

        String query =
                "with dias as ( " +
                "    select sysdate as hoy, level as diasfaltantes, trunc(sysdate + level) as fechacita " +
                "    from dual connect by level <= ? " +
                ") " +
                "select count(c.citaid) as citas, " +
                "       to_char(d.fechacita, 'dd/mm/yyyy', 'NLS_DATE_LANGUAGE = SPANISH') as fechacita, " +
                "       to_char(d.fechacita, 'fmDY', 'NLS_DATE_LANGUAGE = SPANISH') || ' ' || " +
                "       to_char(d.fechacita, 'dd/mm/yyyy', 'NLS_DATE_LANGUAGE = SPANISH') as sfechacita " +
                "from dias d " +
                "left join appmovcom.citas c on (trunc(c.fechora) = d.fechacita and c.estado in ('S', 'P') and c.puntoid = ?) " +
                "where d.fechacita not in (select fecha from diasfestivos) " +
                "  and to_char(d.fechacita, 'fmDY', 'NLS_DATE_LANGUAGE = SPANISH') not in ('SÁB', 'DOM') " +
                "group by d.fechacita " +
                "order by d.fechacita";

        try (Connection connection = dataSource.getConnection();
             PreparedStatement stmt = connection.prepareStatement(query)) {

            stmt.setInt(1, dias);
            stmt.setInt(2, punto);

            try (ResultSet rs = stmt.executeQuery()) {
                ResultSetMetaData meta = rs.getMetaData();
                int cols = meta.getColumnCount();

                while (rs.next()) {
                    Map<String, Object> fila = new HashMap<>();
                    for (int i = 1; i <= cols; i++) {
                        fila.put(meta.getColumnLabel(i).toLowerCase(), rs.getObject(i));
                    }
                    resultado.add(fila);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener días disponibles", e);
        }

        return resultado;
    }

    @ReadOnly
    public List<horarioDisponibleDTO> obtenerHorariosDisponibles(Integer punto, String dia) {
        List<horarioDisponibleDTO> lista = new ArrayList<>();

        String horarios = "09:30,10:00,10:30,11:00,11:30,12:00,12:30,13:00,13:30,14:00,14:30";

        String query = """
            with horarios as (
                select to_char(fechora, 'hh24:mi', 'NLS_DATE_LANGUAGE = SPANISH') hora
                from appmovcom.citas
                where trunc(fechora) = trunc(to_date(?, 'ddmmyyyy'))
                and puntoid = ?
            )
            select column_value horariosdisponibles
            from table(cobranza.splitvlt(?, ','))
            where column_value not in (select * from horarios)
            order by column_value
        """;

        try (Connection conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(query)) {

            ps.setString(1, dia);
            ps.setInt(2, punto);
            ps.setString(3, horarios);

            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                horarioDisponibleDTO dto = new horarioDisponibleDTO();
                dto.setHorariosdisponibles(rs.getString("horariosdisponibles"));
                lista.add(dto);
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener horarios disponibles", e);
        }

        return lista;
    }

    @Transactional
    public String guardarCita(int puntoId, int tipoTramite, String dia, String horario, String telefono, String email) {
        String fechaCita = dia + " " + horario;
        String citaIdExistente = obtenerCitaExistente(puntoId, dia, horario);

        if (citaIdExistente != null) {
            return "Ya existe una cita en ese horario. Revisa disponibilidad nuevamente.";
        }

        String folioGenerado = generarFolio();

        String insertQuery = "INSERT INTO appmovcom.citas (citaid, puntoid, fechora, tipotramid, email, telefono) " +
                             "VALUES (?, ?, TO_DATE(?, 'dd/mm/yyyy hh24:mi'), ?, ?, ?)";

        try (Connection connection = dataSource.getConnection();
             PreparedStatement ps = connection.prepareStatement(insertQuery)) {

            ps.setString(1, folioGenerado);
            ps.setInt(2, puntoId);
            ps.setString(3, fechaCita);
            ps.setInt(4, tipoTramite);
            ps.setString(5, email);
            ps.setString(6, telefono);

            ps.executeUpdate();

        } catch (Exception e) {
            throw new RuntimeException("Error al guardar cita: " + e.getMessage(), e);
        }

        return "Se generó la cita con folio " + folioGenerado + " para la oficina " + nombrePunto(puntoId) +
               " el día " + dia + " a las " + horario + " hrs.";
    }

    private String obtenerCitaExistente(int puntoId, String dia, String horario) {
        String query = "SELECT citaid FROM appmovcom.citas WHERE puntoid = ? AND trunc(fechora) = TO_DATE(?, 'dd/mm/yyyy') AND TO_CHAR(fechora, 'hh24:mi') = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(query)) {

            ps.setInt(1, puntoId);
            ps.setString(2, dia);
            ps.setString(3, horario);

            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return rs.getString("citaid");
                }
            }

        } catch (Exception e) {
            throw new RuntimeException("Error al verificar cita existente: " + e.getMessage(), e);
        }
        return null;
    }

    private String generarFolio() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        while (sb.length() < 7) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    private String nombrePunto(int puntoId) {
        return switch (puntoId) {
            case 1 -> "División del Norte";
            case 2 -> "Parque Morelos";
            case 3 -> "Cañitos";
            case 5 -> "Parque Celaya";
            default -> "Desconocida";
        };
    }

    @Transactional
    public String guardarTramite(tramiteDTO tramite) {
        String tramiteExistente = obtenerTramite(tramite.getUsuarioId(), tramite.getTipoTramite());

        if (tramiteExistente != null) {
            return "Ya hay un trámite registrado para este usuario y tipo. Revisa tu email para dar seguimiento.";
        }

        String folio = generarFolio();

        String insertQuery = """
            INSERT INTO appmovcom.tramites (
                tramiteid, tipotramid, usuarioid, email, telefono, estatus, predioid
            ) VALUES (
                ?, ?, ?, (SELECT email FROM appmovcom.usuarios WHERE usuarioid = ?), ?, 'NUEVO', ?
            )
        """;

        try (Connection conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(insertQuery)) {

            ps.setString(1, folio);
            ps.setInt(2, tramite.getTipoTramite());
            ps.setInt(3, tramite.getUsuarioId());
            ps.setInt(4, tramite.getUsuarioId());
            ps.setString(5, tramite.getTelefono());
            ps.setLong(6, tramite.getPredioId());

            ps.executeUpdate();

        } catch (SQLException e) {
            throw new RuntimeException("Error al guardar trámite: " + e.getMessage(), e);
        }

        // Envío de correo (si lo deseas activar más adelante)
        // enviaCorreo(folio, "G");

        return "Se generó el Trámite con folio " + folio + ". Se te enviará un correo con la información necesaria.";
    }

    private String obtenerTramite(int usuarioId, int tipoTramite) {
        String query = "SELECT tramiteid FROM appmovcom.tramites WHERE usuarioid = ? AND tipotramid = ?";
        try (Connection conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(query)) {

            ps.setInt(1, usuarioId);
            ps.setInt(2, tipoTramite);

            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return rs.getString("tramiteid");
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al verificar trámite existente: " + e.getMessage(), e);
        }
        return null;
    }

}
