<?php

namespace Services;

class B3CprService
{
    public function getAccessToken(): string
    {
        $b3Url = $_ENV['B3_URL'] ?? null;
        $grantType = $_ENV['GRANT_TYPE'] ?? 'client_credentials';
        $certPass = $_ENV['CERT_PASS'] ?? null;

        if (!$b3Url || !$grantType) {
            throw new \RuntimeException('Missing required env vars: B3_URL, GRANT_TYPE');
        }

        $certPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . '319245319-320109623_PROD.cer';
        $keyPath  = dirname(__DIR__) . DIRECTORY_SEPARATOR . '319245319-320109623_PROD.key';
        if (!file_exists($certPath) || !file_exists($keyPath)) {
            throw new \RuntimeException('Client certificate or key not found');
        }

        $postFields = 'grant_type=' . ($grantType);

        $ch = curl_init($b3Url);
        curl_setopt($ch, CURLOPT_POST, true);
        $headers = [
            'Content-Type: application/x-www-form-urlencoded',
            'Accept: application/json',
        ];
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($ch, CURLOPT_SSLCERT, $certPath);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $keyPath);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        if ($certPass) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $certPass);
        }
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

        $responseBody = curl_exec($ch);
        $curlErrNo = curl_errno($ch);
        $curlErr = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlErrNo !== 0) {
            throw new \RuntimeException('cURL error: ' . $curlErr);
        }

        $decoded = json_decode((string)$responseBody, true);
        if (!is_array($decoded)) {
            throw new \RuntimeException('Invalid token response (non-JSON), HTTP ' . $httpCode);
        }
        $token = $decoded['access_token'] ?? null;
        if (!is_string($token) || $token === '') {
            throw new \RuntimeException('access_token not present in token response');
        }
        return $token;
    }
    public function mapToB3(array $cpr): array
    {
        $collateralTypeCodes = $this->splitSemicolonList($cpr['cpr_collateral_type_code'] ?? null);
        $collateralTypeNames = $this->splitSemicolonList($cpr['cpr_collateral_type_name'] ?? null);
        $constitutionProcessIndicators = $this->splitSemicolonList($cpr['cpr_constitution_process_indicator'] ?? null);
        $otcBondsmanAccountCodes = $this->splitSemicolonList($cpr['cpr_otc_bondsman_account_code'] ?? null);

        $collateralsCount = max(
            count($collateralTypeCodes),
            count($collateralTypeNames),
            count($constitutionProcessIndicators),
            count($otcBondsmanAccountCodes)
        );
        if ($collateralsCount <= 0) {
            $collateralsCount = 1;
        }

        $collaterals = [];
        for ($i = 0; $i < $collateralsCount; $i++) {
            $collaterals[] = [
                'collateralTypeCode' => $collateralTypeCodes[$i] ?? null,
                'collateralTypeName' => $collateralTypeNames[$i] ?? null,
                'constitutionProcessIndicator' => $this->mapIndicator($constitutionProcessIndicators[$i] ?? null),
                'otcBondsmanAccountCode' => $otcBondsmanAccountCodes[$i] ?? null,
            ];
        }

        $issuerNames = $this->splitSemicolonList($cpr['cpr_issuer_name'] ?? null);
        $issuerDocumentNumbers = $this->splitSemicolonList($cpr['cpr_issuers_document_number'] ?? null);
        $issuerPersonTypes = $this->splitSemicolonList($cpr['cpr_issuers_person_type_acronym'] ?? null);
        $issuerLegalNatures = $this->splitSemicolonList($cpr['cpr_issuer_legal_nature_code'] ?? null);
        $issuerStates = $this->splitSemicolonList($cpr['cpr_issuers_state_acronym'] ?? null);
        $issuerCities = $this->splitSemicolonList($cpr['cpr_issuers_city_name'] ?? null);

        $issuersCount = max(
            count($issuerNames),
            count($issuerDocumentNumbers),
            count($issuerPersonTypes),
            count($issuerLegalNatures),
            count($issuerStates),
            count($issuerCities)
        );
        if ($issuersCount <= 0) {
            $issuersCount = 1;
        }

        $issuers = [];
        for ($i = 0; $i < $issuersCount; $i++) {
            $issuers[] = [
                'cprIssuerName' => $issuerNames[$i] ?? null,
                'documentNumber' => $issuerDocumentNumbers[$i] ?? null,
                'personTypeAcronym' => $issuerPersonTypes[$i] ?? null,
                'issuerLegalNatureCode' => $issuerLegalNatures[$i] ?? null,
                'stateAcronym' => $issuerStates[$i] ?? null,
                'cityName' => $issuerCities[$i] ?? null,
            ];
        }

        $productionPlaceNames = $this->splitSemicolonList($cpr['cpr_production_place_name'] ?? null);
        $propertyRegistrationNumbers = $this->splitSemicolonList($cpr['cpr_property_registration_number'] ?? null);
        $notaryNames = $this->splitSemicolonList($cpr['cpr_notary_name'] ?? null);
        $totalProductionAreas = $this->splitSemicolonList($cpr['cpr_total_production_area_in_hectares_number'] ?? null);
        $totalAreas = $this->splitSemicolonList($cpr['cpr_total_area_in_hectares_number'] ?? null);
        $carCodes = $this->splitSemicolonList($cpr['cpr_car_code'] ?? null);
        $latitudeCodes = $this->splitSemicolonList($cpr['cpr_latitude_code'] ?? null);
        $longitudeCodes = $this->splitSemicolonList($cpr['cpr_longitude_code'] ?? null);
        $zipCodes = $this->splitSemicolonList($cpr['cpr_zip_code'] ?? null);

        $productionPlacesCount = max(
            count($productionPlaceNames),
            count($propertyRegistrationNumbers),
            count($notaryNames),
            count($totalProductionAreas),
            count($totalAreas),
            count($carCodes),
            count($latitudeCodes),
            count($longitudeCodes),
            count($zipCodes)
        );
        if ($productionPlacesCount <= 0) {
            $productionPlacesCount = 1;
        }

        $productionPlaces = [];
        for ($i = 0; $i < $productionPlacesCount; $i++) {
            $productionPlaces[] = [
                'productionPlaceName' => $productionPlaceNames[$i] ?? null,
                'propertyRegistrationNumber' => $propertyRegistrationNumbers[$i] ?? null,
                'notaryName' => $notaryNames[$i] ?? null,
                'totalProductionAreaInHectaresNumber' => $totalProductionAreas[$i] ?? null,
                'totalAreaInHectaresNumber' => $totalAreas[$i] ?? null,
                'carCode' => $carCodes[$i] ?? null,
                'latitudeCode' => $latitudeCodes[$i] ?? null,
                'longitudeCode' => $longitudeCodes[$i] ?? null,
                'zipCode' => $zipCodes[$i] ?? null,
            ];
        }

        $instrument = [
            'cprTypeCode' => $cpr['cpr_type_code'] ?? null,
            'otcRegisterAccountCode' => $cpr['cpr_otc_register_account_code'] ?? null,
            'otcCustodianAccountCode' => $cpr['cpr_otc_custodian_account_code'] ?? null,
            'electronicEmissionIndicator' => $this->mapIndicator($cpr['cpr_electronic_emission_indicator'] ?? null),
            'issueDate' => $this->convertDate($cpr['cpr_issue_date'] ?? null),
            'maturityDate' => $this->convertDate($cpr['cpr_maturity_date'] ?? null),
            'issueQuantity' => $cpr['cpr_issue_quantity'] ?? null,
            'issueValue' => $cpr['cpr_issue_value'] ?? null,
            'issueFinancialValue' => $cpr['cpr_issue_financial_value'] ?? null,
            'profitabilityStartDate' => $this->convertDate($cpr['cpr_profitability_start_date'] ?? null),
            'automaticExpirationIndicator' => $this->mapIndicator($cpr['cpr_automatic_expiration_indicator'] ?? null),
            'collaterals' => $collaterals,
            'products' => [[
                'cprProductName' => $cpr['cpr_product_name'] ?? null,
                'cprProductClassName' => $cpr['cpr_product_class_name'] ?? null,
                'cprProductHarvest' => $cpr['cpr_product_harvest'] ?? null,
                'cprProductDescription' => $cpr['cpr_product_description'] ?? null,
                'cprProductQuantity' => $cpr['cpr_product_quantity'] ?? null,
                'measureUnitName' => $cpr['cpr_measure_unit_name'] ?? null,
                'packagingWayName' => $cpr['cpr_packaging_way_name'] ?? null,
                'cprProductStatusCode' => $cpr['cpr_product_status_code'] ?? null,
                'productionTypeCode' => $cpr['cpr_production_type_code'] ?? null,
            ]],
            'deliveryPlace' => [
                'documentDeadlineDaysNumber' => $cpr['cpr_document_deadline_days_number'] ?? null,
                'placeName' => $cpr['cpr_place_name'] ?? null,
                'stateAcronym' => $cpr['cpr_deliveryPlace_state_acronym'] ?? null,
                'cityName' => $cpr['cpr_deliveryPlace_city_name'] ?? null,
            ],
            'issuers' => $issuers,
            'scr' => [
                'scrTypeCode' => $cpr['cpr_scr_type_code'] ?? null,
                'finalityCode' => $cpr['cpr_finality_code'] ?? null,
                'contractCode' => $cpr['cpr_contract_code'] ?? null,
            ],
            'deposit' => [
                'otcFavoredAccountCode' => '64359.00-3',
                'selfNumber' => $cpr['cpr_contract_code'] ?? null,
                'settlementModalityTypeCode' => '0',
                'depositQuantity' => '1',
            ],
            'creditor' => [
                'creditorName' => $cpr['cpr_creditor_name'] ?? null,
                'documentNumber' => $cpr['cpr_creditor_document_number'] ?? null,
            ],
            'productionPlaces' => $productionPlaces,
        ];

        return ['data' => ['instrument' => $instrument]];
    }

    private function splitSemicolonList($value): array
    {
        if (is_array($value)) {
            $items = array_map(static fn($v) => trim((string)$v), array_values($value));
            return array_values(array_filter($items, static fn($v) => $v !== ''));
        }

        if (!is_string($value)) {
            return [];
        }

        $trimmed = trim($value);
        if ($trimmed === '') {
            return [];
        }

        $parts = preg_split('/\s*;\s*/', $trimmed) ?: [];
        $parts = array_map(static fn($v) => trim((string)$v), $parts);
        return array_values(array_filter($parts, static fn($v) => $v !== ''));
    }

    private function convertDate(?string $date): ?string
    {
        if (!is_string($date) || trim($date) === '') {
            return null;
        }
        $t = trim($date);
        if (mb_strtoupper($t, 'UTF-8') === 'NA') {
            return null;
        }
        $dt = \DateTime::createFromFormat('Y-m-d', $t);
        if ($dt instanceof \DateTime) {
            return $dt->format('d/m/Y');
        }
        $alt = \DateTime::createFromFormat('d/m/Y', $t);
        if ($alt instanceof \DateTime) {
            return $t;
        }
        return $t;
    }

    private function mapIndicator($value): ?string
    {
        if (!is_string($value)) {
            return null;
        }
        $v = mb_strtoupper(trim($value), 'UTF-8');
        if ($v === 'SIM' || $v === 'S') {
            return 'S';
        }
        if ($v === 'N' || $v === 'NAO' || $v === 'NÃO') {
            return 'N';
        }
        if ($v === 'NA') {
            return null;
        }
        return $value;
    }

    public function sanitize(array $payload): array
    {
        return $this->removeNulls($payload);
    }

    private function removeNulls($value)
    {
        if (is_array($value)) {
            $isAssoc = $this->isAssoc($value);
            $result = [];
            foreach ($value as $k => $v) {
                if (is_string($v) && mb_strtoupper(trim($v), 'UTF-8') === 'NA') {
                    $v = null;
                }
                $v = $this->removeNulls($v);
                if (is_array($v) && count($v) === 0) {
                    continue;
                }
                $result[$k] = $v;
            }
            if (!$isAssoc) {
                $result = array_values($result);
            }
            return $result;
        }
        if (is_string($value) && mb_strtoupper(trim($value), 'UTF-8') === 'NA') {
            return null;
        }
        return $value;
    }

    private function isAssoc(array $arr): bool
    {
        return array_keys($arr) !== range(0, count($arr) - 1);
    }

    public function postCpr(string $accessToken, array $payload): array
    {
        $url = $_ENV['B3_URL_CPR'] ?? null;
        if (!$url) {
            throw new \RuntimeException('B3_URL_CPR not configured');
        }

        $json = json_encode($this->sanitize($payload), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json === false) {
            throw new \RuntimeException('Failed to encode payload');
        }

        $certPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . '319245319-320109623_PROD.cer';
        $keyPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . '319245319-320109623_PROD.key';
        $certPass = $_ENV['CERT_PASS'] ?? null;
        if (!file_exists($certPath) || !file_exists($keyPath)) {
            throw new \RuntimeException('Client certificate or key not found');
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: Bearer ' . $accessToken,
        ];
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($ch, CURLOPT_SSLCERT, $certPath);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $keyPath);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        if ($certPass) {
            curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $certPass);
        }
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

        $responseBody = curl_exec($ch);
        $curlErrNo = curl_errno($ch);
        $curlErr = curl_error($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($curlErrNo !== 0) {
            return ['error' => $curlErr, 'status' => 0];
        }

        $decoded = json_decode((string)$responseBody, true);
        if ($decoded === null) {
            return ['raw' => $responseBody, 'status' => $httpCode ?: 502];
        }
        return ['json' => $decoded, 'status' => $httpCode ?: 200];
    }
}
