<?php
namespace WorkbloomERP\Module\v0\Contato\Factories;

use DateTimeImmutable;
use Ramsey\Uuid\Uuid;
use WorkbloomERP\Services\DBService;
use WorkbloomERP\Utils\ValidateUtil;
use WorkbloomERP\Exceptions\AppException;
use WorkbloomERP\Module\v0\Empresa\Repos\EmpresaRepo;
use WorkbloomERP\Module\v0\Contato\Repos\ContatoRepo;
use WorkbloomERP\Module\v0\Contato\Models\ContatoModel;
use WorkbloomERP\Module\v0\Empresa\Models\EmpresaModel;
use WorkbloomERP\Module\v0\Contato\DTOs\ContatoCreateDTO;

// Constantes para opções de contato
use WorkbloomERP\Constants\SpedCRTConst;
use WorkbloomERP\Constants\BrasilUfsConst;
use WorkbloomERP\Constants\SpedPaisesConst;
use WorkbloomERP\Module\v0\Contato\Constants\OrgaoPublicoConst;
use WorkbloomERP\Module\v0\Contato\Constants\PersonalidadeConst;
use WorkbloomERP\Module\v0\Contato\Constants\ContribuinteICMSConst;
use WorkbloomERP\Module\v0\Contato\Constants\NFSeConsumoIbsCbsConst;
use WorkbloomERP\Module\v0\Contato\Constants\TipoConst;

class ContatoFactory {
    public function __construct(private EmpresaModel $empresaModel, private ContatoCreateDTO $contatoCreateDTO, private ContatoRepo $contatoRepo, private EmpresaRepo $empresaRepo, private DBService $db) {
        if (!$empresaModel) {
            throw new AppException(message: 'Empresa não encontrada.', code: 404);
        }
        if (!$contatoCreateDTO) {
            throw new AppException(message: 'Dados do contato inválidos.', code: 400);
        }
    }

    public function create(ContatoCreateDTO $contatoCreateDTO, EmpresaModel $empresaModel): ?ContatoModel {
        try {
            // Valida os campos comuns a ambos os tipos de contato (PF e PJ)
            $this->validateCommonFields(contatoCreateDTO: $contatoCreateDTO);

            // Cria o contato com base na personalidade (PF ou PJ)
            $contatoModel = match($contatoCreateDTO->getPersonalidade()) {
                'PF' => $this->createPF(contatoCreateDTO: $contatoCreateDTO, empresaModel: $empresaModel),
                'PJ' => $this->createPJ(contatoCreateDTO: $contatoCreateDTO, empresaModel: $empresaModel)
            };

            return $contatoModel;
        } catch(AppException $e) {
            throw $e;
        }
    }

    private function validateCommonFields(ContatoCreateDTO $contatoCreateDTO): void {
        $errors = [];

        $commonFields = [
            'is_active',
            'tipo',
            'nome_empresarial',
            'personalidade',
            'regime_tributario',
            'contribuinte_icms',
            'orgao_publico',
            'end_cep',
            'end_logradouro',
            'end_numero',
            'end_bairro',
            'end_cidade',
            'end_uf',
            'info_email',
            'info_email_nfe',
            'info_telefone',
            'info_uso_consumo_ibs_cbs',
        ];

        foreach ($commonFields as $field) {
            if (!array_key_exists(key: $field, array: $contatoCreateDTO->toArray()) || $contatoCreateDTO->toArray()[$field] === '') {
                $errors[$field] = 'Campo obrigatório.';
            }
        }

        // Verifica se o campo 'tipo' tem um valor válido
        $tipo = $contatoCreateDTO->getTipo() ? strtoupper(string: $contatoCreateDTO->getTipo()) : null;
        if ($tipo && !TipoConst::isValid(value: $tipo)) {
            $errors['tipo'] = "Valor inválido.";
        }

        // Verifica se o campo 'personalidade' tem um valor válido
        $personalidade = $contatoCreateDTO->getPersonalidade() ? strtoupper(string: $contatoCreateDTO->getPersonalidade()) : null;
        if ($personalidade && !PersonalidadeConst::isValid(value: $personalidade)) {
            $errors['personalidade'] = "Valor inválido.";
        }

        // Valida o regime tributário
        if (!in_array($contatoCreateDTO->getRegimeTributario(), [1, 2, 3, 4, null], true)) {
            $errors['regime_tributario'] = "Valor inválido.";
        }

        if ($errors) {
            throw new AppException(message: 'Dados de contato inválidos.',code: 422, details: $errors);
        }
    }

    private function createPF(ContatoCreateDTO $contatoCreateDTO, EmpresaModel $empresaModel): ContatoModel {
        try {
            // Valida os campos específicos para pessoa física
            $this->validatePFFields(contatoCreateDTO: $contatoCreateDTO, empresaModel: $empresaModel);

            $contatoModel = new ContatoModel();
            $contatoModel->setUuid(Uuid::uuid7()->toString());
            $contatoModel->setEmpresaId($empresaModel->getId());
            $contatoModel->setIsActive($contatoCreateDTO->getIsActive());
            $contatoModel->setTipo($contatoCreateDTO->getTipo());
            $contatoModel->setNomeEmpresarial($contatoCreateDTO->getNomeEmpresarial());
            $contatoModel->setPersonalidade($contatoCreateDTO->getPersonalidade());
            $contatoModel->setRegimeTributario($contatoCreateDTO->getRegimeTributario());
            $contatoModel->setContribuinteIcms($contatoCreateDTO->getContribuinteIcms());
            $contatoModel->setOrgaoPublico($contatoCreateDTO->getOrgaoPublico());
            $contatoModel->setEndCep($contatoCreateDTO->getEndCep());
            $contatoModel->setEndIbge($contatoCreateDTO->getEndIbge());
            $contatoModel->setEndLogradouro($contatoCreateDTO->getEndLogradouro());
            $contatoModel->setEndNumero($contatoCreateDTO->getEndNumero());
            $contatoModel->setEndBairro($contatoCreateDTO->getEndBairro());
            $contatoModel->setEndCidade($contatoCreateDTO->getEndCidade());
            $contatoModel->setEndUf($contatoCreateDTO->getEndUf());
            $contatoModel->setInfoEmail($contatoCreateDTO->getInfoEmail());
            $contatoModel->setInfoEmailNfe($contatoCreateDTO->getInfoEmailNfe());
            $contatoModel->setInfoObservacao($contatoCreateDTO->getInfoObservacao());
            $contatoModel->setInfoTelefone($contatoCreateDTO->getInfoTelefone());
            $contatoModel->setInfoUsoConsumoIbsCbs($contatoCreateDTO->getInfoUsoConsumoIbsCbs());
            $contatoModel->setCreatedAt(new DateTimeImmutable());

            return $contatoModel;
        } catch(AppException $e) {
            throw $e;
        }
    }

    private function validatePFFields(ContatoCreateDTO $contatoCreateDTO, EmpresaModel $empresaModel): void {
        $errors = [];

        if (($contatoCreateDTO->getDocumentCpf() === '') || !ValidateUtil::cpf(cpf: $contatoCreateDTO->getDocumentCpf())) {
            $errors['document_cpf'] = 'CPF informado é inválido.';
            throw new AppException(message: 'Dados de contato inválidos.', code: 422, details: $errors);
        }

        $existingContato = $this->contatoRepo->findOneByConditions(
            empresa_id: $empresaModel->getId(),
            conditions: [
                ['field' => 'document_cpf', 'operator' => '=', 'value' => $contatoCreateDTO->getDocumentCpf()]
            ]
        );

        if ($existingContato) {
            $errors['document_cpf'] = 'CPF já cadastrado para esta empresa.';
            throw new AppException(message: 'Dados de contato inválidos.', code: 422, details: $errors);
        }

        if ($errors) {
            throw new AppException(message: 'Dados de contato inválidos.', code: 422, details: $errors);
        }
    }

    private function createPJ(ContatoCreateDTO $contatoCreateDTO, EmpresaModel $empresaModel): ContatoModel {
        try {
            // Valida os campos específicos para pessoa física
            // $this->validatePFFields(contatoCreateDTO: $contatoCreateDTO);

            return new ContatoModel();
        } catch(AppException $e) {
            throw $e;
        }
    }
}