<?php

declare(strict_types=1);

namespace App\Services\NFSe;

use DOMDocument;
use Exception;

class NFSeRecifeService
{
    private const XMLNS = 'http://www.abrasf.org.br/nfse.xsd';
    private const CODIGO_MUNICIPIO_RECIFE = '2611606';

    private array $empresa;

    public function __construct(array $empresa)
    {
        $this->empresa = $empresa;
    }

    /**
     * Gera o XML do EnviarLoteRpsEnvio seguindo o layout ABRASF (Recife).
     *
     * @throws Exception
     */
    public function gerarXmlRps(array $payload): string
    {
        $dom = new DOMDocument('1.0', 'UTF-8');
        $dom->formatOutput = true;

        $root = $dom->createElementNS(self::XMLNS, 'EnviarLoteRpsEnvio');
        $dom->appendChild($root);

        $lote = $dom->createElement('LoteRps');
        $lote->setAttribute('Id', 'Lote_' . $payload['numero_lote']);
        $lote->setAttribute('versao', '1.00');
        $root->appendChild($lote);

        $this->appendTextNode($dom, $lote, 'NumeroLote', (string) $payload['numero_lote']);

        $prestadorCnpj = $this->sanitizarNumero($this->empresa['cnpj'] ?? $this->empresa['document'] ?? '');
        if (strlen($prestadorCnpj) !== 14) {
            throw new Exception('CNPJ do prestador inválido para gerar NFS-e.');
        }

        $cpfCnpjPrestador = $dom->createElement('CpfCnpj');
        $lote->appendChild($cpfCnpjPrestador);
        $this->appendTextNode($dom, $cpfCnpjPrestador, 'Cnpj', $prestadorCnpj);

        $inscricaoMunicipal = $this->sanitizarNumero($this->empresa['inscricao_municipal'] ?? '');
        if ($inscricaoMunicipal === '') {
            throw new Exception('Inscrição municipal do prestador não encontrada.');
        }
        $this->appendTextNode($dom, $lote, 'InscricaoMunicipal', $inscricaoMunicipal);
        $this->appendTextNode($dom, $lote, 'QuantidadeRps', '1');

        $listaRps = $dom->createElement('ListaRps');
        $lote->appendChild($listaRps);

        $rps = $dom->createElement('Rps');
        $listaRps->appendChild($rps);

        $infRps = $dom->createElement('InfRps');
        $infRps->setAttribute('Id', 'RPS_' . $payload['identificacao']['numero']);
        $rps->appendChild($infRps);

        $identificacaoRps = $dom->createElement('IdentificacaoRps');
        $infRps->appendChild($identificacaoRps);

        $this->appendTextNode($dom, $identificacaoRps, 'Numero', (string) $payload['identificacao']['numero']);
        $this->appendTextNode($dom, $identificacaoRps, 'Serie', $payload['identificacao']['serie']);
        $this->appendTextNode($dom, $identificacaoRps, 'Tipo', (string) ($payload['identificacao']['tipo'] ?? 1));

        $this->appendTextNode($dom, $infRps, 'DataEmissao', $payload['data_emissao']);
        $this->appendTextNode($dom, $infRps, 'NaturezaOperacao', (string) $payload['natureza_operacao']);

        if (!empty($payload['regime_especial_tributacao'])) {
            $this->appendTextNode($dom, $infRps, 'RegimeEspecialTributacao', (string) $payload['regime_especial_tributacao']);
        }

        $this->appendTextNode($dom, $infRps, 'OptanteSimplesNacional', (string) ($payload['optante_simples_nacional'] ?? '2'));
        $this->appendTextNode($dom, $infRps, 'IncentivadorCultural', (string) ($payload['incentivador_cultural'] ?? '2'));
        $this->appendTextNode($dom, $infRps, 'Status', (string) ($payload['status'] ?? '1'));

        $servico = $dom->createElement('Servico');
        $infRps->appendChild($servico);

        $valores = $dom->createElement('Valores');
        $servico->appendChild($valores);

        $this->appendTextNode($dom, $valores, 'ValorServicos', $this->formatarValor($payload['servico']['valor_servicos'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorDeducoes', $this->formatarValor($payload['servico']['valor_deducoes'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorPis', $this->formatarValor($payload['servico']['valor_pis'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorCofins', $this->formatarValor($payload['servico']['valor_cofins'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorInss', $this->formatarValor($payload['servico']['valor_inss'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorIr', $this->formatarValor($payload['servico']['valor_ir'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorCsll', $this->formatarValor($payload['servico']['valor_csll'] ?? 0));

        $issRetido = $payload['servico']['iss_retido'] ? '1' : '2';
        $this->appendTextNode($dom, $valores, 'IssRetido', $issRetido);
        $this->appendTextNode($dom, $valores, 'ValorIss', $this->formatarValor($payload['servico']['valor_iss'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorIssRetido', $this->formatarValor($payload['servico']['valor_iss_retido'] ?? 0));
        $this->appendTextNode($dom, $valores, 'OutrasRetencoes', $this->formatarValor($payload['servico']['outras_retencoes'] ?? 0));
        $this->appendTextNode($dom, $valores, 'BaseCalculo', $this->formatarValor($payload['servico']['base_calculo'] ?? $payload['servico']['valor_servicos'] ?? 0));
        $this->appendTextNode($dom, $valores, 'Aliquota', $this->formatarAliquota($payload['servico']['aliquota'] ?? 0));
        $this->appendTextNode($dom, $valores, 'ValorLiquidoNfse', $this->formatarValor($payload['servico']['valor_liquido'] ?? $payload['servico']['valor_servicos'] ?? 0));
        $this->appendTextNode($dom, $valores, 'DescontoIncondicionado', $this->formatarValor($payload['servico']['desconto_incondicionado'] ?? 0));
        $this->appendTextNode($dom, $valores, 'DescontoCondicionado', $this->formatarValor($payload['servico']['desconto_condicionado'] ?? 0));

        $this->appendTextNode($dom, $servico, 'ItemListaServico', $payload['servico']['item_lista_servico']);

        if (!empty($payload['servico']['codigo_cnae'])) {
            $this->appendTextNode($dom, $servico, 'CodigoCnae', $payload['servico']['codigo_cnae']);
        }

        if (!empty($payload['servico']['codigo_tributacao_municipio'])) {
            $this->appendTextNode($dom, $servico, 'CodigoTributacaoMunicipio', $payload['servico']['codigo_tributacao_municipio']);
        }

        $this->appendTextNode($dom, $servico, 'Discriminacao', $payload['servico']['discriminacao']);
        $this->appendTextNode($dom, $servico, 'CodigoMunicipio', $payload['servico']['codigo_municipio'] ?? self::CODIGO_MUNICIPIO_RECIFE);

        $prestador = $dom->createElement('Prestador');
        $infRps->appendChild($prestador);
        $cpfCnpjPrestadorRps = $dom->createElement('CpfCnpj');
        $prestador->appendChild($cpfCnpjPrestadorRps);
        $this->appendTextNode($dom, $cpfCnpjPrestadorRps, 'Cnpj', $prestadorCnpj);
        $this->appendTextNode($dom, $prestador, 'InscricaoMunicipal', $inscricaoMunicipal);

        $tomadorData = $payload['tomador'] ?? [];
        if (!empty($tomadorData)) {
            $tomador = $dom->createElement('Tomador');
            $infRps->appendChild($tomador);

            $identificacaoTomador = $dom->createElement('IdentificacaoTomador');
            $tomador->appendChild($identificacaoTomador);

            $cpfCnpjTomador = $this->sanitizarNumero($tomadorData['cpf_cnpj'] ?? '');
            if ($cpfCnpjTomador !== '') {
                $cpfCnpjNode = $dom->createElement('CpfCnpj');
                $identificacaoTomador->appendChild($cpfCnpjNode);

                if (strlen($cpfCnpjTomador) === 11) {
                    $this->appendTextNode($dom, $cpfCnpjNode, 'Cpf', $cpfCnpjTomador);
                } elseif (strlen($cpfCnpjTomador) === 14) {
                    $this->appendTextNode($dom, $cpfCnpjNode, 'Cnpj', $cpfCnpjTomador);
                }
            }

            if (!empty($tomadorData['inscricao_municipal'])) {
                $this->appendTextNode($dom, $identificacaoTomador, 'InscricaoMunicipal', $this->sanitizarNumero($tomadorData['inscricao_municipal']));
            }

            if (!empty($tomadorData['razao_social'])) {
                $this->appendTextNode($dom, $tomador, 'RazaoSocial', $tomadorData['razao_social']);
            }

            if (!empty($tomadorData['endereco'])) {
                $endereco = $dom->createElement('Endereco');
                $tomador->appendChild($endereco);

                $this->appendTextNode($dom, $endereco, 'Endereco', $tomadorData['endereco']['logradouro'] ?? '');
                $this->appendTextNode($dom, $endereco, 'Numero', $tomadorData['endereco']['numero'] ?? 'S/N');

                if (!empty($tomadorData['endereco']['complemento'])) {
                    $this->appendTextNode($dom, $endereco, 'Complemento', $tomadorData['endereco']['complemento']);
                }

                $this->appendTextNode($dom, $endereco, 'Bairro', $tomadorData['endereco']['bairro'] ?? '');
                $this->appendTextNode($dom, $endereco, 'CodigoMunicipio', $tomadorData['endereco']['codigo_municipio'] ?? self::CODIGO_MUNICIPIO_RECIFE);
                $this->appendTextNode($dom, $endereco, 'Uf', $tomadorData['endereco']['uf'] ?? 'PE');
                $cepTomador = $this->sanitizarNumero($tomadorData['endereco']['cep'] ?? '');
                if ($cepTomador !== '') {
                    $this->appendTextNode($dom, $endereco, 'Cep', str_pad($cepTomador, 8, '0', STR_PAD_LEFT));
                }
            }

            if (!empty($tomadorData['contato'])) {
                $contato = $dom->createElement('Contato');
                $tomador->appendChild($contato);

                if (!empty($tomadorData['contato']['telefone'])) {
                    $this->appendTextNode($dom, $contato, 'Telefone', $this->sanitizarNumero($tomadorData['contato']['telefone']));
                }

                if (!empty($tomadorData['contato']['email'])) {
                    $this->appendTextNode($dom, $contato, 'Email', $tomadorData['contato']['email']);
                }
            }
        }

        return $dom->saveXML();
    }

    public function salvarXml(string $xml, string $cnpj, string $serie, string $numeroRps): string
    {
        $cnpjLimpo = $this->sanitizarNumero($cnpj);
        $anoMes = date('Y_m');
        $baseDir = rtrim(ROOT_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Integrations' . DIRECTORY_SEPARATOR . 'NFSe' . DIRECTORY_SEPARATOR . 'arquivos' . DIRECTORY_SEPARATOR . $cnpjLimpo . DIRECTORY_SEPARATOR . 'nfse' . DIRECTORY_SEPARATOR . 'xml' . DIRECTORY_SEPARATOR . $anoMes;

        if (!is_dir($baseDir) && !mkdir($baseDir, 0775, true) && !is_dir($baseDir)) {
            throw new Exception('Não foi possível criar o diretório para armazenar o XML da NFS-e.');
        }

        $arquivo = sprintf('RPS_%s_%s.xml', $serie, str_pad((string) $numeroRps, 9, '0', STR_PAD_LEFT));
        $caminhoCompleto = $baseDir . DIRECTORY_SEPARATOR . $arquivo;

        if (file_put_contents($caminhoCompleto, $xml) === false) {
            throw new Exception('Falha ao salvar o XML da NFS-e.');
        }

        return str_replace(rtrim(ROOT_PATH, DIRECTORY_SEPARATOR), '', $caminhoCompleto);
    }

    private function appendTextNode(DOMDocument $dom, \DOMNode $parent, string $tag, string $value): void
    {
        $node = $dom->createElement($tag, htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8'));
        $parent->appendChild($node);
    }

    private function sanitizarNumero(string $valor): string
    {
        return preg_replace('/\D/', '', $valor) ?? '';
    }

    private function formatarValor($valor): string
    {
        return number_format((float) $valor, 2, '.', '');
    }

    private function formatarAliquota($aliquotaPercentual): string
    {
        $aliquotaDecimal = ((float) $aliquotaPercentual) / 100;
        return number_format($aliquotaDecimal, 4, '.', '');
    }
}

