<?php
declare(strict_types=1);

namespace App\Services;

use App\Config\Config;
use App\Core\DB;
use App\Services\SefazService;
use NFePHP\NFe\Tools;
use NFePHP\Common\Certificate;

class NFePhpService
{
    public static function testarAcesso(int $tenantId): array
    {
        $stmt = DB::pdo()->prepare("SELECT name, cnpj, uf, cert_path, cert_password, nsu, ambiente, cert_password_enc FROM tenants WHERE id = ?");
        $stmt->execute([$tenantId]);
        $tenant = $stmt->fetch();
        if (!$tenant) {
            return [
                'ok' => false,
                'status' => 0,
                'mensagem' => 'Empresa não encontrada.',
            ];
        }
        $cnpj = preg_replace('/\D+/', '', (string)($tenant['cnpj'] ?? ''));
        $uf = (string)($tenant['uf'] ?? '');
        $razao = (string)($tenant['razao_social'] ?? ($tenant['name'] ?? ''));
        $certPath = (string)($tenant['cert_path'] ?? '');
        $certPass = (string)($tenant['cert_password'] ?? '');
        $ambienteTenant = (string)($tenant['ambiente'] ?? '');
        if (!$certPass && !empty($tenant['cert_password_enc'])) {
            try {
                $certPass = \App\Services\CryptoService::decrypt((string)$tenant['cert_password_enc']);
            } catch (\Throwable $e) {
            }
        }
        if (!$cnpj || !$uf || !$certPath || !is_file($certPath)) {
            $msg = 'CNPJ, UF ou certificado não configurados corretamente.';
            $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
            $log->execute([$tenantId, 'teste', '', $msg, 400, date('Y-m-d H:i:s')]);
            return [
                'ok' => false,
                'status' => 400,
                'mensagem' => $msg,
            ];
        }
        $nsuStr = preg_replace('/\D+/', '', (string)($tenant['nsu'] ?? '0'));
        $nsuInt = (int)($nsuStr !== '' ? $nsuStr : '0');
        $config = [
            'atualizacao' => date('Y-m-d H:i:s'),
            'tpAmb' => ($ambienteTenant ?: Config::get('env')) === 'producao' ? 1 : 2,
            'razaosocial' => $razao,
            'cnpj' => $cnpj,
            'siglaUF' => $uf,
            'schemes' => 'PL_009_V4',
            'versao' => '4.00',
            'tokenIBPT' => '',
            'CSC' => '',
            'CSCid' => ''
        ];
        if ($certPass === '') {
            $msg = 'Senha do certificado não informada.';
            $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
            $log->execute([$tenantId, 'teste', '', $msg, 400, date('Y-m-d H:i:s')]);
            return [
                'ok' => false,
                'status' => 400,
                'mensagem' => $msg,
            ];
        }
        $nsuPad = str_pad((string)$nsuInt, 15, '0', STR_PAD_LEFT);
        $fb = SefazService::distribuicao($tenantId, $cnpj, $nsuPad);
        $fbBody = (string)($fb['body'] ?? '');
        if ($fbBody === '') {
            $statusHttp = (int)($fb['status'] ?? 0);
            $msgBase = 'Resposta vazia da SEFAZ no teste.';
            $detalhe = '';
            try {
                $stmtErr = DB::pdo()->query("SELECT status_code, error FROM sefaz_failed ORDER BY id DESC LIMIT 1");
                $lastErr = $stmtErr->fetch();
                if ($lastErr) {
                    $statusHttp = (int)($lastErr['status_code'] ?? $statusHttp);
                    if (!empty($lastErr['error'])) {
                        $detalhe = ' Detalhe técnico: ' . $lastErr['error'];
                    }
                }
            } catch (\Throwable $e) {
            }
            $msg = $msgBase . $detalhe;
            $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
            $log->execute([$tenantId, 'teste', '', $msg, $statusHttp ?: 500, date('Y-m-d H:i:s')]);
            return [
                'ok' => false,
                'status' => $statusHttp ?: 500,
                'mensagem' => $msg,
            ];
        }
        $dom = new \DOMDocument('1.0', 'utf-8');
        $dom->loadXML($fbBody);
        $xpath = new \DOMXPath($dom);
        $xpath->registerNamespace('nfe', 'http://www.portalfiscal.inf.br/nfe');
        $cStatNode = $xpath->query('//nfe:retDistDFeInt/nfe:cStat')->item(0);
        $xMotivoNode = $xpath->query('//nfe:retDistDFeInt/nfe:xMotivo')->item(0);
        $cStat = $cStatNode ? (int)$cStatNode->nodeValue : 0;
        $xMotivo = $xMotivoNode ? $xMotivoNode->nodeValue : '';
        $ok = in_array($cStat, [137, 138], true);
        $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
        $log->execute([$tenantId, 'teste', '', $xMotivo ?: 'Sem mensagem de retorno.', $cStat, date('Y-m-d H:i:s')]);
        return [
            'ok' => $ok,
            'status' => $cStat,
            'mensagem' => $xMotivo ?: 'Sem mensagem de retorno.',
        ];
    }

    public static function baixarNotasDestinadas(int $tenantId): int
    {
        $stmt = DB::pdo()->prepare("SELECT name, cnpj, uf, cert_path, cert_password, nsu FROM tenants WHERE id = ?");
        $stmt->execute([$tenantId]);
        $tenant = $stmt->fetch();
        if (!$tenant) {
            return 0;
        }
        $cnpj = preg_replace('/\D+/', '', (string)($tenant['cnpj'] ?? ''));
        $uf = (string)($tenant['uf'] ?? '');
        $razao = (string)($tenant['razao_social'] ?? ($tenant['name'] ?? ''));
        $certPath = (string)($tenant['cert_path'] ?? '');
        $certPass = (string)($tenant['cert_password'] ?? '');
        $ambienteTenant = (string)($tenant['ambiente'] ?? '');
        if (!$certPass && !empty($tenant['cert_password_enc'])) {
            try {
                $certPass = \App\Services\CryptoService::decrypt((string)$tenant['cert_password_enc']);
            } catch (\Throwable $e) {
            }
        }
        if (!$cnpj || !$uf || !$certPath || !is_file($certPath)) {
            return 0;
        }
        $nsuStr = preg_replace('/\D+/', '', (string)($tenant['nsu'] ?? '0'));
        $nsuInt = (int)($nsuStr !== '' ? $nsuStr : '0');
        $config = [
            'atualizacao' => date('Y-m-d H:i:s'),
            'tpAmb' => ($ambienteTenant ?: Config::get('env')) === 'producao' ? 1 : 2,
            'razaosocial' => $razao,
            'cnpj' => $cnpj,
            'siglaUF' => $uf,
            'schemes' => 'PL_009_V4',
            'versao' => '4.00',
            'tokenIBPT' => '',
            'CSC' => '',
            'CSCid' => ''
        ];
        if ($certPass === '') {
            $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
            $log->execute([$tenantId, 'distribuicao', '', 'Senha do certificado não informada.', 400, date('Y-m-d H:i:s')]);
            return 0;
        }
        try {
            $pfxContent = file_get_contents($certPath);
            $cert = Certificate::readPfx($pfxContent, $certPass);
            $tools = new Tools(json_encode($config, JSON_UNESCAPED_UNICODE), $cert);
            $tools->model(55);
        } catch (\Throwable $e) {
            $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
            $log->execute([$tenantId, 'distribuicao', '', $e->getMessage(), 500, date('Y-m-d H:i:s')]);
            return 0;
        }
        $totalNovas = 0;
        $loop = 0;
        do {
            $loop++;
            try {
                $resp = $tools->sefazDistDFe($nsuInt, 0);
            } catch (\Throwable $e) {
                $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
                $log->execute([$tenantId, 'distribuicao', '', $e->getMessage(), 500, date('Y-m-d H:i:s')]);
                break;
            }
            if (!$resp) {
                $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
                $log->execute([$tenantId, 'distribuicao', '', 'Resposta vazia da SEFAZ.', 500, date('Y-m-d H:i:s')]);
                break;
            }
            $dom = new \DOMDocument('1.0', 'utf-8');
            $dom->loadXML($resp);
            $xpath = new \DOMXPath($dom);
            $xpath->registerNamespace('nfe', 'http://www.portalfiscal.inf.br/nfe');
            $cStatNode = $xpath->query('//nfe:retDistDFeInt/nfe:cStat')->item(0);
            if ($cStatNode && $cStatNode->nodeValue !== '138') {
                $xMotivoNode = $xpath->query('//nfe:retDistDFeInt/nfe:xMotivo')->item(0);
                $msg = $xMotivoNode ? $xMotivoNode->nodeValue : 'Retorno diferente de 138.';
                $status = (int)$cStatNode->nodeValue;
                $log = DB::pdo()->prepare("INSERT INTO sefaz_logs (tenant_id, operation, request, response, status_code, created_at) VALUES (?, ?, ?, ?, ?, ?)");
                $log->execute([$tenantId, 'distribuicao', '', $msg, $status, date('Y-m-d H:i:s')]);
                break;
            }
            $docZipNodes = $xpath->query('//nfe:retDistDFeInt/nfe:loteDistDFeInt/nfe:docZip');
            if (!$docZipNodes || $docZipNodes->length === 0) {
                break;
            }
            foreach ($docZipNodes as $node) {
                $schema = $node->getAttribute('schema');
                $bin = base64_decode($node->nodeValue);
                $xml = function_exists('gzdecode') ? @gzdecode($bin) : @gzinflate(substr($bin, 10));
                if (!$xml) {
                    continue;
                }
                if (strpos($schema, 'resNFe') !== false) {
                    continue;
                }
                $chave = '';
                $numero = '';
                $dataEmissao = null;
                $valor = 0.0;
                $emitCNPJ = '';
                $destCNPJ = '';
                $cfop = '';
                $cst = '';
                try {
                    $sx = @simplexml_load_string($xml);
                    if ($sx) {
                        $nfe = isset($sx->NFe) ? $sx->NFe : $sx;
                        if (isset($nfe->infNFe)) {
                            $inf = $nfe->infNFe;
                            if (isset($inf['Id'])) {
                                $id = (string)$inf['Id'];
                                if (preg_match('/NFe(\d{44})/', $id, $m)) {
                                    $chave = $m[1];
                                }
                            }
                            if (isset($inf->ide->nNF)) {
                                $numero = (string)$inf->ide->nNF;
                            }
                            if (isset($inf->ide->dhEmi)) {
                                $dataEmissao = (string)$inf->ide->dhEmi;
                            } elseif (isset($inf->ide->dEmi)) {
                                $dataEmissao = (string)$inf->ide->dEmi . 'T00:00:00';
                            }
                            if (isset($inf->total->ICMSTot->vNF)) {
                                $valor = (float)$inf->total->ICMSTot->vNF;
                            }
                            if (isset($inf->emit->CNPJ)) {
                                $emitCNPJ = preg_replace('/\D+/', '', (string)$inf->emit->CNPJ);
                            }
                            if (isset($inf->dest->CNPJ)) {
                                $destCNPJ = preg_replace('/\D+/', '', (string)$inf->dest->CNPJ);
                            }
                            if (isset($inf->det)) {
                                // pega CFOP do primeiro item
                                foreach ($inf->det as $det) {
                                    if (isset($det->prod->CFOP)) {
                                        $cfop = (string)$det->prod->CFOP;
                                    }
                                    // tenta extrair CST/CSOSN do bloco ICMS
                                    if (isset($det->imposto->ICMS)) {
                                        foreach ($det->imposto->ICMS->children() as $icmsVar) {
                                            if (isset($icmsVar->CST)) {
                                                $cst = (string)$icmsVar->CST;
                                            } elseif (isset($icmsVar->CSOSN)) {
                                                $cst = (string)$icmsVar->CSOSN;
                                            }
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }
                } catch (\Throwable $e) {
                }
                if (!$chave && preg_match('/<chNFe>(\d{44})<\/chNFe>/', $xml, $m)) {
                    $chave = $m[1];
                }
                if (!$chave) {
                    continue;
                }
                $anoMes = $dataEmissao ? substr(str_replace(['-', ':', 'T'], '', $dataEmissao), 0, 6) : date('Ym');
                $base = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'xml' . DIRECTORY_SEPARATOR . $cnpj . DIRECTORY_SEPARATOR . substr($anoMes, 0, 4) . '-' . substr($anoMes, 4, 2);
                if (!is_dir($base)) {
                    @mkdir($base, 0777, true);
                }
                $path = $base . DIRECTORY_SEPARATOR . $chave . '.xml';
                file_put_contents($path, $xml);
                $now = date('Y-m-d H:i:s');
                $exists = DB::pdo()->prepare("SELECT id FROM notas WHERE tenant_id = ? AND chave_acesso = ?");
                $exists->execute([$tenantId, $chave]);
                if ($exists->fetch()) {
                    continue;
                }
                $ins = DB::pdo()->prepare("INSERT INTO notas (tenant_id, numero_nota, chave_acesso, data_emissao, valor, caminho_xml, emitente_cnpj, destinatario_cnpj, cfop, cst, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
                $ins->execute([$tenantId, $numero, $chave, $dataEmissao ? substr($dataEmissao, 0, 19) : null, $valor, $path, $emitCNPJ, $destCNPJ, $cfop, $cst, $now]);
                $totalNovas++;
            }
            $ultNSUNode = $xpath->query('//nfe:retDistDFeInt/nfe:ultNSU')->item(0);
            if ($ultNSUNode) {
                $newNsu = $ultNSUNode->nodeValue;
                if ((string)$newNsu === (string)$nsuInt) {
                    break;
                }
                $nsuInt = (int)$newNsu;
                $upd = DB::pdo()->prepare("UPDATE tenants SET nsu = ?, updated_at = ? WHERE id = ?");
                $upd->execute([$newNsu, date('Y-m-d H:i:s'), $tenantId]);
            } else {
                break;
            }
        } while ($loop < 20);
        return $totalNovas;
    }
}
