<?php
namespace Modules\GestorContratos\Services;

use PDO;
use Exception;

class ContratoService {
    private PDO $pdo;
    private string $tenantId;

    public function __construct(string $tenantId) {
        $this->tenantId = $tenantId;
        $this->pdo = \Gestoreasy\Core\Tenant\ConnectionManager::forTenant($tenantId);
    }

    /**
     * Cria um novo contrato
     */
    public function criarContrato(array $dados): int {
        try {
            $this->pdo->beginTransaction();

            // Gerar número único do contrato
            $numero = $this->gerarNumeroContrato();

            // Processar variáveis no conteúdo
            $conteudoHtml = $dados['conteudo_html'] ?? '';
            $variaveisPreenchidas = $dados['variaveis_preenchidas'] ?? [];
            
            if (!empty($variaveisPreenchidas)) {
                $conteudoHtml = $this->preencherVariaveis($conteudoHtml, $variaveisPreenchidas);
            }

            // Inserir contrato
            $stmt = $this->pdo->prepare('
                INSERT INTO contratos (
                    tenant_id, numero, titulo, tipo, modelo_id,
                    cliente_id, fornecedor_id, venda_id, financeiro_id,
                    conteudo_html, variaveis_preenchidas_json,
                    status, data_inicio, data_fim, data_vencimento, data_expiracao,
                    valor_total, valor_parcela, numero_parcelas,
                    observacoes, tags, created_by
                ) VALUES (
                    :tenant_id, :numero, :titulo, :tipo, :modelo_id,
                    :cliente_id, :fornecedor_id, :venda_id, :financeiro_id,
                    :conteudo_html, :variaveis_preenchidas_json,
                    :status, :data_inicio, :data_fim, :data_vencimento, :data_expiracao,
                    :valor_total, :valor_parcela, :numero_parcelas,
                    :observacoes, :tags, :created_by
                )
            ');

            $dataExpiracao = null;
            if (!empty($dados['dias_expiracao'])) {
                $dataExpiracao = date('Y-m-d H:i:s', strtotime('+' . $dados['dias_expiracao'] . ' days'));
            }

            $stmt->execute([
                ':tenant_id' => $this->tenantId,
                ':numero' => $numero,
                ':titulo' => $dados['titulo'] ?? 'Contrato sem título',
                ':tipo' => $dados['tipo'] ?? 'geral',
                ':modelo_id' => $dados['modelo_id'] ?? null,
                ':cliente_id' => $dados['cliente_id'] ?? null,
                ':fornecedor_id' => $dados['fornecedor_id'] ?? null,
                ':venda_id' => $dados['venda_id'] ?? null,
                ':financeiro_id' => $dados['financeiro_id'] ?? null,
                ':conteudo_html' => $conteudoHtml,
                ':variaveis_preenchidas_json' => json_encode($variaveisPreenchidas),
                ':status' => $dados['status'] ?? 'rascunho',
                ':data_inicio' => $dados['data_inicio'] ?? null,
                ':data_fim' => $dados['data_fim'] ?? null,
                ':data_vencimento' => $dados['data_vencimento'] ?? null,
                ':data_expiracao' => $dataExpiracao,
                ':valor_total' => $dados['valor_total'] ?? null,
                ':valor_parcela' => $dados['valor_parcela'] ?? null,
                ':numero_parcelas' => $dados['numero_parcelas'] ?? null,
                ':observacoes' => $dados['observacoes'] ?? null,
                ':tags' => !empty($dados['tags']) ? json_encode($dados['tags']) : null,
                ':created_by' => $dados['created_by'] ?? null
            ]);

            $contratoId = (int)$this->pdo->lastInsertId();

            // Adicionar signatários se fornecidos
            if (!empty($dados['signatarios'])) {
                $this->adicionarSignatarios($contratoId, $dados['signatarios']);
            }

            // Registrar no histórico
            $this->registrarHistorico($contratoId, 'criado', [
                'titulo' => $dados['titulo'] ?? 'Contrato sem título',
                'tipo' => $dados['tipo'] ?? 'geral'
            ]);

            $this->pdo->commit();
            return $contratoId;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new Exception('Erro ao criar contrato: ' . $e->getMessage());
        }
    }

    /**
     * Atualiza um contrato existente
     */
    public function atualizarContrato(int $contratoId, array $dados): bool {
        try {
            $this->pdo->beginTransaction();

            // Buscar contrato atual
            $stmt = $this->pdo->prepare('SELECT * FROM contratos WHERE id = ? AND tenant_id = ?');
            $stmt->execute([$contratoId, $this->tenantId]);
            $contratoAtual = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$contratoAtual) {
                throw new Exception('Contrato não encontrado');
            }

            // Atualizar campos
            $campos = [];
            $valores = [];

            if (isset($dados['titulo'])) {
                $campos[] = 'titulo = :titulo';
                $valores[':titulo'] = $dados['titulo'];
            }

            if (isset($dados['conteudo_html'])) {
                $conteudoHtml = $dados['conteudo_html'];
                if (!empty($dados['variaveis_preenchidas'])) {
                    $conteudoHtml = $this->preencherVariaveis($conteudoHtml, $dados['variaveis_preenchidas']);
                }
                $campos[] = 'conteudo_html = :conteudo_html';
                $valores[':conteudo_html'] = $conteudoHtml;
            }

            if (isset($dados['status'])) {
                $campos[] = 'status = :status';
                $valores[':status'] = $dados['status'];
            }

            if (isset($dados['data_inicio'])) {
                $campos[] = 'data_inicio = :data_inicio';
                $valores[':data_inicio'] = $dados['data_inicio'];
            }

            if (isset($dados['data_fim'])) {
                $campos[] = 'data_fim = :data_fim';
                $valores[':data_fim'] = $dados['data_fim'];
            }

            if (isset($dados['valor_total'])) {
                $campos[] = 'valor_total = :valor_total';
                $valores[':valor_total'] = $dados['valor_total'];
            }

            if (!empty($campos)) {
                $valores[':id'] = $contratoId;
                $valores[':tenant_id'] = $this->tenantId;

                $sql = 'UPDATE contratos SET ' . implode(', ', $campos) . ' WHERE id = :id AND tenant_id = :tenant_id';
                $stmt = $this->pdo->prepare($sql);
                $stmt->execute($valores);
            }

            // Registrar no histórico
            $this->registrarHistorico($contratoId, 'editado', $dados, $contratoAtual);

            $this->pdo->commit();
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new Exception('Erro ao atualizar contrato: ' . $e->getMessage());
        }
    }

    /**
     * Busca um contrato por ID
     */
    public function buscarContrato(int $contratoId): ?array {
        $stmt = $this->pdo->prepare('
            SELECT c.*,
                   m.nome as modelo_nome,
                   cl.nome as cliente_nome,
                   f.nome as fornecedor_nome
            FROM contratos c
            LEFT JOIN contratos_modelos m ON c.modelo_id = m.id
            LEFT JOIN clients cl ON c.cliente_id = cl.id
            LEFT JOIN suppliers f ON c.fornecedor_id = f.id
            WHERE c.id = ? AND c.tenant_id = ?
        ');
        $stmt->execute([$contratoId, $this->tenantId]);
        $contrato = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($contrato) {
            $contrato['variaveis_preenchidas_json'] = json_decode($contrato['variaveis_preenchidas_json'] ?? '{}', true);
            $contrato['tags'] = json_decode($contrato['tags'] ?? '[]', true);
            $contrato['signatarios'] = $this->buscarSignatarios($contratoId);
            $contrato['anexos'] = $this->buscarAnexos($contratoId);
        }

        return $contrato ?: null;
    }

    /**
     * Lista contratos com filtros
     */
    public function listarContratos(array $filtros = [], int $page = 1, int $perPage = 20): array {
        $where = ['c.tenant_id = :tenant_id'];
        $params = [':tenant_id' => $this->tenantId];

        if (!empty($filtros['status'])) {
            $where[] = 'c.status = :status';
            $params[':status'] = $filtros['status'];
        }

        if (!empty($filtros['tipo'])) {
            $where[] = 'c.tipo = :tipo';
            $params[':tipo'] = $filtros['tipo'];
        }

        if (!empty($filtros['cliente_id'])) {
            $where[] = 'c.cliente_id = :cliente_id';
            $params[':cliente_id'] = $filtros['cliente_id'];
        }

        if (!empty($filtros['numero'])) {
            $where[] = 'c.numero LIKE :numero';
            $params[':numero'] = '%' . $filtros['numero'] . '%';
        }

        if (!empty($filtros['data_inicio'])) {
            $where[] = 'c.data_inicio >= :data_inicio';
            $params[':data_inicio'] = $filtros['data_inicio'];
        }

        if (!empty($filtros['data_fim'])) {
            $where[] = 'c.data_fim <= :data_fim';
            $params[':data_fim'] = $filtros['data_fim'];
        }

        $whereSql = 'WHERE ' . implode(' AND ', $where);
        $offset = ($page - 1) * $perPage;

        // Contar total
        $countSql = "SELECT COUNT(*) FROM contratos c $whereSql";
        $countStmt = $this->pdo->prepare($countSql);
        $countStmt->execute($params);
        $total = (int)$countStmt->fetchColumn();

        // Buscar contratos
        $sql = "
            SELECT c.*,
                   m.nome as modelo_nome,
                   cl.nome as cliente_nome,
                   f.nome as fornecedor_nome
            FROM contratos c
            LEFT JOIN contratos_modelos m ON c.modelo_id = m.id
            LEFT JOIN clients cl ON c.cliente_id = cl.id
            LEFT JOIN suppliers f ON c.fornecedor_id = f.id
            $whereSql
            ORDER BY c.created_at DESC
            LIMIT :limit OFFSET :offset
        ";

        $stmt = $this->pdo->prepare($sql);
        foreach ($params as $key => $value) {
            $stmt->bindValue($key, $value);
        }
        $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $stmt->execute();

        $contratos = $stmt->fetchAll(PDO::FETCH_ASSOC);

        return [
            'data' => $contratos,
            'total' => $total,
            'page' => $page,
            'per_page' => $perPage,
            'total_pages' => ceil($total / $perPage)
        ];
    }

    /**
     * Adiciona signatários a um contrato
     */
    public function adicionarSignatarios(int $contratoId, array $signatarios): void {
        // Remover signatários existentes se necessário
        $stmt = $this->pdo->prepare('DELETE FROM contratos_signatarios WHERE contrato_id = ?');
        $stmt->execute([$contratoId]);

        // Inserir novos signatários
        $stmt = $this->pdo->prepare('
            INSERT INTO contratos_signatarios (
                contrato_id, nome, email, cpf_cnpj, telefone,
                tipo, user_id, ordem, status, token_acesso, token_expiracao
            ) VALUES (
                :contrato_id, :nome, :email, :cpf_cnpj, :telefone,
                :tipo, :user_id, :ordem, :status, :token_acesso, :token_expiracao
            )
        ');

        foreach ($signatarios as $index => $signatario) {
            $token = bin2hex(random_bytes(32));
            $dataExpiracao = date('Y-m-d H:i:s', strtotime('+30 days'));

            $stmt->execute([
                ':contrato_id' => $contratoId,
                ':nome' => $signatario['nome'],
                ':email' => $signatario['email'],
                ':cpf_cnpj' => $signatario['cpf_cnpj'] ?? null,
                ':telefone' => $signatario['telefone'] ?? null,
                ':tipo' => $signatario['tipo'] ?? 'externo',
                ':user_id' => $signatario['user_id'] ?? null,
                ':ordem' => $signatario['ordem'] ?? ($index + 1),
                ':status' => 'pendente',
                ':token_acesso' => $token,
                ':token_expiracao' => $dataExpiracao
            ]);
        }
    }

    /**
     * Busca signatários de um contrato
     */
    public function buscarSignatarios(int $contratoId): array {
        $stmt = $this->pdo->prepare('
            SELECT * FROM contratos_signatarios
            WHERE contrato_id = ?
            ORDER BY ordem ASC
        ');
        $stmt->execute([$contratoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Busca anexos de um contrato
     */
    public function buscarAnexos(int $contratoId): array {
        $stmt = $this->pdo->prepare('
            SELECT * FROM contratos_anexos
            WHERE contrato_id = ?
            ORDER BY created_at DESC
        ');
        $stmt->execute([$contratoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Preenche variáveis no conteúdo do contrato
     */
    private function preencherVariaveis(string $conteudo, array $variaveis): string {
        foreach ($variaveis as $chave => $valor) {
            $conteudo = str_replace('[' . $chave . ']', $valor, $conteudo);
            $conteudo = str_replace('{{' . $chave . '}}', $valor, $conteudo);
        }
        return $conteudo;
    }

    /**
     * Gera número único do contrato
     */
    private function gerarNumeroContrato(): string {
        $ano = date('Y');
        $stmt = $this->pdo->prepare('
            SELECT COUNT(*) + 1 as proximo
            FROM contratos
            WHERE tenant_id = ? AND YEAR(created_at) = ?
        ');
        $stmt->execute([$this->tenantId, $ano]);
        $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
        $numero = $resultado['proximo'] ?? 1;
        return 'CT-' . $ano . '-' . str_pad($numero, 6, '0', STR_PAD_LEFT);
    }

    /**
     * Registra ação no histórico
     */
    private function registrarHistorico(int $contratoId, string $acao, array $dados, ?array $dadosAntes = null): void {
        $stmt = $this->pdo->prepare('
            INSERT INTO contratos_historico (
                contrato_id, acao, descricao, user_id, user_nome, user_email,
                dados_antes_json, dados_depois_json, ip_address, user_agent
            ) VALUES (
                :contrato_id, :acao, :descricao, :user_id, :user_nome, :user_email,
                :dados_antes_json, :dados_depois_json, :ip_address, :user_agent
            )
        ');

        $userContext = \Gestoreasy\Core\Auth\UserContext::getCurrent();
        
        $stmt->execute([
            ':contrato_id' => $contratoId,
            ':acao' => $acao,
            ':descricao' => $this->gerarDescricaoAcao($acao, $dados),
            ':user_id' => $userContext?->userId,
            ':user_nome' => $userContext?->name,
            ':user_email' => $userContext?->email,
            ':dados_antes_json' => $dadosAntes ? json_encode($dadosAntes) : null,
            ':dados_depois_json' => json_encode($dados),
            ':ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
            ':user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null
        ]);
    }

    /**
     * Gera descrição da ação para o histórico
     */
    private function gerarDescricaoAcao(string $acao, array $dados): string {
        $descricoes = [
            'criado' => 'Contrato criado',
            'editado' => 'Contrato editado',
            'enviado' => 'Contrato enviado para assinatura',
            'assinado' => 'Contrato assinado',
            'cancelado' => 'Contrato cancelado',
            'arquivado' => 'Contrato arquivado'
        ];
        return $descricoes[$acao] ?? 'Ação realizada no contrato';
    }

    /**
     * Arquivar contrato
     */
    public function arquivarContrato(int $contratoId): bool {
        try {
            $this->pdo->beginTransaction();

            $userContext = \Gestoreasy\Core\Auth\UserContext::getCurrent();

            $stmt = $this->pdo->prepare('
                UPDATE contratos
                SET status = "arquivado",
                    arquivado_at = NOW(),
                    arquivado_by = ?
                WHERE id = ? AND tenant_id = ?
            ');
            $stmt->execute([$userContext?->userId, $contratoId, $this->tenantId]);

            $this->registrarHistorico($contratoId, 'arquivado', []);

            $this->pdo->commit();
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new Exception('Erro ao arquivar contrato: ' . $e->getMessage());
        }
    }

    /**
     * Cancelar contrato
     */
    public function cancelarContrato(int $contratoId, string $motivo = null): bool {
        try {
            $this->pdo->beginTransaction();

            $stmt = $this->pdo->prepare('
                UPDATE contratos
                SET status = "cancelado",
                    observacoes = CONCAT(COALESCE(observacoes, ""), "\n\nCancelado: ", ?)
                WHERE id = ? AND tenant_id = ?
            ');
            $stmt->execute([$motivo ?? 'Cancelado pelo usuário', $contratoId, $this->tenantId]);

            $this->registrarHistorico($contratoId, 'cancelado', ['motivo' => $motivo]);

            $this->pdo->commit();
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new Exception('Erro ao cancelar contrato: ' . $e->getMessage());
        }
    }

    /**
     * Renova um contrato criando uma nova versão
     */
    public function renovarContrato(int $contratoId, array $opcoes = []): int {
        try {
            $this->pdo->beginTransaction();

            // Buscar contrato original
            $stmt = $this->pdo->prepare('SELECT * FROM contratos WHERE id = ? AND tenant_id = ?');
            $stmt->execute([$contratoId, $this->tenantId]);
            $contratoOriginal = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$contratoOriginal) {
                throw new Exception('Contrato original não encontrado');
            }

            if ($contratoOriginal['status'] !== 'assinado') {
                throw new Exception('Apenas contratos assinados podem ser renovados');
            }

            // Preparar dados do novo contrato
            $dadosNovo = [
                'titulo' => $contratoOriginal['titulo'] . ' - Renovação',
                'tipo' => $contratoOriginal['tipo'],
                'modelo_id' => $contratoOriginal['modelo_id'],
                'cliente_id' => $contratoOriginal['cliente_id'],
                'fornecedor_id' => $contratoOriginal['fornecedor_id'],
                'venda_id' => $contratoOriginal['venda_id'],
                'conteudo_html' => $opcoes['manter_termos'] ?? true 
                    ? $contratoOriginal['conteudo_html'] 
                    : $contratoOriginal['conteudo_html'],
                'variaveis_preenchidas' => json_decode($contratoOriginal['variaveis_preenchidas_json'] ?? '{}', true),
                'valor_total' => $contratoOriginal['valor_total'],
                'valor_parcela' => $contratoOriginal['valor_parcela'],
                'numero_parcelas' => $contratoOriginal['numero_parcelas'],
                'data_inicio' => $opcoes['data_inicio'] ?? date('Y-m-d'),
                'data_fim' => $opcoes['data_fim'] ?? null,
                'status' => 'rascunho'
            ];

            // Criar novo contrato
            $novoContratoId = $this->criarContrato($dadosNovo);

            // Registrar renovação
            $userContext = \Gestoreasy\Core\Auth\UserContext::getCurrent();
            $stmt = $this->pdo->prepare('
                INSERT INTO contratos_renovacoes (
                    contrato_original_id, contrato_novo_id, data_renovacao,
                    motivo, manter_termos, status, created_by
                ) VALUES (?, ?, CURDATE(), ?, ?, "aprovado", ?)
            ');
            $stmt->execute([
                $contratoId,
                $novoContratoId,
                $opcoes['motivo'] ?? 'Renovação automática',
                $opcoes['manter_termos'] ?? true ? 1 : 0,
                $userContext?->userId
            ]);

            // Registrar no histórico
            $this->registrarHistorico($contratoId, 'renovado', [
                'novo_contrato_id' => $novoContratoId
            ]);

            $this->pdo->commit();
            return $novoContratoId;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new Exception('Erro ao renovar contrato: ' . $e->getMessage());
        }
    }
}

