<?php

class DominicanaAPI {
    private $baseUrl;
    private $ventasUrl;
    private $usuario;
    private $password;
    private $token;
    private $logger;
    
    public function __construct() {
        $this->baseUrl = getConfigValue('dominicana_api_url', 'https://apptest.dominicanadeseguros.com:9443/apidomiapp');
        $this->ventasUrl = getConfigValue('dominicana_api_ventas_url', 'https://apptest.dominicanadeseguros.com:9443/apiventasdcs/api');
        $this->usuario = getConfigValue('dominicana_usuario', 'DISASHOP');
        $this->password = getConfigValue('dominicana_password', 'abcd@1234');
        $this->logger = new Logger();
    }
    
    // Cache filesystem del token DCS — evita re-autenticar en cada request HTTP.
    // JWT dura 30 min (1800s); cacheamos por 25 con margen de seguridad.
    // El comportamiento externo del API no cambia: solo se ahorran ~250ms por llamada
    // a DCS cuando el token previo aún es válido.
    const DCS_TOKEN_CACHE_TTL = 1500; // 25 minutos

    private function getTokenCacheFile() {
        return __DIR__ . '/../storage/cache/dcs_token.json';
    }

    public function authenticate() {
        if ($this->token) {
            return $this->token;
        }

        // 1) Intentar usar token cacheado en filesystem
        $cacheFile = $this->getTokenCacheFile();
        if (file_exists($cacheFile)) {
            $cached = @json_decode(@file_get_contents($cacheFile), true);
            if (is_array($cached)
                && !empty($cached['token'])
                && !empty($cached['expires_at'])
                && $cached['expires_at'] > time()
                && (!isset($cached['usuario']) || $cached['usuario'] === $this->usuario)
            ) {
                $this->token = $cached['token'];
                return $this->token;
            }
        }

        // 2) No hay caché válido → autenticar contra DCS
        $data = [
            'cdUsuario' => $this->usuario,
            'contrasena' => $this->password
        ];

        try {
            $response = $this->makeRequest('POST', '/usuario/autenticar', $data);

            if (isset($response['token'])) {
                $this->token = $response['token'];
                $this->logger->info('Autenticación exitosa con Dominicana de Seguros');

                // 3) Persistir token en cache para próximos requests HTTP
                @mkdir(dirname($cacheFile), 0775, true);
                @file_put_contents($cacheFile, json_encode([
                    'token'      => $this->token,
                    'expires_at' => time() + self::DCS_TOKEN_CACHE_TTL,
                    'usuario'    => $this->usuario,
                ]), LOCK_EX);
                @chmod($cacheFile, 0664);

                return $this->token;
            } else {
                throw new Exception('Token no recibido en la respuesta de autenticación');
            }
        } catch (Exception $e) {
            $this->logger->error('Error en autenticación con Dominicana de Seguros', [
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Invalida el token cacheado (útil si DCS rechaza un token aparentemente vigente).
     */
    public function invalidateTokenCache() {
        $this->token = null;
        $cacheFile = $this->getTokenCacheFile();
        if (file_exists($cacheFile)) {
            @unlink($cacheFile);
        }
    }
    
    public function createPerson($personData) {
        $token = $this->authenticate();

        // Limpiar y validar el teléfono (solo números)
        $telefono = preg_replace('/[^0-9]/', '', $personData['telefono']);

        // Extraer código de área y número
        // Si tiene 10 dígitos: 829-641-0000 -> nuArea=829, nuTelefono=6410000
        if (strlen($telefono) === 10) {
            $nuArea = substr($telefono, 0, 3);      // Primeros 3 dígitos (809, 829, 849)
            $nuTelefono = substr($telefono, 3, 7);  // Últimos 7 dígitos
        } else {
            // Fallback si el formato es diferente
            $nuArea = '809';
            $nuTelefono = $telefono;
        }

        $data = [
            'cdUsuario' => $this->usuario,
            'cdTipoDocumento' => 'CED',
            'nuDocumento' => $personData['cedula'],
            'nmPrimerNombre' => strtoupper(trim($personData['primer_nombre'])),
            'nmSegundoNombre' => strtoupper(trim($personData['segundo_nombre'] ?? '')),
            'nmPrimerApellido' => strtoupper(trim($personData['primer_apellido'])),
            'nmSegundoApellido' => strtoupper(trim($personData['segundo_apellido'] ?? '')),
            'feNacimiento' => $personData['fecha_nacimiento'] ?? '01-01-1990',
            'inPep' => 0,
            'cdPaisResidencia' => 809,
            'cdTipoPersona' => 5,
            'cdEstadoCivil' => 'S',
            'cdSexo' => $personData['sexo'] ?? 'M',
            'cdActividadEconomica' => 2,
            'cdIngresoAnual' => 4,
            'personaDireccion' => [
                'nmCalle' => 'A Notificar',
                'cdTipoVia' => 1,
                'cdSector' => 3112,
                'cdTipoDireccion' => 1,
                'cdTipoVivienda' => 3,
                'nuCasa' => 'S/N',
                'nuPiso' => ''
            ],
            'personaEmail' => [
                [
                    'deEmail' => $personData['email'] ?? 'anotificar@disashop.com',
                    'cdTipoEmail' => 1
                ]
            ],
            'personaTelefono' => [
                [
                    'cdPais' => 809,
                    'nuArea' => $nuArea,
                    'nuTelefono' => $nuTelefono,
                    'cdTipoTelefono' => 3
                ]
            ]
        ];

        $this->logger->info('Creando persona en Dominicana', ['data' => $data]);

        return $this->makeRequest('POST', '/persona', $data, $token);
    }
    
    public function getPerson($cedula) {
        $token = $this->authenticate();
        return $this->makeRequest('GET', "/persona/{$cedula}", [], $token);
    }
    
    public function getPlanes() {
        $token = $this->authenticate();
        return $this->makeRequest('GET', '/datosgenerales/auto/planes', [], $token, $this->ventasUrl);
    }

    public function getMarcas() {
        $token = $this->authenticate();
        return $this->makeRequest('GET', '/datosgenerales/auto/marcas', [], $token, $this->ventasUrl);
    }

    public function getModelos($cdMarca) {
        $token = $this->authenticate();
        return $this->makeRequest('GET', "/datosgenerales/auto/modelos/{$cdMarca}", [], $token, $this->ventasUrl);
    }

    public function getAnos() {
        $token = $this->authenticate();
        return $this->makeRequest('GET', '/datosgenerales/auto/anio', [], $token, $this->ventasUrl);
    }

    public function getVersiones($deIndiceDato) {
        $token = $this->authenticate();
        return $this->makeRequest('GET', "/datosgenerales/auto/versiones/" . rawurlencode($deIndiceDato), [], $token, $this->ventasUrl);
    }

    /**
     * Obtiene detalle de un vehículo por su código de versión DCS.
     * Retorna cilindraje, tonelaje, tipoVehiculo (AUTOMÓVIL/JEEP/CAMIÓN) y claseVehiculo.
     * Endpoint: GET /datosgenerales/auto/detalledatovehiculo/{deIndiceDato}
     * donde deIndiceDato = "cdMarca|cdModelo|cdVersion" (URL-encoded)
     *
     * @param string $deIndiceDato Código de versión formato "cdMarca|cdModelo|cdVersion"
     * @return array {cilindraje, tonelaje, tipoVehiculo, claseVehiculo}
     */
    public function getDetalleVehiculo($deIndiceDato) {
        $token = $this->authenticate();
        // Endpoint en apidomiapp (baseUrl), NO en apiventasdcs — según documentación PDF
        return $this->makeRequest(
            'GET',
            '/datosgenerales/auto/detalledatovehiculo/' . rawurlencode($deIndiceDato),
            [],
            $token
        );
    }

    public function getUsos() {
        $token = $this->authenticate();
        return $this->makeRequest('GET', '/datosgenerales/auto/usos', [], $token, $this->ventasUrl);
    }

    public function getColores() {
        $token = $this->authenticate();
        return $this->makeRequest('GET', '/datosgenerales/auto/colores', [], $token, $this->ventasUrl);
    }

    public function procesarVenta($ventaData) {
        $token = $this->authenticate();
        return $this->makeRequest('POST', '/venta/procesarventa', $ventaData, $token, $this->ventasUrl);
    }
    
    public function emitirVenta($emisionData) {
        $token = $this->authenticate();
        return $this->makeRequest('POST', '/venta/emitir', $emisionData, $token, $this->ventasUrl);
    }

    public function getPrerenovacionesAsegurado($nuDocumento, $page = 0, $size = 20) {
        $token = $this->authenticate();
        $qs = http_build_query(['page' => $page, 'size' => $size]);
        return $this->makeRequest('GET', '/prerenovacion/asegurado/' . rawurlencode($nuDocumento) . '?' . $qs, [], $token, $this->ventasUrl);
    }

    public function renovarPolizas(array $renovaciones) {
        $token = $this->authenticate();
        return $this->makeRequest('POST', '/prerenovacion/renovar', $renovaciones, $token, $this->ventasUrl);
    }
    
    public function descargarPoliza($nuPoliza, $nuCertificado, $nuEndoso) {
        $token = $this->authenticate();

        // Extraer la parte central del número de póliza
        // Ejemplo: 1-600-45409-1 -> 45409
        $polizaParts = explode('-', $nuPoliza);
        $polizaNumero = isset($polizaParts[2]) ? $polizaParts[2] : $nuPoliza;

        $url = "/reporte/marbeteconimagen/{$polizaNumero}/{$nuCertificado}/{$nuEndoso}/{$this->usuario}";
        return $this->makeRequest('GET', $url, [], $token, null, true);
    }

    public function consultarEmisiones($cdUsuario, $filtros = []) {
        $token = $this->authenticate();

        $params = ['cdUsuario' => $cdUsuario];

        if (isset($filtros['nuPoliza'])) {
            $params['nuPoliza'] = $filtros['nuPoliza'];
        }
        if (isset($filtros['nmAsegurado'])) {
            $params['nmAsegurado'] = $filtros['nmAsegurado'];
        }
        if (isset($filtros['cdProducto'])) {
            $params['cdProducto'] = $filtros['cdProducto'];
        }
        if (isset($filtros['feEmision'])) {
            $params['feEmision'] = $filtros['feEmision'];
        }

        $params['page'] = $filtros['page'] ?? 1;
        $params['size'] = $filtros['size'] ?? 10;

        $queryString = http_build_query($params);
        $endpoint = '/venta/listado?' . $queryString;

        return $this->makeRequest('GET', $endpoint, [], $token, $this->ventasUrl);
    }
    
    private function makeRequest($method, $endpoint, $data = [], $token = null, $customBaseUrl = null, $returnRaw = false) {
        $url = ($customBaseUrl ?: $this->baseUrl) . $endpoint;

        // Retry automático ante bug intermitente de infra DCS PROD:
        // ocasionalmente devuelven HTTP 4xx/5xx + body con HTML del frontend SPA
        // (en lugar de JSON del API o de la página de error de Tomcat).
        // Solo reintentamos GETs (idempotentes) y solo cuando se detecta ese patrón.
        // POSTs nunca se reintentan (riesgo de duplicación de venta/persona).
        $maxRetries = ($method === 'GET') ? 2 : 0;
        $attempt    = 0;
        $response   = null;
        $httpCode   = 0;
        $error      = '';

        while (true) {
            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 20, // Reducido de 30 a 20 segundos
                CURLOPT_CONNECTTIMEOUT => 5, // Timeout de conexión de 5 segundos
                CURLOPT_CUSTOMREQUEST => $method,
                CURLOPT_HTTPHEADER => [
                    'Content-Type: application/json',
                    $token ? "Authorization: $token" : null
                ],
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false
            ]);

            if (in_array($method, ['POST', 'PUT']) && !empty($data)) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }

            $tStart   = microtime(true);
            $response = curl_exec($ch);
            $timeMs   = round((microtime(true) - $tStart) * 1000);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error    = curl_error($ch);
            curl_close($ch);

            // Detectar el patrón anómalo de infra DCS:
            // status >= 400 con body que es el HTML del frontend SPA (NO la página de Tomcat).
            // Si encaja, reintentamos con backoff exponencial corto.
            $esBugInfraDCS = (
                $httpCode >= 400
                && is_string($response)
                && stripos($response, '<!doctype html>') !== false
                && stripos($response, 'Dominicana de Seguros') !== false
                && stripos($response, 'Tomcat') === false
            );

            if ($esBugInfraDCS && $attempt < $maxRetries) {
                $waitMs = 500 * ($attempt + 1); // 500ms, 1000ms
                $this->logger->warning('DCS infra anomaly detected, retrying', [
                    'attempt'     => $attempt + 1,
                    'max_retries' => $maxRetries,
                    'http_code'   => $httpCode,
                    'endpoint'    => $endpoint,
                    'wait_ms'     => $waitMs,
                ]);
                usleep($waitMs * 1000);
                $attempt++;
                continue;
            }

            break;
        }

        // Log detallado de la petición (final, después de posibles reintentos)
        $this->logger->info("API Request to Dominicana", [
            'method' => $method,
            'url' => $url,
            'endpoint' => $endpoint,
            'http_code' => $httpCode,
            'time_ms' => $timeMs ?? null,
            'data_sent' => $data,
            'response' => $response,
            'has_token' => !empty($token),
            'retries_used' => $attempt,
        ]);

        if ($error) {
            $this->logger->error("cURL Error", ['error' => $error]);
            throw new Exception("Error cURL: $error");
        }

        if ($returnRaw) {
            return $response;
        }

        $decodedResponse = json_decode($response, true);

        if ($httpCode >= 400) {
            $errorMsg = "HTTP Error $httpCode";

            // Intentar incluir el mensaje de error más detallado posible
            if (isset($decodedResponse['errores']) && is_array($decodedResponse['errores'])) {
                $errorMsg .= ": " . implode(', ', $decodedResponse['errores']);
            } elseif (isset($decodedResponse['error'])) {
                $errorMsg .= ": " . $decodedResponse['error'];
            } elseif (isset($decodedResponse['message'])) {
                $errorMsg .= ": " . $decodedResponse['message'];
            } elseif (!empty($response)) {
                // Si no hay mensaje específico, incluir la respuesta completa
                $errorMsg .= " - Full Response: " . $response;
            } else {
                $errorMsg .= " - Empty response from server";
            }

            $this->logger->error("API Error Response", [
                'http_code' => $httpCode,
                'endpoint' => $endpoint,
                'method' => $method,
                'data_sent' => $data,
                'response' => $response,
                'decoded' => $decodedResponse
            ]);

            throw new Exception($errorMsg);
        }

        if (json_last_error() !== JSON_ERROR_NONE && !$returnRaw) {
            throw new Exception("Error decodificando JSON: " . json_last_error_msg());
        }

        return $decodedResponse;
    }
}