<?php
namespace Modules\Financeiro\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Audit\AuditLogger;
use PDO;
use Exception;

class ContasReceberController {
    private PDO $db;
    

    public function __construct() {
        $this->db = ConnectionManager::forTenant();
    }

    public function list(Request $request): Response {
        try {
            $page = max(1, (int)$request->getQueryParam('page', 1));
            $perPage = min(100, max(10, (int)$request->getQueryParam('per_page', 20)));
            $status = $request->getQueryParam('status', '');
            $search = $request->getQueryParam('q', '');
            $start = (string)$request->getQueryParam('start', '');
            $end = (string)$request->getQueryParam('end', '');
            
            $offset = ($page - 1) * $perPage;
            
            // Base query
            $whereConditions = ['1=1'];
            $params = [];
            
            if ($status && in_array($status, ['pendente', 'recebido', 'vencido'])) {
                $whereConditions[] = 'status = :status';
                $params['status'] = $status;
            }
            
            if ($search) {
                $whereConditions[] = '(descricao LIKE :search OR cliente LIKE :search OR documento LIKE :search)';
                $params['search'] = '%' . $search . '%';
            }

            if ($start) {
                $whereConditions[] = 'data_vencimento >= :start';
                $params['start'] = $start;
            }
            if ($end) {
                $whereConditions[] = 'data_vencimento <= :end';
                $params['end'] = $end;
            }
            
            $whereClause = implode(' AND ', $whereConditions);
            
            // Check if table exists
            $tableExists = $this->db->query("SHOW TABLES LIKE 'contas_receber'")->rowCount() > 0;
            
            if (!$tableExists) {
                return Response::json([
                    'data' => [],
                    'pagination' => [
                        'page' => $page,
                        'per_page' => $perPage,
                        'total' => 0,
                        'total_pages' => 0
                    ],
                    'message' => 'Tabela contas_receber não encontrada. Execute as migrações.'
                ]);
            }
            
            // Count total
            $countQuery = "SELECT COUNT(*) FROM contas_receber WHERE $whereClause";
            $countStmt = $this->db->prepare($countQuery);
            $countStmt->execute($params);
            $total = $countStmt->fetchColumn();

            // Totals aggregated
            $sumQuery = "SELECT COALESCE(SUM(valor),0) AS total_valor, COALESCE(SUM(valor_recebido),0) AS total_recebido FROM contas_receber WHERE $whereClause";
            $sumStmt = $this->db->prepare($sumQuery);
            foreach ($params as $key => $value) { $sumStmt->bindValue(":$key", $value); }
            $sumStmt->execute();
            $totalsRow = $sumStmt->fetch(PDO::FETCH_ASSOC) ?: ['total_valor' => 0, 'total_recebido' => 0];

            // Totals by status
            $byStatusQuery = "SELECT status, COALESCE(SUM(valor),0) AS s_valor, COALESCE(SUM(valor_recebido),0) AS s_recebido FROM contas_receber WHERE $whereClause GROUP BY status";
            $byStatusStmt = $this->db->prepare($byStatusQuery);
            foreach ($params as $key => $value) { $byStatusStmt->bindValue(":$key", $value); }
            $byStatusStmt->execute();
            $rowsByStatus = $byStatusStmt->fetchAll(PDO::FETCH_ASSOC);
            $totalsByStatus = [
                'pendente' => ['valor' => 0.0, 'valor_recebido' => 0.0],
                'vencido' => ['valor' => 0.0, 'valor_recebido' => 0.0],
                'recebido' => ['valor' => 0.0, 'valor_recebido' => 0.0]
            ];
            foreach ($rowsByStatus as $r) {
                $st = $r['status'];
                if (isset($totalsByStatus[$st])) {
                    $totalsByStatus[$st]['valor'] = (float)$r['s_valor'];
                    $totalsByStatus[$st]['valor_recebido'] = (float)$r['s_recebido'];
                }
            }
            
            // Get data
            $query = "SELECT * FROM contas_receber WHERE $whereClause ORDER BY data_vencimento ASC LIMIT :limit OFFSET :offset";
            $stmt = $this->db->prepare($query);
            
            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();
            $contas = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            // Log audit (read)
            AuditLogger::logRead('contas_receber', null, [
                'filters' => compact('status', 'search'),
                'pagination' => compact('page', 'perPage')
            ]);
            
            return Response::json([
                'data' => $contas,
                'pagination' => [
                    'page' => $page,
                    'per_page' => $perPage,
                    'total' => (int)$total,
                    'total_pages' => ceil($total / $perPage)
                ],
                'totals' => [
                    'valor' => (float)$totalsRow['total_valor'],
                    'valor_recebido' => (float)$totalsRow['total_recebido']
                ],
                'totals_by_status' => $totalsByStatus
            ]);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao listar contas a receber: ' . $e->getMessage()], 500);
        }
    }

    public function getById(Request $request): Response {
        try {
        $params = $request->get('params', []);
        $id = isset($params[0]) ? $params[0] : null;
            
            $stmt = $this->db->prepare("SELECT * FROM contas_receber WHERE id = :id");
            $stmt->execute(['id' => $id]);
            $conta = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$conta) {
                return Response::json(['error' => 'Conta não encontrada'], 404);
            }
            
            return Response::json(['data' => $conta]);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao buscar conta: ' . $e->getMessage()], 500);
        }
    }

    public function create(Request $request): Response {
        try {
            $data = $request->getJsonBody();
            
            // Validação básica
            $required = ['descricao', 'valor', 'data_vencimento', 'cliente'];
            foreach ($required as $field) {
                if (empty($data[$field])) {
                    return Response::json(['error' => "Campo '$field' é obrigatório"], 400);
                }
            }
            
            $stmt = $this->db->prepare("
                INSERT INTO contas_receber (descricao, valor, data_vencimento, cliente, documento, observacoes, status, created_at)
                VALUES (:descricao, :valor, :data_vencimento, :cliente, :documento, :observacoes, 'pendente', NOW())
            ");
            
            $stmt->execute([
                'descricao' => $data['descricao'],
                'valor' => $data['valor'],
                'data_vencimento' => $data['data_vencimento'],
                'cliente' => $data['cliente'],
                'documento' => $data['documento'] ?? null,
                'observacoes' => $data['observacoes'] ?? null
            ]);
            
            $id = $this->db->lastInsertId();
            
            AuditLogger::logCreate('contas_receber', (int)$id, $data);
            
            return Response::json(['message' => 'Conta criada com sucesso', 'id' => $id], 201);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao criar conta: ' . $e->getMessage()], 500);
        }
    }

    public function update(Request $request): Response {
        try {
        $params = $request->get('params', []);
        $id = isset($params[0]) ? $params[0] : null;
            $data = $request->getJsonBody();
            
            // Check if exists
            $stmt = $this->db->prepare("SELECT * FROM contas_receber WHERE id = :id");
            $stmt->execute(['id' => $id]);
            $existing = $stmt->fetch(PDO::FETCH_ASSOC);
            if (!$existing) {
                return Response::json(['error' => 'Conta não encontrada'], 404);
            }
            
            $stmt = $this->db->prepare("
                UPDATE contas_receber 
                SET descricao = :descricao, valor = :valor, data_vencimento = :data_vencimento, 
                    cliente = :cliente, documento = :documento, observacoes = :observacoes, updated_at = NOW()
                WHERE id = :id
            ");
            
            $stmt->execute([
                'id' => $id,
                'descricao' => $data['descricao'],
                'valor' => $data['valor'],
                'data_vencimento' => $data['data_vencimento'],
                'cliente' => $data['cliente'],
                'documento' => $data['documento'] ?? null,
                'observacoes' => $data['observacoes'] ?? null
            ]);
            
            AuditLogger::logUpdate('contas_receber', (int)$id, $existing, $data);
            
            return Response::json(['message' => 'Conta atualizada com sucesso']);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao atualizar conta: ' . $e->getMessage()], 500);
        }
    }

    public function delete(Request $request): Response {
        try {
        $params = $request->get('params', []);
        $id = isset($params[0]) ? $params[0] : null;
            
            // Check if exists
            $stmt = $this->db->prepare("SELECT * FROM contas_receber WHERE id = :id");
            $stmt->execute(['id' => $id]);
            $conta = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$conta) {
                return Response::json(['error' => 'Conta não encontrada'], 404);
            }
            
            if ($conta['status'] === 'recebido') {
                return Response::json(['error' => 'Não é possível excluir conta já recebida'], 400);
            }
            
            $stmt = $this->db->prepare("DELETE FROM contas_receber WHERE id = :id");
            $stmt->execute(['id' => $id]);
            
            AuditLogger::logDelete('contas_receber', (int)$id, $conta);
            
            return Response::json(['message' => 'Conta excluída com sucesso']);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao excluir conta: ' . $e->getMessage()], 500);
        }
    }

    public function receive(Request $request): Response {
        try {
        $params = $request->get('params', []);
        $id = isset($params[0]) ? $params[0] : null;
            $data = $request->getJsonBody();
            
            // Check if exists
            $stmt = $this->db->prepare("SELECT * FROM contas_receber WHERE id = :id");
            $stmt->execute(['id' => $id]);
            $conta = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$conta) {
                return Response::json(['error' => 'Conta não encontrada'], 404);
            }
            
            if ($conta['status'] === 'recebido') {
                return Response::json(['error' => 'Conta já está recebida'], 400);
            }
            
            $stmt = $this->db->prepare("
                UPDATE contas_receber 
                SET status = 'recebido', data_recebimento = :data_recebimento, valor_recebido = :valor_recebido, 
                    forma_recebimento = :forma_recebimento, observacoes_recebimento = :observacoes, updated_at = NOW()
                WHERE id = :id
            ");
            
            $stmt->execute([
                'id' => $id,
                'data_recebimento' => $data['data_recebimento'] ?? date('Y-m-d'),
                'valor_recebido' => $data['valor_recebido'] ?? $conta['valor'],
                'forma_recebimento' => $data['forma_recebimento'] ?? 'dinheiro',
                'observacoes' => $data['observacoes'] ?? null
            ]);

            // Se uma conta bancária foi informada, registrar entrada em Bancos
            $contaBancariaId = isset($data['conta_bancaria_id']) ? (int)$data['conta_bancaria_id'] : 0;
            if ($contaBancariaId <= 0) {
                try {
                    $q = $this->db->query("SELECT id FROM bank_accounts WHERE status = 'ativa' ORDER BY id ASC LIMIT 1");
                    $fallbackAcc = $q->fetch(PDO::FETCH_ASSOC);
                    if ($fallbackAcc && isset($fallbackAcc['id'])) $contaBancariaId = (int)$fallbackAcc['id'];
                } catch (\Throwable $e) { /* ignore */ }
            }
            $dataReceb = $data['data_recebimento'] ?? date('Y-m-d');
            $valorReceb = isset($data['valor_recebido']) ? (float)$data['valor_recebido'] : (float)$conta['valor'];
            if ($contaBancariaId > 0 && $valorReceb > 0) {
                try {
                    // Buscar saldo atual
                    $stAcc = $this->db->prepare('SELECT * FROM bank_accounts WHERE id = ?');
                    $stAcc->execute([$contaBancariaId]);
                    $acc = $stAcc->fetch(PDO::FETCH_ASSOC);
                    if ($acc) {
                        $saldoAtual = (float)($acc['saldo'] ?? 0);
                        $novoSaldo = $saldoAtual + $valorReceb;
                        $descricao = sprintf('Recebimento CR #%d - %s %s', (int)$id, (string)($conta['cliente'] ?? ''), (string)($conta['documento'] ?? $conta['descricao'] ?? ''));
                        // Inserir movimentação com origem
                        $insMov = $this->db->prepare('INSERT INTO bank_transactions (conta_id, data, descricao, tipo, valor, saldo, source_type, source_id, created_at) VALUES (?,?,?,?,?,?,?,?, NOW())');
                        $insMov->execute([$contaBancariaId, $dataReceb, $descricao, 'entrada', $valorReceb, $novoSaldo, 'contas_receber', (int)$id]);
                        // Atualizar saldo
                        $updAcc = $this->db->prepare('UPDATE bank_accounts SET saldo = ?, updated_at = NOW() WHERE id = ?');
                        $updAcc->execute([$novoSaldo, $contaBancariaId]);
                        // Auditoria
                        AuditLogger::logCreate('bank_transactions', (int)$this->db->lastInsertId(), [
                            'conta_id' => $contaBancariaId,
                            'data' => $dataReceb,
                            'descricao' => $descricao,
                            'tipo' => 'entrada',
                            'valor' => $valorReceb,
                            'saldo' => $novoSaldo,
                            'source_type' => 'contas_receber',
                            'source_id' => (int)$id
                        ], ['hook' => 'contas_receber.receive']);
                    }
                } catch (\Throwable $e) {
                    AuditLogger::log('error', 'bank_transactions', (int)$id, null, ['message' => 'Falha ao registrar entrada bancária', 'error' => $e->getMessage()]);
                }
            }

            AuditLogger::log('receive', 'contas_receber', (int)$id, $conta, $data);
            
            return Response::json(['message' => 'Conta recebida com sucesso']);
            
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao receber conta: ' . $e->getMessage()], 500);
        }
    }

    public function exportCsv(Request $request): Response {
        try {
            $sep = (string)$request->getQueryParam('sep', ';');
            if (!in_array($sep, [';', ',', '\t'], true)) { $sep = ';'; }
            if ($sep === '\t') { $sep = "\t"; }

            $status = (string)$request->getQueryParam('status', '');
            $search = (string)$request->getQueryParam('q', '');
            $start = (string)$request->getQueryParam('start', '');
            $end = (string)$request->getQueryParam('end', '');

            $where = ['1=1'];
            $params = [];
            if ($status && in_array($status, ['pendente', 'recebido', 'vencido'], true)) {
                $where[] = 'status = :status';
                $params['status'] = $status;
            }
            if ($search) {
                $where[] = '(descricao LIKE :search OR cliente LIKE :search OR documento LIKE :search)';
                $params['search'] = '%' . $search . '%';
            }
            if ($start) { $where[] = 'data_vencimento >= :start'; $params['start'] = $start; }
            if ($end) { $where[] = 'data_vencimento <= :end'; $params['end'] = $end; }
            $whereClause = implode(' AND ', $where);

            $tableExists = $this->db->query("SHOW TABLES LIKE 'contas_receber'\n")->rowCount() > 0;
            if (!$tableExists) {
                return new Response(404, 'Tabela contas_receber não encontrada');
            }

            $sql = "SELECT id, descricao, cliente, valor, data_vencimento, status, documento, data_recebimento, valor_recebido, forma_recebimento, observacoes, created_at, updated_at FROM contas_receber WHERE $whereClause ORDER BY data_vencimento ASC";
            $stmt = $this->db->prepare($sql);
            foreach ($params as $k => $v) { $stmt->bindValue(":$k", $v); }
            $stmt->execute();
            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

            $bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
            $out = fopen('php://temp', 'r+');
            fputs($out, $bom);
            $headers = [
                'ID','Descrição','Cliente','Valor','Vencimento','Status','Documento','Data Recebimento','Valor Recebido','Forma Recebimento','Observações','Criado em','Atualizado em'
            ];
            fputcsv($out, $headers, $sep);
            $fmtDate = function (?string $s): ?string {
                if (!$s) { return null; }
                $t = strtotime($s);
                return $t ? date('d-m-Y', $t) : $s;
            };
            $fmtDateTime = function (?string $s): ?string {
                if (!$s) { return null; }
                $t = strtotime($s);
                return $t ? date('d-m-Y H:i:s', $t) : $s;
            };
            $fmtMoney = function ($n): ?string {
                if ($n === null || $n === '') { return null; }
                $f = is_numeric($n) ? (float)$n : (float)str_replace(',', '.', (string)$n);
                return number_format($f, 2, ',', '');
            };
            foreach ($rows as $r) {
                fputcsv($out, [
                    $r['id'] ?? null,
                    $r['descricao'] ?? null,
                    $r['cliente'] ?? null,
                    isset($r['valor']) ? $fmtMoney($r['valor']) : null,
                    isset($r['data_vencimento']) ? $fmtDate($r['data_vencimento']) : null,
                    $r['status'] ?? null,
                    $r['documento'] ?? null,
                    isset($r['data_recebimento']) ? $fmtDate($r['data_recebimento']) : null,
                    isset($r['valor_recebido']) ? $fmtMoney($r['valor_recebido']) : null,
                    $r['forma_recebimento'] ?? null,
                    $r['observacoes'] ?? null,
                    isset($r['created_at']) ? $fmtDateTime($r['created_at']) : null,
                    isset($r['updated_at']) ? $fmtDateTime($r['updated_at']) : null
                ], $sep);
            }
            rewind($out);
            $csv = stream_get_contents($out);
            fclose($out);
            return new Response(200, $csv, [
                'Content-Type' => 'text/csv; charset=utf-8',
                'Content-Disposition' => 'attachment; filename="contas_receber.csv"'
            ]);
        } catch (Exception $e) {
            return Response::json(['error' => 'Erro ao exportar CSV: ' . $e->getMessage()], 500);
        }
    }
}