<?php

namespace Modules\APIPublica\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Modules\APIPublica\Services\RateLimitService;
use PDO;

class APIController {
    public function listProducts(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            
            $page = max(1, (int)($req->query['page'] ?? 1));
            $per = min(100, max(1, (int)($req->query['per_page'] ?? 20)));
            $offset = ($page - 1) * $per;

            $sql = "SELECT id, codigo, nome, descricao, preco, estoque, created_at
                    FROM products
                    ORDER BY id DESC
                    LIMIT ? OFFSET ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$per, $offset]);
            $produtos = $stmt->fetchAll(PDO::FETCH_ASSOC);

            // Total
            $total = (int)$pdo->query('SELECT COUNT(*) FROM products')->fetchColumn();

            return Response::json([
                'data' => $produtos,
                'pagination' => [
                    'page' => $page,
                    'per_page' => $per,
                    'total' => $total,
                    'total_pages' => (int)ceil($total / $per)
                ]
            ]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function getProduct(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);

            $stmt = $pdo->prepare('SELECT * FROM products WHERE id = ?');
            $stmt->execute([$id]);
            $produto = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$produto) {
                return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Produto não encontrado']]);
            }

            return Response::json(['data' => $produto]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function createProduct(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $body = is_array($req->body) ? $req->body : [];

            $stmt = $pdo->prepare('
                INSERT INTO products (codigo, nome, descricao, preco, estoque)
                VALUES (?, ?, ?, ?, ?)
            ');
            $stmt->execute([
                $body['codigo'] ?? '',
                $body['nome'] ?? '',
                $body['descricao'] ?? null,
                $body['preco'] ?? 0,
                $body['estoque'] ?? 0
            ]);

            $productId = (int)$pdo->lastInsertId();

            // Disparar webhook
            $this->dispararWebhook('product.created', ['product_id' => $productId]);

            return Response::json(['data' => ['id' => $productId]], 201);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function updateProduct(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);
            $body = is_array($req->body) ? $req->body : [];

            $allowedFields = ['nome', 'descricao', 'preco', 'estoque'];
            $updates = [];
            $params = [];

            foreach ($allowedFields as $field) {
                if (isset($body[$field])) {
                    $updates[] = "{$field} = ?";
                    $params[] = $body[$field];
                }
            }

            if (empty($updates)) {
                return new Response(422, ['error' => ['code' => 'validation_error', 'message' => 'Nenhum campo para atualizar']]);
            }

            $params[] = $id;
            $sql = 'UPDATE products SET ' . implode(', ', $updates) . ' WHERE id = ?';
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);

            // Disparar webhook
            $this->dispararWebhook('product.updated', ['product_id' => $id]);

            return Response::json(['data' => ['success' => true]]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function deleteProduct(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);

            $stmt = $pdo->prepare('DELETE FROM products WHERE id = ?');
            $stmt->execute([$id]);

            if ($stmt->rowCount() === 0) {
                return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Produto não encontrado']]);
            }

            // Disparar webhook
            $this->dispararWebhook('product.deleted', ['product_id' => $id]);

            return Response::json(['data' => ['success' => true]]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function listCustomers(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            
            $page = max(1, (int)($req->query['page'] ?? 1));
            $per = min(100, max(1, (int)($req->query['per_page'] ?? 20)));
            $offset = ($page - 1) * $per;

            $sql = "SELECT id, nome, email, telefone, cpf_cnpj, created_at
                    FROM clients
                    ORDER BY id DESC
                    LIMIT ? OFFSET ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$per, $offset]);
            $clientes = $stmt->fetchAll(PDO::FETCH_ASSOC);

            $total = (int)$pdo->query('SELECT COUNT(*) FROM clients')->fetchColumn();

            return Response::json([
                'data' => $clientes,
                'pagination' => [
                    'page' => $page,
                    'per_page' => $per,
                    'total' => $total,
                    'total_pages' => (int)ceil($total / $per)
                ]
            ]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function getCustomer(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);

            $stmt = $pdo->prepare('SELECT * FROM clients WHERE id = ?');
            $stmt->execute([$id]);
            $cliente = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$cliente) {
                return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Cliente não encontrado']]);
            }

            return Response::json(['data' => $cliente]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function createCustomer(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $body = is_array($req->body) ? $req->body : [];

            $stmt = $pdo->prepare('
                INSERT INTO clients (nome, email, telefone, cpf_cnpj)
                VALUES (?, ?, ?, ?)
            ');
            $stmt->execute([
                $body['nome'] ?? '',
                $body['email'] ?? null,
                $body['telefone'] ?? null,
                $body['cpf_cnpj'] ?? null
            ]);

            $clienteId = (int)$pdo->lastInsertId();

            // Disparar webhook
            $this->dispararWebhook('customer.created', ['customer_id' => $clienteId]);

            return Response::json(['data' => ['id' => $clienteId]], 201);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function updateCustomer(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);
            $body = is_array($req->body) ? $req->body : [];

            $allowedFields = ['nome', 'email', 'telefone', 'cpf_cnpj'];
            $updates = [];
            $params = [];

            foreach ($allowedFields as $field) {
                if (isset($body[$field])) {
                    $updates[] = "{$field} = ?";
                    $params[] = $body[$field];
                }
            }

            if (empty($updates)) {
                return new Response(422, ['error' => ['code' => 'validation_error', 'message' => 'Nenhum campo para atualizar']]);
            }

            $params[] = $id;
            $sql = 'UPDATE clients SET ' . implode(', ', $updates) . ' WHERE id = ?';
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);

            // Disparar webhook
            $this->dispararWebhook('customer.updated', ['customer_id' => $id]);

            return Response::json(['data' => ['success' => true]]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function listOrders(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            
            $page = max(1, (int)($req->query['page'] ?? 1));
            $per = min(100, max(1, (int)($req->query['per_page'] ?? 20)));
            $offset = ($page - 1) * $per;

            $sql = "SELECT id, numero, cliente_id, total, status, created_at
                    FROM sales_orders
                    ORDER BY id DESC
                    LIMIT ? OFFSET ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$per, $offset]);
            $pedidos = $stmt->fetchAll(PDO::FETCH_ASSOC);

            $total = (int)$pdo->query('SELECT COUNT(*) FROM sales_orders')->fetchColumn();

            return Response::json([
                'data' => $pedidos,
                'pagination' => [
                    'page' => $page,
                    'per_page' => $per,
                    'total' => $total,
                    'total_pages' => (int)ceil($total / $per)
                ]
            ]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function getOrder(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $id = (int)($req->get('params')[0] ?? 0);

            $stmt = $pdo->prepare('SELECT * FROM sales_orders WHERE id = ?');
            $stmt->execute([$id]);
            $pedido = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$pedido) {
                return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
            }

            return Response::json(['data' => $pedido]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function createOrder(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $data = is_array($req->body) ? $req->body : [];

            // Validações
            $clienteId = (int)($data['cliente_id'] ?? 0);
            $items = is_array($data['items'] ?? null) ? $data['items'] : [];
            $observacoes = $data['observacoes'] ?? '';

            if ($clienteId <= 0) {
                return new Response(422, ['error' => ['code' => 'cliente_id_required', 'message' => 'cliente_id é obrigatório']]);
            }

            // Validar cliente
            $cst = $pdo->prepare('SELECT id FROM clients WHERE id = ? LIMIT 1');
            $cst->execute([$clienteId]);
            if (!$cst->fetch()) {
                return new Response(404, ['error' => ['code' => 'client_not_found', 'message' => 'Cliente não encontrado']]);
            }

            // Validar e calcular itens
            $valorTotal = 0.0;
            $summary = [];
            
            if (count($items) === 0) {
                return new Response(422, ['error' => ['code' => 'items_required', 'message' => 'Pelo menos um item é obrigatório']]);
            }

            foreach ($items as $it) {
                $pid = (int)($it['product_id'] ?? 0);
                $qtd = (float)($it['quantidade'] ?? 0);
                
                if ($pid <= 0 || $qtd <= 0) {
                    return new Response(422, ['error' => ['code' => 'invalid_item', 'message' => 'Item inválido: product_id e quantidade > 0 requeridos']]);
                }

                // Buscar produto
                $st = $pdo->prepare('SELECT id, codigo, preco, estoque FROM products WHERE id = ? LIMIT 1');
                $st->execute([$pid]);
                $prod = $st->fetch(PDO::FETCH_ASSOC);
                
                if (!$prod) {
                    return new Response(404, ['error' => ['code' => 'product_not_found', 'message' => 'Produto não encontrado', 'product_id' => $pid]]);
                }

                // Validar estoque (opcional, pode ser desabilitado)
                $estoqueAtual = (float)($prod['estoque'] ?? 0);
                if ($estoqueAtual < $qtd) {
                    return new Response(422, [
                        'error' => [
                            'code' => 'insufficient_stock',
                            'message' => 'Estoque insuficiente',
                            'product_id' => $pid,
                            'available' => $estoqueAtual,
                            'required' => $qtd
                        ]
                    ]);
                }

                // Usar preço do produto ou preço informado
                $preco = isset($it['preco']) ? (float)$it['preco'] : (float)$prod['preco'];
                $linhaTotal = $qtd * $preco;
                $valorTotal += $linhaTotal;
                
                $summary[] = [
                    'product_id' => $pid,
                    'quantidade' => $qtd,
                    'preco' => $preco,
                    'total' => $linhaTotal
                ];
            }

            // Gerar número do pedido
            $numero = null;
            try {
                $stmtSeq = $pdo->query("SELECT MAX(CAST(SUBSTRING(numero, 4) AS UNSIGNED)) AS max_seq FROM sales_orders WHERE numero LIKE 'PD-%'");
                $rowSeq = $stmtSeq->fetch();
                $next = ((int)($rowSeq['max_seq'] ?? 0)) + 1;
                $numero = 'PD-' . str_pad((string)$next, 10, '0', STR_PAD_LEFT);
            } catch (\Throwable $e) {
                $numero = null;
            }

            // Criar pedido
            $pdo->beginTransaction();
            
            try {
                if ($numero !== null) {
                    $ins = $pdo->prepare('INSERT INTO sales_orders (numero, cliente_id, valor_total, status, observacoes) VALUES (?,?,?,?,?)');
                    $ins->execute([$numero, $clienteId, $valorTotal, 'aberto', $observacoes]);
                    $newId = (int)$pdo->lastInsertId();
                } else {
                    // Fallback
                    $ins = $pdo->prepare('INSERT INTO sales_orders (numero, cliente_id, valor_total, status, observacoes) VALUES (?,?,?,?,?)');
                    $ins->execute(['', $clienteId, $valorTotal, 'aberto', $observacoes]);
                    $newId = (int)$pdo->lastInsertId();
                    $numero = 'PD-' . str_pad((string)$newId, 10, '0', STR_PAD_LEFT);
                    $pdo->prepare('UPDATE sales_orders SET numero = ? WHERE id = ?')->execute([$numero, $newId]);
                }

                // Criar itens
                if (count($summary) > 0) {
                    $itIns = $pdo->prepare('INSERT INTO sales_order_items (order_id, product_id, quantidade, preco, total) VALUES (?,?,?,?,?)');
                    foreach ($summary as $s) {
                        $itIns->execute([
                            $newId,
                            (int)$s['product_id'],
                            (float)$s['quantidade'],
                            (float)$s['preco'],
                            (float)$s['total']
                        ]);
                    }
                }

                $pdo->commit();

                // Log de auditoria
                try {
                    \Gestoreasy\Core\Audit\AuditLogger::logCreate(
                        'sales_orders',
                        $newId,
                        ['numero' => $numero, 'cliente_id' => $clienteId, 'valor_total' => $valorTotal, 'status' => 'aberto'],
                        ['endpoint' => 'api.v1.orders.create', 'api_key_id' => $this->getApiKeyId($req)]
                    );
                } catch (\Exception $e) {
                    // Ignorar erro de log
                }

                return Response::json([
                    'data' => [
                        'id' => $newId,
                        'numero' => $numero,
                        'cliente_id' => $clienteId,
                        'valor_total' => $valorTotal,
                        'status' => 'aberto',
                        'items' => $summary
                    ]
                ], 201);

            } catch (\Exception $e) {
                $pdo->rollBack();
                throw $e;
            }

        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    /**
     * Obtém ID da API key do request
     */
    private function getApiKeyId(Request $req): ?int {
        $apiKey = $req->headers['X-API-Key'] ?? $req->headers['Authorization'] ?? null;
        if (!$apiKey) {
            return null;
        }

        // Remover "Bearer " se presente
        $apiKey = str_replace('Bearer ', '', $apiKey);

        try {
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $stmt = $pdo->prepare('SELECT id FROM api_keys WHERE api_key = ? AND tenant_id = ? AND ativo = 1 LIMIT 1');
            $stmt->execute([$apiKey, $tenantId]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            return $row ? (int)$row['id'] : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    public function getInventory(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();

            $sql = "SELECT product_id, estoque as quantidade
                    FROM products
                    WHERE estoque > 0";
            $stmt = $pdo->query($sql);
            $estoque = $stmt->fetchAll(PDO::FETCH_ASSOC);

            return Response::json(['data' => $estoque]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    public function getProductInventory(Request $req): Response {
        try {
            $this->verificarRateLimit($req);
            
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $pdo = ConnectionManager::forTenant();
            $productId = (int)($req->get('params')[0] ?? 0);

            $stmt = $pdo->prepare('SELECT id, codigo, nome, estoque FROM products WHERE id = ?');
            $stmt->execute([$productId]);
            $produto = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$produto) {
                return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Produto não encontrado']]);
            }

            return Response::json(['data' => [
                'product_id' => $produto['id'],
                'codigo' => $produto['codigo'],
                'nome' => $produto['nome'],
                'estoque' => (float)$produto['estoque']
            ]]);
        } catch (\Exception $e) {
            return new Response(500, ['error' => ['code' => 'error', 'message' => $e->getMessage()]]);
        }
    }

    /**
     * Verifica rate limit
     */
    private function verificarRateLimit(Request $req): void {
        $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
        $apiKeyId = $req->get('api_key_id');
        $ip = $req->get('ip_address') ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        
        $identificador = $apiKeyId ? "api_key_{$apiKeyId}" : "ip_{$ip}";
        
        $rateLimitService = new RateLimitService($tenantId);
        $permitido = $rateLimitService->verificar($identificador, $apiKeyId);
        
        if (!$permitido) {
            throw new \Exception('Rate limit excedido', 429);
        }
    }

    /**
     * Dispara webhook
     */
    private function dispararWebhook(string $evento, array $dados): void {
        try {
            $tenantId = \Gestoreasy\Core\Tenant\TenantContext::getTenantId();
            $webhookService = new \Modules\APIPublica\Services\WebhookService($tenantId);
            $webhookService->disparar($evento, $dados);
        } catch (\Exception $e) {
            // Log erro mas não interrompe a requisição
            error_log("Erro ao disparar webhook: " . $e->getMessage());
        }
    }
}

