package jumapacelaya.gob.mx.appots.servicio;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.micronaut.data.connection.annotation.Connectable;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.transaction.Transactional;
import jumapacelaya.gob.mx.appots.dto.FotoBase64DTO;
import jumapacelaya.gob.mx.appots.dto.OTHijaDTO;
import jumapacelaya.gob.mx.appots.dto.dictamenDTO;
import jumapacelaya.gob.mx.appots.dto.empleadoOrigenDTO;
import jumapacelaya.gob.mx.appots.dto.fotoOTDTO;
import jumapacelaya.gob.mx.appots.dto.motivoPreguntaDTO;
import jumapacelaya.gob.mx.appots.dto.noCommMensajeDTO;
import jumapacelaya.gob.mx.appots.dto.origenDTO;
import jumapacelaya.gob.mx.appots.dto.otDTO;
import jumapacelaya.gob.mx.appots.dto.preguntaDTO;
import jumapacelaya.gob.mx.appots.dto.respuestaOTDTO;
import jumapacelaya.gob.mx.appots.dto.respuestaPreguntaDTO;
import jumapacelaya.gob.mx.appots.dto.transaccionDTO;

@Singleton
public class AppOtsServicio {

    @Inject
    DataSource dataSource;

    @Connectable
    public List<otDTO> obtenerOTsPorPredio(Long predioid) {
        List<otDTO> lista = new ArrayList<>();

        String query = "SELECT * FROM TABLE(pk_rest_ots.FN_GETINFOGRALOTSPREDIO(?))";

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

            ps.setLong(1, predioid);
            ResultSet rs = ps.executeQuery();

            while (rs.next()) {
                otDTO dto = new otDTO();

                dto.setOtid(rs.getLong("otid"));
                dto.setPredioid(rs.getLong("predioid"));
                dto.setClienteid(rs.getLong("clienteid"));
                dto.setFecha(rs.getString("fecha"));
                dto.setFechamax(rs.getString("fechamax"));
                dto.setFechaprecierre(rs.getString("fechaprecierre"));
                dto.setFechacierre(rs.getString("fechacierre"));
                dto.setFechaanulacion(rs.getString("fechaanulacion"));
                dto.setMotivoid(rs.getInt("motivoid"));
                dto.setMotivo(rs.getString("motivo"));
                dto.setOrigenid(rs.getInt("origenid"));
                dto.setUsuarioalta(rs.getString("usuarioalta"));
                dto.setUsuarioprecierre(rs.getString("usuarioprecierre"));
                dto.setUsuariocierre(rs.getString("usuariocierre"));
                dto.setUsuarioanulacion(rs.getString("usuarioanulacion"));
                dto.setDescripcion(rs.getString("descripcion"));
                dto.setObservacion(rs.getString("observacion"));
                dto.setHorainicio(rs.getString("horainicio"));
                dto.setHoraftermino(rs.getString("horaftermino"));
                dto.setOtpadre(rs.getLong("otpadre"));
                dto.setFechareporte(rs.getString("fechareporte"));
                dto.setNumreportes(rs.getInt("numreportes"));
                dto.setFechaasignacion(rs.getString("fechaasignacion"));
                dto.setCuadrillaasignada(rs.getString("cuadrillaasignada"));
                dto.setFechaprog(rs.getString("fechaprog"));
                dto.setNombsuperv(rs.getString("nombsuperv"));
                dto.setNombintegra(rs.getString("nombintegra"));
                dto.setMotivoanulacion(rs.getString("motivoanulacion"));
                dto.setMotsusid(rs.getInt("motsusid"));
                dto.setUsudictamen(rs.getString("usudictamen"));
                dto.setObsdictamen(rs.getString("obsdictamen"));
                dto.setDireccionot(rs.getString("direccionot"));
                dto.setCoordx(rs.getBigDecimal("coordx"));
                dto.setCoordy(rs.getBigDecimal("coordy"));

                lista.add(dto);
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al consultar OTs del predio: " + e.getMessage(), e);
        }

        return lista;
    }

    @Transactional
    public List<otDTO> obtenerInfoGralOT(Long otid) {
        List<otDTO> lista = new ArrayList<>();

        String query = "select ote.otid, ote.predioid, ote.clienteid, " +
                "ote.fecha, ote.fechamax, ote.fechaprecierre, ote.fechacierre, ote.fechaanulacion, " +
                "motivoid, otm.descripcion motivo, otm.origenid, " +
                "ote.usuarioalta, ote.usuarioprecierre, ote.usuariocierre, ote.usuarioanula usuarioanulacion, " +
                "ote.descripcion, ote.observacion, ote.horainicio, ote.horatermino horaftermino, ote.otipadre otpadre, " +
                "ote.fechareporte, ote.noreportes numreportes, ote.fechaasig fechaasignacion, ote.cuadrillaasig cuadrillaasignada, " +
                "ote.fechaprog, null nombsuperv, null nombintegra, ote.motanulacion motivoanulacion, " +
                "motsusid, ote.usudictamen, ote.obsdictamen, ote.direccion_ot direccionot, " +
                "ote.coordx, ote.coordy " +
                "from ordentrabajoenc ote " +
                "inner join ot_motivos otm using (motivoid) " +
                "left join ot_motivo_suspen ots using (motsusid) " +
                "where ( ? is null or otid = ? ) " +
                "and fechaprecierre is null " +
                "and fechaanulacion is null " +
                "order by otid";

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

            stmt.setObject(1, otid);
            stmt.setObject(2, otid);

            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    otDTO dto = new otDTO();

                    dto.setOtid(rs.getLong("otid"));
                    dto.setPredioid(rs.getLong("predioid"));
                    dto.setClienteid(rs.getLong("clienteid"));
                    dto.setFecha(rs.getString("fecha"));
                    dto.setFechamax(rs.getString("fechamax"));
                    dto.setFechaprecierre(rs.getString("fechaprecierre"));
                    dto.setFechacierre(rs.getString("fechacierre"));
                    dto.setFechaanulacion(rs.getString("fechaanulacion"));
                    dto.setMotivoid(rs.getInt("motivoid"));
                    dto.setMotivo(rs.getString("motivo"));
                    dto.setOrigenid(rs.getInt("origenid"));
                    dto.setUsuarioalta(rs.getString("usuarioalta"));
                    dto.setUsuarioprecierre(rs.getString("usuarioprecierre"));
                    dto.setUsuariocierre(rs.getString("usuariocierre"));
                    dto.setUsuarioanulacion(rs.getString("usuarioanulacion"));
                    dto.setDescripcion(rs.getString("descripcion"));
                    dto.setObservacion(rs.getString("observacion"));
                    dto.setHorainicio(rs.getString("horainicio"));
                    dto.setHoraftermino(rs.getString("horaftermino"));
                    dto.setOtpadre(rs.getLong("otpadre"));
                    dto.setFechareporte(rs.getString("fechareporte"));
                    dto.setNumreportes(rs.getInt("numreportes"));
                    dto.setFechaasignacion(rs.getString("fechaasignacion"));
                    dto.setCuadrillaasignada(rs.getString("cuadrillaasignada"));
                    dto.setFechaprog(rs.getString("fechaprog"));
                    dto.setNombsuperv(rs.getString("nombsuperv")); // puede venir null
                    dto.setNombintegra(rs.getString("nombintegra")); // puede venir null
                    dto.setMotivoanulacion(rs.getString("motivoanulacion"));
                    dto.setMotsusid(rs.getInt("motsusid"));
                    dto.setUsudictamen(rs.getString("usudictamen"));
                    dto.setObsdictamen(rs.getString("obsdictamen"));
                    dto.setDireccionot(rs.getString("direccionot"));
                    dto.setCoordx(rs.getBigDecimal("coordx"));
                    dto.setCoordy(rs.getBigDecimal("coordy"));

                    lista.add(dto);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener información general de OT: " + e.getMessage(), e);
        }

        return lista;
    }

    @Transactional
    public List<otDTO> obtenerOtsProgramadasPorCuadrillaYMotivo(String cuadrillaId, String motivoId) {
        List<otDTO> lista = new ArrayList<>();

        String baseQuery = """
            select ote.otid, predioid, ote.clienteid, ote.fecha, ote.fechamax, ote.fechaprecierre, ote.fechacierre, ote.fechaanulacion, motivoid, otm.descripcion motivo, otm.origenid, 
            ote.usuarioalta, ote.usuarioprecierre, ote.usuariocierre, ote.usuarioanula, ote.descripcion, ote.observacion, ote.horainicio, ote.horatermino, ote.otipadre, 
            ote.fechareporte, ote.noreportes, ote.fechaasig, ote.cuadrillaasig, ote.fechaprog, ote.motanulacion, motsusid, ots.nombre motsuspen, ote.usudictamen, ote.obsdictamen, 
            ote.direccion_ot, coalesce(ote.coordx, p.coordx) coordx, coalesce(ote.coordy, p.coordy) coordy, p.ctalocaliza
            from
            ordentrabajoenc ote
            left join predios p using (predioid)
            inner join ot_motivos otm using (motivoid)
            left join ot_motivo_suspen ots using (motsusid)
            where 
            trunc(fechaprog)=trunc(sysdate)
            and fechaprecierre is null
            and fechaanulacion is null
        """;

        StringBuilder query = new StringBuilder(baseQuery);

        if (!"all".equalsIgnoreCase(cuadrillaId)) {
            query.append(" AND cuadrillaasig LIKE ? ");
        }
        if (!"all".equalsIgnoreCase(motivoId)) {
            query.append(" AND motivoid LIKE ? ");
        }

        query.append(" ORDER BY otid");

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

            int index = 1;
            if (!"all".equalsIgnoreCase(cuadrillaId)) {
                stmt.setString(index++, cuadrillaId);
            }
            if (!"all".equalsIgnoreCase(motivoId)) {
                stmt.setString(index++, motivoId);
            }

            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    otDTO dto = new otDTO();

                    dto.setOtid(rs.getLong("otid"));
                    dto.setPredioid(rs.getLong("predioid"));
                    dto.setClienteid(rs.getLong("clienteid"));
                    dto.setFecha(rs.getString("fecha"));
                    dto.setFechamax(rs.getString("fechamax"));
                    dto.setFechaprecierre(rs.getString("fechaprecierre"));
                    dto.setFechacierre(rs.getString("fechacierre"));
                    dto.setFechaanulacion(rs.getString("fechaanulacion"));
                    dto.setMotivoid(rs.getInt("motivoid"));
                    dto.setMotivo(rs.getString("motivo"));
                    dto.setOrigenid(rs.getInt("origenid"));
                    dto.setUsuarioalta(rs.getString("usuarioalta"));
                    dto.setUsuarioprecierre(rs.getString("usuarioprecierre"));
                    dto.setUsuariocierre(rs.getString("usuariocierre"));
                    dto.setUsuarioanulacion(rs.getString("usuarioanula"));
                    dto.setDescripcion(rs.getString("descripcion"));
                    dto.setObservacion(rs.getString("observacion"));
                    dto.setHorainicio(rs.getString("horainicio"));
                    dto.setHoraftermino(rs.getString("horatermino")); // usa el nombre real del DTO
                    dto.setOtpadre(rs.getLong("otipadre"));
                    dto.setFechareporte(rs.getString("fechareporte"));
                    dto.setNumreportes(rs.getInt("noreportes"));
                    dto.setFechaasignacion(rs.getString("fechaasig"));
                    dto.setCuadrillaasignada(rs.getString("cuadrillaasig"));
                    dto.setFechaprog(rs.getString("fechaprog"));
                    dto.setMotivoanulacion(rs.getString("motanulacion"));
                    dto.setMotsusid(rs.getInt("motsusid"));
                    dto.setUsudictamen(rs.getString("usudictamen"));
                    dto.setObsdictamen(rs.getString("obsdictamen"));
                    dto.setDireccionot(rs.getString("direccion_ot"));
                    dto.setCoordx(rs.getBigDecimal("coordx"));
                    dto.setCoordy(rs.getBigDecimal("coordy"));
                    dto.setNombsuperv(rs.getString("nombsuperv"));
                    dto.setNombintegra(rs.getString("nombintegra"));

                    lista.add(dto);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener OTs programadas: " + e.getMessage(), e);
        }

        return lista;
    }

    public List<otDTO> obtenerOtsProgramadasPorOrigenYMotivo(String origenid, String motivoid) {
        List<otDTO> resultado = new ArrayList<>();

        StringBuilder query = new StringBuilder();
        query.append("SELECT ote.otid, predioid, ")
             .append("coalesce(decode(ote.clienteid,0,null,ote.clienteid), to_char(p.clienteid)) AS clienteid, ")
             .append("ote.fecha, ote.fechamax, ote.fechaprecierre, ote.fechacierre, ote.fechaanulacion, motivoid, ")
             .append("otm.descripcion AS motivo, otm.origenid, ote.usuarioalta, ote.usuarioprecierre, ote.usuariocierre, ")
             .append("ote.usuarioanula, ote.descripcion, ote.observacion, ote.horainicio, ote.horatermino, ote.otipadre, ")
             .append("ote.fechareporte, ote.noreportes, ote.fechaasig, ote.cuadrillaasig, ote.fechaprog, ote.motanulacion, ")
             .append("motsusid, ots.nombre AS motsuspen, ote.usudictamen, ote.obsdictamen, ote.direccion_ot, ")
             .append("coalesce(ote.coordx, p.coordx) AS coordx, coalesce(ote.coordy, p.coordy) AS coordy, p.ctalocaliza ")
             .append("FROM ordentrabajoenc ote ")
             .append("LEFT JOIN predios p USING (predioid) ")
             .append("INNER JOIN ot_motivos otm USING (motivoid) ")
             .append("LEFT JOIN ot_motivo_suspen ots USING (motsusid) ")
             .append("WHERE trunc(fechaprog) = trunc(sysdate) ")
             .append("AND fechaprecierre IS NULL ")
             .append("AND fechaanulacion IS NULL ");

        if (!"all".equalsIgnoreCase(origenid)) {
            query.append(" AND origenid LIKE ? ");
        }

        if (!"all".equalsIgnoreCase(motivoid)) {
            query.append(" AND motivoid LIKE ? ");
        }

        query.append(" ORDER BY otid");

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

            int index = 1;
            if (!"all".equalsIgnoreCase(origenid)) {
                ps.setString(index++, origenid);
            }
            if (!"all".equalsIgnoreCase(motivoid)) {
                ps.setString(index++, motivoid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    otDTO dto = new otDTO();
                    dto.setOtid(rs.getLong("otid"));
                    dto.setPredioid(rs.getLong("predioid"));
                    dto.setClienteid(rs.getLong("clienteid"));
                    dto.setFecha(rs.getString("fecha"));
                    dto.setFechamax(rs.getString("fechamax"));
                    dto.setFechaprecierre(rs.getString("fechaprecierre"));
                    dto.setFechacierre(rs.getString("fechacierre"));
                    dto.setFechaanulacion(rs.getString("fechaanulacion"));
                    dto.setMotivoid(rs.getInt("motivoid"));
                    dto.setMotivo(rs.getString("motivo"));
                    dto.setOrigenid(rs.getInt("origenid"));
                    dto.setUsuarioalta(rs.getString("usuarioalta"));
                    dto.setUsuarioprecierre(rs.getString("usuarioprecierre"));
                    dto.setUsuariocierre(rs.getString("usuariocierre"));
                    dto.setUsuarioanulacion(rs.getString("usuarioanula"));
                    dto.setDescripcion(rs.getString("descripcion"));
                    dto.setObservacion(rs.getString("observacion"));
                    dto.setHorainicio(rs.getString("horainicio"));
                    dto.setHoraftermino(rs.getString("horatermino"));
                    dto.setOtpadre(rs.getLong("otipadre"));
                    dto.setFechareporte(rs.getString("fechareporte"));
                    dto.setNumreportes(rs.getInt("noreportes"));
                    dto.setFechaasignacion(rs.getString("fechaasig"));
                    dto.setCuadrillaasignada(rs.getString("cuadrillaasig"));
                    dto.setFechaprog(rs.getString("fechaprog"));
                    dto.setMotivoanulacion(rs.getString("motanulacion"));
                    dto.setMotsusid(rs.getInt("motsusid"));
                    dto.setUsudictamen(rs.getString("usudictamen"));
                    dto.setObsdictamen(rs.getString("obsdictamen"));
                    dto.setDireccionot(rs.getString("direccion_ot"));
                    dto.setCoordx(rs.getBigDecimal("coordx"));
                    dto.setCoordy(rs.getBigDecimal("coordy"));

                    resultado.add(dto);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener OTs programadas por origen y motivo: " + e.getMessage(), e);
        }

        return resultado;
    }

    public List<otDTO> obtenerOtsProgramadasPorAreaOrigenMotivo(String areaid, String origenid, String motivoid) {
        List<otDTO> lista = new ArrayList<>();
        StringBuilder sql = new StringBuilder();

        sql.append("SELECT ote.otid, predioid, ")
        .append("COALESCE(DECODE(ote.clienteid, 0, NULL, ote.clienteid), TO_CHAR(p.clienteid)) AS clienteid, ")
        .append("ote.fecha, ote.fechamax, ote.fechaprecierre, ote.fechacierre, ote.fechaanulacion, ")
        .append("motivoid, otm.descripcion AS motivo, otm.origenid, ote.usuarioalta, ote.usuarioprecierre, ")
        .append("ote.usuariocierre, ote.usuarioanula AS usuarioanulacion, ote.descripcion, ote.observacion, ")
        .append("ote.horainicio, ote.horatermino AS horaftermino, ote.otipadre AS otpadre, ")
        .append("ote.fechareporte, ote.noreportes AS numreportes, ote.fechaasig AS fechaasignacion, ")
        .append("ote.cuadrillaasig AS cuadrillaasignada, ote.fechaprog, ote.motanulacion AS motivoanulacion, ")
        .append("motsusid, ots.nombre AS motsuspen, ote.usudictamen, ote.obsdictamen, ")
        .append("ote.direccion_ot AS direccionot, COALESCE(ote.coordx, p.coordx) AS coordx, ")
        .append("COALESCE(ote.coordy, p.coordy) AS coordy ")
        .append("FROM ordentrabajoenc ote ")
        .append("LEFT JOIN predios p USING (predioid) ")
        .append("INNER JOIN ot_motivos otm USING (motivoid) ")
        .append("LEFT JOIN ot_motivo_suspen ots USING (motsusid) ")
        .append("WHERE TRUNC(fechaprog) = TRUNC(SYSDATE) ")
        .append("AND fechaprecierre IS NULL ")
        .append("AND fechaanulacion IS NULL ");

        if (!"all".equalsIgnoreCase(areaid)) {
            sql.append("AND uniorgid IN (SELECT * FROM TABLE(splitvlt(?, '¬'))) ");
        }
        if (!"all".equalsIgnoreCase(origenid)) {
            sql.append("AND origenid IN (SELECT * FROM TABLE(splitvlt(?, '¬'))) ");
        }
        if (!"all".equalsIgnoreCase(motivoid)) {
            sql.append("AND motivoid IN (SELECT * FROM TABLE(splitvlt(?, '¬'))) ");
        }

        sql.append("ORDER BY otid");

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

            int paramIndex = 1;
            if (!"all".equalsIgnoreCase(areaid)) {
                ps.setString(paramIndex++, areaid);
            }
            if (!"all".equalsIgnoreCase(origenid)) {
                ps.setString(paramIndex++, origenid);
            }
            if (!"all".equalsIgnoreCase(motivoid)) {
                ps.setString(paramIndex++, motivoid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    otDTO dto = new otDTO();
                    dto.setOtid(rs.getLong("otid"));
                    dto.setPredioid(rs.getLong("predioid"));
                    dto.setClienteid(rs.getLong("clienteid"));
                    dto.setFecha(rs.getString("fecha"));
                    dto.setFechamax(rs.getString("fechamax"));
                    dto.setFechaprecierre(rs.getString("fechaprecierre"));
                    dto.setFechacierre(rs.getString("fechacierre"));
                    dto.setFechaanulacion(rs.getString("fechaanulacion"));
                    dto.setMotivoid(rs.getInt("motivoid"));
                    dto.setMotivo(rs.getString("motivo"));
                    dto.setOrigenid(rs.getInt("origenid"));
                    dto.setUsuarioalta(rs.getString("usuarioalta"));
                    dto.setUsuarioprecierre(rs.getString("usuarioprecierre"));
                    dto.setUsuariocierre(rs.getString("usuariocierre"));
                    dto.setUsuarioanulacion(rs.getString("usuarioanulacion"));
                    dto.setDescripcion(rs.getString("descripcion"));
                    dto.setObservacion(rs.getString("observacion"));
                    dto.setHorainicio(rs.getString("horainicio"));
                    dto.setHoraftermino(rs.getString("horaftermino"));
                    dto.setOtpadre(rs.getLong("otpadre"));
                    dto.setFechareporte(rs.getString("fechareporte"));
                    dto.setNumreportes(rs.getInt("numreportes"));
                    dto.setFechaasignacion(rs.getString("fechaasignacion"));
                    dto.setCuadrillaasignada(rs.getString("cuadrillaasignada"));
                    dto.setFechaprog(rs.getString("fechaprog"));
                    dto.setMotivoanulacion(rs.getString("motivoanulacion"));
                    dto.setMotsusid(rs.getInt("motsusid"));
                    dto.setUsudictamen(rs.getString("usudictamen"));
                    dto.setObsdictamen(rs.getString("obsdictamen"));
                    dto.setDireccionot(rs.getString("direccionot"));
                    dto.setCoordx(rs.getBigDecimal("coordx"));
                    dto.setCoordy(rs.getBigDecimal("coordy"));
                    lista.add(dto);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener OTs programadas por área-origen-motivo: " + e.getMessage(), e);
        }

        return lista;
    }

    public List<origenDTO> obtenerOrigenes(String origenid) {
        List<origenDTO> lista = new ArrayList<>();
        String sql = "SELECT * FROM operacion.ot_origenes ";
        
        if (!"all".equalsIgnoreCase(origenid)) {
            sql += "WHERE origenid LIKE ? ";
        }
        sql += "ORDER BY origenid";

        try (Connection conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(sql)) {
            
            if (!"all".equalsIgnoreCase(origenid)) {
                ps.setString(1, origenid);
            }

            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                origenDTO dto = new origenDTO();
                dto.setOrigenid(rs.getInt("origenid"));
                dto.setDescripcion(rs.getString("descripcion"));
                lista.add(dto);
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener los origenes: " + e.getMessage(), e);
        }

        return lista;
    }

    public List<Map<String, Object>> obtenerMotivos(String motivoid) {
        List<Map<String, Object>> motivos = new ArrayList<>();
        String query = "SELECT * FROM operacion.ot_motivos ";
        
        if (!"all".equalsIgnoreCase(motivoid)) {
            query += "WHERE motivoid LIKE ? ";
        }

        query += "ORDER BY motivoid";

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

            if (!"all".equalsIgnoreCase(motivoid)) {
                stmt.setString(1, motivoid);
            }

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

                while (rs.next()) {
                    Map<String, Object> row = new HashMap<>();
                    for (int i = 1; i <= columns; i++) {
                        row.put(meta.getColumnLabel(i), rs.getObject(i));
                    }
                    motivos.add(row);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener motivos: " + e.getMessage(), e);
        }

        return motivos;
    }

    public List<Map<String, Object>> obtenerMotivosPorAreaYMotivo(String areaid, String motivoid) {
        String baseQuery = "SELECT * FROM operacion.ot_motivos";
        String where = "";
        String where2 = "";

        if ("all".equalsIgnoreCase(areaid)) {
            if (!"all".equalsIgnoreCase(motivoid)) {
                where2 = " WHERE TRIM(motivoid) = '" + motivoid + "' ";
            }
        } else {
            where = " WHERE TRIM(uniorgid) = '" + areaid + "' ";
            if (!"all".equalsIgnoreCase(motivoid)) {
                where2 = " AND TRIM(motivoid) = '" + motivoid + "' ";
            }
        }

        String finalQuery = baseQuery + where + where2 + " ORDER BY uniorgid, motivoid";

        try (Connection connection = dataSource.getConnection();
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(finalQuery)) {

            List<Map<String, Object>> resultado = new ArrayList<>();
            ResultSetMetaData meta = rs.getMetaData();
            int columnCount = meta.getColumnCount();

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

            return resultado;

        } catch (SQLException e) {
            throw new RuntimeException("Error al consultar motivos por área y motivo: " + e.getMessage(), e);
        }
    }

    public List<Map<String, Object>> obtenerMotivoSusp(Integer motsusid) {
        List<Map<String, Object>> resultado = new ArrayList<>();
        String query = "SELECT * FROM operacion.ot_motivo_suspen ";

        if (!"all".equalsIgnoreCase(String.valueOf(motsusid))) {
            query += "WHERE motsusid LIKE ? ";
        }

        query += "ORDER BY motsusid";

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

            if (!"all".equalsIgnoreCase(String.valueOf(motsusid))) {
                ps.setInt(1, motsusid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                ResultSetMetaData meta = rs.getMetaData();
                int columnCount = meta.getColumnCount();

                while (rs.next()) {
                    Map<String, Object> row = new LinkedHashMap<>();
                    for (int i = 1; i <= columnCount; i++) {
                        row.put(meta.getColumnLabel(i).toLowerCase(), rs.getObject(i));
                    }
                    resultado.add(row);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener motivos de suspensión: " + e.getMessage(), e);
        }

        return resultado;
    }

    public List<preguntaDTO> obtenerPreguntas(String preguntaid, String dato) {
        List<preguntaDTO> preguntas = new ArrayList<>();

        String query = """
            SELECT p.*, 
                DECODE((SELECT u.preguntaid 
                        FROM operacion.pregununi u 
                        WHERE u.preguntaid = p.preguntaid AND ROWNUM = 1), NULL, 'No', 'Si') unimed
            FROM operacion.ot_preguntas p
            """ + 
            ("all".equalsIgnoreCase(preguntaid) ? "" : " WHERE preguntaid LIKE ? ") +
            " ORDER BY preguntaid";

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

            if (!"all".equalsIgnoreCase(preguntaid)) {
                ps.setString(1, preguntaid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    preguntaDTO dto = new preguntaDTO();
                    dto.setPreguntaid(rs.getInt("preguntaid"));
                    dto.setDescripcion(rs.getString("descripcion"));
                    dto.setSentenciaSQL(rs.getString("sentenciasql"));
                    dto.setUnimed(rs.getString("unimed"));

                    // Si hay sentencia SQL
                    if (dto.getSentenciaSQL() != null) {
                        List<Map<String, Object>> resultadoExtra = new ArrayList<>();
                        String innerQuery;

                        if ("6007".equals(dto.getPreguntaid().toString())) {
                            innerQuery = "SELECT MODELOMEDID, MODELOMEDID || ' ' || DESCRIPCION descripcion FROM MODELOSMEDIDOR WHERE TRIM(MARCAMEDID) = ?";
                            try (PreparedStatement psInner = conn.prepareStatement(innerQuery)) {
                                psInner.setString(1, dato);
                                try (ResultSet rsInner = psInner.executeQuery()) {
                                    while (rsInner.next()) {
                                        Map<String, Object> row = new HashMap<>();
                                        row.put("MODELOMEDID", rsInner.getString("MODELOMEDID"));
                                        row.put("descripcion", rsInner.getString("descripcion"));
                                        resultadoExtra.add(row);
                                    }
                                }
                            }
                        } else {
                            try (Statement stmtInner = conn.createStatement();
                                ResultSet rsInner = stmtInner.executeQuery(dto.getSentenciaSQL())) {
                                ResultSetMetaData metaData = rsInner.getMetaData();
                                int columnCount = metaData.getColumnCount();

                                while (rsInner.next()) {
                                    Map<String, Object> row = new HashMap<>();
                                    for (int i = 1; i <= columnCount; i++) {
                                        row.put(metaData.getColumnLabel(i), rsInner.getObject(i));
                                    }
                                    resultadoExtra.add(row);
                                }
                            }
                        }
                        dto.setSentenciaSQLData(resultadoExtra);
                    }

                    // Si UNIMED = "Si"
                    if ("Si".equalsIgnoreCase(dto.getUnimed())) {
                        List<Map<String, Object>> unimedList = new ArrayList<>();
                        String unimedQuery = """
                            SELECT * 
                            FROM operacion.unidad_material u 
                            RIGHT JOIN operacion.pregununi p USING (umid) 
                            WHERE preguntaid = ? 
                            ORDER BY umid
                        """;
                        try (PreparedStatement psUnimed = conn.prepareStatement(unimedQuery)) {
                            psUnimed.setInt(1, dto.getPreguntaid());
                            try (ResultSet rsUnimed = psUnimed.executeQuery()) {
                                ResultSetMetaData meta = rsUnimed.getMetaData();
                                int colCount = meta.getColumnCount();
                                while (rsUnimed.next()) {
                                    Map<String, Object> row = new HashMap<>();
                                    for (int i = 1; i <= colCount; i++) {
                                        row.put(meta.getColumnLabel(i), rsUnimed.getObject(i));
                                    }
                                    unimedList.add(row);
                                }
                            }
                        }
                        dto.setUnimedData(unimedList);
                    }

                    preguntas.add(dto);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener preguntas: " + e.getMessage(), e);
        }

        return preguntas;
    }

    @Transactional
    public List<Map<String, Object>> obtenerUnidadesMedida(String umid) {
        List<Map<String, Object>> resultados = new ArrayList<>();
        String query = "SELECT * FROM operacion.unidad_material";

        if (!"all".equalsIgnoreCase(umid)) {
            query += " WHERE umid LIKE ? ";
        }

        query += " ORDER BY umid";

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

            if (!"all".equalsIgnoreCase(umid)) {
                ps.setString(1, umid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                ResultSetMetaData meta = rs.getMetaData();
                int colCount = meta.getColumnCount();

                while (rs.next()) {
                    Map<String, Object> fila = new LinkedHashMap<>();
                    for (int i = 1; i <= colCount; i++) {
                        fila.put(meta.getColumnName(i).toLowerCase(), rs.getObject(i));
                    }
                    resultados.add(fila);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener unidades de medida: " + e.getMessage(), e);
        }

        return resultados;
    }

    public List<motivoPreguntaDTO> obtenerMotivoPreguntas(String motivoid, String preguntaid) {
        List<motivoPreguntaDTO> resultado = new ArrayList<>();

        StringBuilder query = new StringBuilder("SELECT * FROM operacion.ot_motpregdet");

        boolean tieneCondicion = false;

        if (!"all".equalsIgnoreCase(motivoid)) {
            query.append(" WHERE trim(motivoid) = '").append(motivoid).append("'");
            tieneCondicion = true;
        }

        if (!"all".equalsIgnoreCase(preguntaid)) {
            if (tieneCondicion) {
                query.append(" AND");
            } else {
                query.append(" WHERE");
            }
            query.append(" trim(preguntaid) = '").append(preguntaid).append("'");
        }

        query.append(" ORDER BY motivoid, ordenmot");

        try (Connection connection = dataSource.getConnection();
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(query.toString())) {

            while (rs.next()) {
                motivoPreguntaDTO dto = new motivoPreguntaDTO();
                dto.setMotivoid(rs.getInt("motivoid"));
                dto.setPreguntaid(rs.getInt("preguntaid"));
                dto.setOrdenmot(rs.getInt("ordenmot"));
                resultado.add(dto);
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al consultar motivo-pregunta: " + e.getMessage(), e);
        }

        return resultado;
    }

    public List<dictamenDTO> obtenerDictamenesPorMotivoYDictamen(String motivoId, String dictamenId) {
        List<dictamenDTO> resultados = new ArrayList<>();

        String baseQuery = """
            SELECT d.dictamenid, d.descripcion, d.activo, d.norealizacion, 
                m.motivoid, m.activo AS activomotivo
            FROM operacion.ot_dictamenes d
            LEFT JOIN operacion.ot_motivodictamen m USING (dictamenid)
            WHERE NOT REGEXP_LIKE(d.dictamenid, 'ANU|MIG')
            AND d.activo = 'S'
            AND m.activo = 'S'
            AND d.norealizacion = 'N'
        """;

        StringBuilder queryBuilder = new StringBuilder(baseQuery);

        if (!"all".equalsIgnoreCase(motivoId)) {
            queryBuilder.append(" AND TRIM(m.motivoid) = '").append(motivoId).append("' ");
        }

        if (!"all".equalsIgnoreCase(dictamenId)) {
            queryBuilder.append(" AND TRIM(d.dictamenid) = '").append(dictamenId).append("' ");
        }

        queryBuilder.append(" ORDER BY d.dictamenid");

        try (Connection conn = dataSource.getConnection();
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(queryBuilder.toString())) {

            while (rs.next()) {
                dictamenDTO dto = new dictamenDTO();
                dto.setDictamenid(rs.getString("dictamenid"));
                dto.setDescripcion(rs.getString("descripcion"));
                dto.setActivo(rs.getString("activo"));
                dto.setNorealizacion(rs.getString("norealizacion"));
                dto.setMotivoid(rs.getString("motivoid"));
                dto.setActivoMotivo(rs.getString("activomotivo"));
                resultados.add(dto);
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener dictámenes: " + e.getMessage(), e);
        }

        return resultados;
    }

    public List<Map<String, Object>> obtenerCortesFisicos(String motivoid) {
        List<Map<String, Object>> resultado = new ArrayList<>();
        StringBuilder query = new StringBuilder("SELECT * FROM operacion.ot_cortesfisicos ");

        if (!"all".equalsIgnoreCase(motivoid)) {
            query.append("WHERE motivoid LIKE ? ");
        }

        query.append("ORDER BY motivoid");

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

            if (!"all".equalsIgnoreCase(motivoid)) {
                ps.setString(1, motivoid);
            }

            try (ResultSet rs = ps.executeQuery()) {
                ResultSetMetaData meta = rs.getMetaData();
                int columnas = meta.getColumnCount();

                while (rs.next()) {
                    Map<String, Object> fila = new HashMap<>();
                    for (int i = 1; i <= columnas; i++) {
                        fila.put(meta.getColumnLabel(i).toLowerCase(), rs.getObject(i));
                    }
                    resultado.add(fila);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener cortes físicos: " + e.getMessage(), e);
        }

        return resultado;
    }

    public List<empleadoOrigenDTO> obtenerEmpleadosPorOrigenYUsuario(String origenid, String usuarioid) {
        List<empleadoOrigenDTO> empleados = new ArrayList<>();

        String sql = """
            SELECT DISTINCT empleadoid, usuarioid, nombrecompleto, titulo, fechaalt, fechabaj,
                lector, inspector, cambio, corte, reconexion, cobro, ordtrab, fotos,
                serv_catastro, sobrante, dictamenot, prorrogas, anularconv, ordenarpreg,
                jefeid, uniorgid, uniorg
            FROM (
                SELECT empleadoid, usuarioid, nombrecompleto, titulo, fechaalt, fechabaj,
                    lector, inspector, cambio, corte, reconexion, cobro, ordtrab, fotos,
                    serv_catastro, sobrante, dictamenot, prorrogas, anularconv, ordenarpreg,
                    jefeid, admon.fn_getuniorg(usuarioid) uniorgid,
                    admon.fn_getuniorgdesc(usuarioid) uniorg
                FROM empleados e
            ) d
            LEFT JOIN ot_motivos m USING (uniorgid)
            WHERE (:origenid = 'all' OR UPPER(TRIM(origenid)) = UPPER(:origenid))
            AND (:usuarioid = 'all' OR :usuarioid IS NULL OR UPPER(TRIM(usuarioid)) = UPPER(:usuarioid))
            ORDER BY empleadoid
        """;

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

            ps.setString(1, origenid);
            ps.setString(2, usuarioid);

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    empleadoOrigenDTO dto = new empleadoOrigenDTO();
                    dto.setEmpleadoid(rs.getString("empleadoid"));
                    dto.setUsuarioid(rs.getString("usuarioid"));
                    dto.setNombrecompleto(rs.getString("nombrecompleto"));
                    dto.setTitulo(rs.getString("titulo"));
                    dto.setFechaalt(rs.getString("fechaalt"));
                    dto.setFechabaj(rs.getString("fechabaj"));
                    dto.setLector(rs.getString("lector"));
                    dto.setInspector(rs.getString("inspector"));
                    dto.setCambio(rs.getString("cambio"));
                    dto.setCorte(rs.getString("corte"));
                    dto.setReconexion(rs.getString("reconexion"));
                    dto.setCobro(rs.getString("cobro"));
                    dto.setOrdtrab(rs.getString("ordtrab"));
                    dto.setFotos(rs.getString("fotos"));
                    dto.setServ_catastro(rs.getString("serv_catastro"));
                    dto.setSobrante(rs.getString("sobrante"));
                    dto.setDictamenot(rs.getString("dictamenot"));
                    dto.setProrrogas(rs.getString("prorrogas"));
                    dto.setAnularconv(rs.getString("anularconv"));
                    dto.setOrdenarpreg(rs.getString("ordenarpreg"));
                    dto.setJefeid(rs.getString("jefeid"));
                    dto.setUniorgid(rs.getString("uniorgid"));
                    dto.setUniorg(rs.getString("uniorg"));

                    empleados.add(dto);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al obtener empleados por origen y usuario: " + e.getMessage(), e);
        }

        return empleados;
    }

    public Map<String, Object> actualizarSituacionOT(Long otid, String situacion) {
        String query;

        switch (situacion) {
            case "T":
                query = "UPDATE rest.otsituacion SET situacion = 'T' WHERE otid = ?";
                break;
            case "R":
                query = "INSERT INTO rest.otsituacion VALUES (?, 'R', SYSDATE, NULL, NULL)";
                break;
            case "S":
                query = "UPDATE rest.otsituacion SET situacion = 'S', fechorasitio = SYSDATE WHERE otid = ?";
                break;
            case "C":
                query = "UPDATE rest.otsituacion SET situacion = 'C', fechoracomienza = SYSDATE WHERE otid = ?";
                break;
            case "D":
                query = "DELETE FROM rest.otsituacion WHERE otid = ?";
                break;
            default:
                throw new IllegalArgumentException("Situación inválida: " + situacion);
        }

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

            stmt.setLong(1, otid);
            int rowsAffected = stmt.executeUpdate();

            Map<String, Object> result = new LinkedHashMap<>();
            if (rowsAffected > 0) {
                result.put("status", "200");
                result.put("message", "Estado de OT Actualizado [" + situacion + "]");
                result.put("data", Map.of("OTID", otid));
            } else {
                result.put("status", "500");
                result.put("message", "No se pudo actualizar Situación de OT [" + situacion + "]");
                result.put("data", Map.of("OTID", otid));
            }
            return result;

        } catch (SQLException e) {
            throw new RuntimeException("Error al actualizar situación de OT: " + e.getMessage(), e);
        }
    }

    public Map<String, Object> registrarAvisoNoComm(Long otid, List<noCommMensajeDTO> mensajes) {
        Map<String, Object> respuesta = new HashMap<>();
        String mensaje = null;

        for (noCommMensajeDTO msg : mensajes) {
            if (msg.getOTID().equals(otid)) {
                mensaje = msg.getMENSAJE();
                break;
            }
        }

        if (mensaje == null) {
            respuesta.put("status", "404");
            respuesta.put("message", "No se encontró mensaje para el OTID " + otid);
            return respuesta;
        }

        String query = "INSERT INTO rest.otsnocomm (otid, fechora, mensaje) VALUES (?, SYSDATE, ?)";
        try (Connection con = dataSource.getConnection();
            PreparedStatement ps = con.prepareStatement(query)) {
            ps.setLong(1, otid);
            ps.setString(2, mensaje);
            int result = ps.executeUpdate();
            if (result > 0) {
                respuesta.put("status", "200");
                respuesta.put("message", "Aviso No Comm Recibido [N]..");
                respuesta.put("data", Map.of("OTID", otid));
            } else {
                respuesta.put("status", "500");
                respuesta.put("message", "No se pudo insertar Aviso No Comm [N]..");
                respuesta.put("data", Map.of("OTID", otid));
            }
        } catch (SQLException e) {
            respuesta.put("status", "500");
            respuesta.put("message", "Error al insertar aviso No Comm: " + e.getMessage());
            respuesta.put("data", Map.of("OTID", otid));
        }
        return respuesta;
    }

    public void guardarRespuestas(List<respuestaOTDTO> respuestas) {
        for (respuestaOTDTO ot : respuestas) {
            Long otid = ot.getOTid();

            try (Connection conn = dataSource.getConnection()) {
                // 1. Obtener datos del predio, carpeta fotos y motivoid
                String cpid = null;
                String carpetaFotos = null;
                String motivoid = null;

                String query = """
                    SELECT COALESCE(TO_CHAR(predioid), 'C' || clienteid) AS cpid,
                           PATH_FOTOS_OTS AS carpetafotos,
                           motivoid
                    FROM ordentrabajoenc, PARAM_LECT_FACT
                    WHERE otid = ?
                """;

                try (PreparedStatement ps = conn.prepareStatement(query)) {
                    ps.setLong(1, otid);
                    ResultSet rs = ps.executeQuery();
                    if (rs.next()) {
                        cpid = rs.getString("cpid");
                        carpetaFotos = rs.getString("carpetafotos");
                        motivoid = rs.getString("motivoid");
                    }
                }

                if (cpid == null || carpetaFotos == null) {
                    throw new RuntimeException("Datos de OT no encontrados");
                }

                // 2. Crear carpetas
                String localPath = "AppOTs/" + cpid + "/" + otid;
                String nasPath = carpetaFotos.replace("\\", "/") + cpid + "/" + otid;

                new File(localPath + "/fotos").mkdirs();
                new File(nasPath).mkdirs();

                // 3. Guardar archivo JSON
                String jsonPath = localPath + "/Data_" + otid + ".json";
                try (OutputStream out = new FileOutputStream(jsonPath)) {
                    out.write(new ObjectMapper().writeValueAsBytes(List.of(ot)));
                }

                // 4. Guardar fotos
                int nfoto = 1;
                for (FotoBase64DTO foto : ot.getFoto()) {
                    byte[] imageBytes = Base64.getDecoder().decode(foto.getBase64());

                    try (OutputStream out1 = new FileOutputStream(localPath + "/fotos/" + otid + "_foto" + nfoto + ".jpg");
                         OutputStream out2 = new FileOutputStream(nasPath + "/" + otid + "_foto" + nfoto + ".jpg")) {

                        out1.write(imageBytes);
                        out2.write(imageBytes);
                    }
                    nfoto++;
                }

                // 5. Preparar registros de respuestas (tipo PL/SQL Table)
                StringBuilder respPregBuilder = new StringBuilder("REST.TBOTDET(");
                boolean first = true;
                for (respuestaPreguntaDTO resp : ot.getResppreg()) {
                    if (!first) respPregBuilder.append(", ");
                    respPregBuilder.append("REST.TOTDET(")
                        .append(otid).append(", ")
                        .append("'").append(resp.getPreguntaId()).append("', ")
                        .append("'").append(resp.getRespuesta()).append("', ")
                        .append("'").append(resp.getUmId()).append("')");
                    first = false;
                }
                respPregBuilder.append(")");

                String respPregSQL = ot.getResppreg().isEmpty() ? "null" : respPregBuilder.toString();

                // 6. Preparar registros de otras hijas
                StringBuilder othijasBuilder = new StringBuilder("REST.TBOTHIJAS(");
                first = true;
                for (OTHijaDTO hija : ot.getOThijas()) {
                    if (List.of("300", "304", "311", "317", "321").contains(hija.getMotivoId()))
                        continue;

                    if (!first) othijasBuilder.append(", ");
                    othijasBuilder.append("REST.TOTHIJA('")
                        .append(hija.getMotivoId()).append("', '")
                        .append(hija.getDescripcion()).append("', '")
                        .append(hija.getMotSusId()).append("')");
                    first = false;
                }
                othijasBuilder.append(")");
                String othijasSQL = ot.getOThijas().isEmpty() ? "null" : othijasBuilder.toString();

                // 7. Insertar en DB
                String insertSQL = """
                    INSERT INTO rest.otresp (
                        otid, fechaprecierre, usuarioprecierre,
                        horainicio, horatermino, observacion,
                        nombsuperv, nombintegra, dictamenid,
                        obsdictamen, usudictamen, transaccid,
                        transaccdt, respreg, othijas
                    ) VALUES (
                        ?, TO_DATE(? || ' ' || ?, 'dd/mm/yyyy hh24:mi:ss'),
                        ?, TO_DATE(? || ' ' || ?, 'dd/mm/yyyy hh24:mi:ss'),
                        ?, ?, ?, ?, ?, utl_raw.cast_to_raw(?), SYSTIMESTAMP,
                        %s, %s
                    )
                """.formatted(respPregSQL, othijasSQL);

                try (PreparedStatement ps = conn.prepareStatement(insertSQL)) {
                    ps.setLong(1, otid);
                    ps.setString(2, ot.getFechaPreCierre());
                    ps.setString(3, ot.getHoraInicio());
                    ps.setString(4, ot.getUsuarioPreCierre());
                    ps.setString(5, ot.getFechaPreCierre());
                    ps.setString(6, ot.getHoraTermino());
                    ps.setString(7, ot.getObservacion());
                    ps.setString(8, ot.getNombSuperv());
                    ps.setString(9, ot.getNombIntegra());
                    ps.setString(10, ot.getDictamenId());
                    ps.setString(11, ot.getObsDictamen());
                    ps.setString(12, ot.getTransaccId());

                    ps.executeUpdate();
                }

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

    public void guardarFotosOT(Integer otid, List<fotoOTDTO> fotosOTList) {
        for (fotoOTDTO ot : fotosOTList) {
            if (!otid.equals(ot.getOTID())) {
                continue;
            }

            String rutaBase = "AppOTs/" + otid;
            File carpeta = new File(rutaBase + "/fotos/");
            if (!carpeta.exists()) {
                carpeta.mkdirs();
            }

            int contador = 1;
            for (fotoOTDTO.FotoDTO foto : ot.getFOTOS()) {
                String nombreArchivo = rutaBase + "/fotos/" + otid + "_foto" + contador + ".jpg";
                guardarImagenDesdeBase64(foto.getBase64(), nombreArchivo);
                contador++;
            }
        }
    }

    private void guardarImagenDesdeBase64(String base64, String rutaArchivo) {
        try (FileOutputStream fos = new FileOutputStream(rutaArchivo)) {
            byte[] imagenBytes = Base64.getDecoder().decode(base64);
            fos.write(imagenBytes);
        } catch (IOException e) {
            throw new RuntimeException("Error al guardar imagen: " + rutaArchivo, e);
        }
    }

    public List<transaccionDTO> obtenerPorTransaccionId(String transaccid) {
        List<transaccionDTO> resultados = new ArrayList<>();
        String query = "SELECT otid, transaccdt " +
                       "FROM rest.otresp " +
                       "WHERE regexp_like(utl_raw.cast_to_varchar2(transaccid), ?)";

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

            ps.setString(1, transaccid);

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    transaccionDTO dto = new transaccionDTO();
                    dto.setOtid(rs.getLong("otid"));
                    Timestamp ts = rs.getTimestamp("transaccdt");
                    dto.setTransaccdt(ts != null ? ts.toLocalDateTime() : null);
                    resultados.add(dto);
                }
            }

        } catch (SQLException e) {
            throw new RuntimeException("Error al consultar transacción: " + e.getMessage(), e);
        }

        return resultados;
    }
}
