<?php
namespace Modules\AsaasAddon\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Config\Config;
use Gestoreasy\Core\Security\Crypto;
use Gestoreasy\Core\Audit\AuditLogger;

class AsaasController {
    private function pdo(): \PDO { return ConnectionManager::forTenant(); }

    private function ensureTables(\PDO $pdo): void {
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS asaas_settings (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              environment VARCHAR(20) NOT NULL DEFAULT "production",
              base_url VARCHAR(255) NULL,
              api_key_enc BLOB NULL,
              api_key_iv BLOB NULL,
              api_key_tag BLOB NULL,
              api_key_salt BLOB NULL,
              webhook_secret_enc BLOB NULL,
              webhook_secret_iv BLOB NULL,
              webhook_secret_tag BLOB NULL,
              webhook_secret_salt BLOB NULL,
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
              updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS asaas_logs (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              endpoint VARCHAR(255) NOT NULL,
              method VARCHAR(10) NOT NULL,
              status INT NULL,
              request_payload MEDIUMTEXT NULL,
              response_body MEDIUMTEXT NULL,
              error_message TEXT NULL,
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS asaas_customer_map (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              client_id BIGINT UNSIGNED NOT NULL,
              asaas_customer_id VARCHAR(64) NOT NULL,
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
              UNIQUE KEY uniq_client (client_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS asaas_payment_links (
              id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
              sale_id VARCHAR(64) NOT NULL,
              client_id BIGINT UNSIGNED NOT NULL,
              asaas_payment_id VARCHAR(64) NOT NULL,
              asaas_link_id VARCHAR(64) NULL,
              link_url VARCHAR(512) NULL,
              status VARCHAR(64) NOT NULL DEFAULT "aguardando compensação",
              created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
    }

    private function loadConfig(\PDO $pdo): array {
        $row = $pdo->query('SELECT * FROM asaas_settings ORDER BY id DESC LIMIT 1')->fetch();
        if (!$row) return [];
        $env = $row['environment'] ?? 'production';
        $base = $row['base_url'] ?? null;
        $apiKey = null;
        $webhook = null;
        try {
            if (!empty($row['api_key_enc'])) {
                $dec = Crypto::decryptJson($row['api_key_enc'], $row['api_key_iv'], $row['api_key_tag'], $row['api_key_salt']);
                $apiKey = (string)($dec['api_key'] ?? null);
            }
            if (!empty($row['webhook_secret_enc'])) {
                $dec2 = Crypto::decryptJson($row['webhook_secret_enc'], $row['webhook_secret_iv'], $row['webhook_secret_tag'], $row['webhook_secret_salt']);
                $webhook = (string)($dec2['webhook_secret'] ?? null);
            }
        } catch (\Throwable $e) {}
        return [
            'environment' => $env,
            'base_url' => $base,
            'api_key' => $apiKey,
            'webhook_secret' => $webhook,
        ];
    }

    public function getConfig(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        return Response::json(['config' => [
            'environment' => $cfg['environment'] ?? 'production',
            'base_url' => $cfg['base_url'] ?? null,
            'has_api_key' => !empty($cfg['api_key']),
            'has_webhook_secret' => !empty($cfg['webhook_secret'])
        ]]);
    }

    public function saveConfig(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        $env = (string)($r->body['environment'] ?? 'production');
        $base = (string)($r->body['base_url'] ?? '');
        $apiKey = (string)($r->body['api_key'] ?? '');
        $webhook = (string)($r->body['webhook_secret'] ?? '');
        if (!in_array($env, ['production','sandbox'], true)) {
            return Response::json(['error' => 'environment inválido'], 422);
        }
        $encKey = !empty($apiKey) ? Crypto::encryptJson(['api_key' => $apiKey]) : null;
        $encWebhook = !empty($webhook) ? Crypto::encryptJson(['webhook_secret' => $webhook]) : null;
        $stmt = $pdo->prepare('INSERT INTO asaas_settings (environment, base_url, api_key_enc, api_key_iv, api_key_tag, api_key_salt, webhook_secret_enc, webhook_secret_iv, webhook_secret_tag, webhook_secret_salt) VALUES (?,?,?,?,?,?,?,?,?,?)');
        $stmt->execute([
            $env,
            ($base ?: null),
            $encKey['cipher'] ?? null,
            $encKey['iv'] ?? null,
            $encKey['tag'] ?? null,
            $encKey['salt'] ?? null,
            $encWebhook['cipher'] ?? null,
            $encWebhook['iv'] ?? null,
            $encWebhook['tag'] ?? null,
            $encWebhook['salt'] ?? null,
        ]);
        AuditLogger::log('asaas', 'save_config', ['env' => $env]);
        return Response::json(['ok' => true]);
    }

    private function callAsaas(array $cfg, string $path, string $method = 'GET', ?array $payload = null): array {
        $baseUrl = $cfg['base_url'] ?: 'https://www.asaas.com/api/v3';
        if (($cfg['environment'] ?? 'production') === 'sandbox') {
            $baseUrl = $cfg['base_url'] ?: 'https://sandbox.asaas.com/api/v3';
        }
        $url = rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            // Asaas aceita Authorization: Bearer <key> ou access_token header
            'Authorization: Bearer ' . ($cfg['api_key'] ?? '')
        ];
        // Modo mock para testes
        if (strtolower((string)Config::get('ASAAS_MOCK', 'false')) === 'true') {
            // Respostas simplificadas para endpoints comuns
            if (str_starts_with($path, 'customers')) {
                return ['status' => 200, 'body' => json_encode(['data' => [['id' => 'cus_mock_1','name' => 'Cliente Mock']]])];
            }
            if ($path === 'payments' && $method === 'POST') {
                return ['status' => 201, 'body' => json_encode(['id' => 'pay_mock_1', 'status' => 'PENDING', 'invoiceUrl' => 'https://mock/boletos/123'])];
            }
            if ($path === 'paymentLinks' && $method === 'POST') {
                return ['status' => 201, 'body' => json_encode(['id' => 'plink_mock_1', 'url' => 'https://mock/pay/abc'])];
            }
        }

        $opts = ['http' => [
            'method' => $method,
            'timeout' => 20,
            'ignore_errors' => true,
            'header' => implode("\r\n", $headers)
        ]];
        if ($payload !== null) {
            $opts['http']['content'] = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        $ctx = stream_context_create($opts);
        $res = @file_get_contents($url, false, $ctx);
        // Extrair status do $http_response_header
        $status = null;
        global $http_response_header;
        if (is_array($http_response_header)) {
            foreach ($http_response_header as $h) {
                if (preg_match('/^HTTP\/\d+\.\d+\s+(\d+)/', $h, $m)) { $status = (int)$m[1]; break; }
            }
        }
        return ['status' => $status ?? 0, 'body' => $res === false ? null : $res];
    }

    private function logOp(\PDO $pdo, string $endpoint, string $method, ?int $status, ?array $req, ?string $resp, ?string $err): void {
        try {
            $stmt = $pdo->prepare('INSERT INTO asaas_logs (endpoint, method, status, request_payload, response_body, error_message) VALUES (?,?,?,?,?,?)');
            $stmt->execute([$endpoint, $method, $status, $req ? json_encode($req) : null, $resp, $err]);
        } catch (\Throwable $e) {}
    }

    public function listCustomers(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $q = (string)($r->query['q'] ?? '');
        $page = max(1, (int)($r->query['page'] ?? 1));
        $per = max(1, min(100, (int)($r->query['per_page'] ?? 20)));
        $query = http_build_query(['name' => $q, 'offset' => ($page - 1) * $per, 'limit' => $per]);
        $call = $this->callAsaas($cfg, 'customers?' . $query, 'GET');
        $this->logOp($pdo, 'customers', 'GET', $call['status'], null, $call['body'], null);
        if ($call['status'] >= 200 && $call['status'] < 300) {
            $json = json_decode($call['body'] ?? '[]', true);
            return Response::json(['data' => $json]);
        }
        return Response::json(['error' => 'asaas_error', 'status' => $call['status'], 'response' => $call['body']], 502);
    }

    public function listPayments(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $q = [];
        foreach (['customer','billingType','status'] as $key) { if (!empty($r->query[$key])) $q[$key] = $r->query[$key]; }
        $q['limit'] = max(1, min(100, (int)($r->query['limit'] ?? 20)));
        $query = http_build_query($q);
        $call = $this->callAsaas($cfg, 'payments?' . $query, 'GET');
        $this->logOp($pdo, 'payments', 'GET', $call['status'], null, $call['body'], null);
        if ($call['status'] >= 200 && $call['status'] < 300) {
            $json = json_decode($call['body'] ?? '[]', true);
            return Response::json(['data' => $json]);
        }
        return Response::json(['error' => 'asaas_error', 'status' => $call['status'], 'response' => $call['body']], 502);
    }

    public function createPayment(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $body = [
            'customer' => $r->body['customer'] ?? null,
            'billingType' => $r->body['billingType'] ?? 'BOLETO',
            'value' => $r->body['value'] ?? null,
            'dueDate' => $r->body['dueDate'] ?? null,
            'description' => $r->body['description'] ?? null,
        ];
        foreach (['customer','value','dueDate'] as $req) { if (empty($body[$req])) return Response::json(['error' => "{$req} requerido"], 422); }
        $call = $this->callAsaas($cfg, 'payments', 'POST', $body);
        $this->logOp($pdo, 'payments', 'POST', $call['status'], $body, $call['body'], null);
        if ($call['status'] >= 200 && $call['status'] < 300) {
            $json = json_decode($call['body'] ?? '{}', true);
            AuditLogger::log('asaas', 'create_payment', ['customer' => $body['customer'], 'value' => $body['value']]);
            return Response::json(['data' => $json], 201);
        }
        return Response::json(['error' => 'asaas_error', 'status' => $call['status'], 'response' => $call['body']], 502);
    }

    public function refundPayment(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $params = $r->get('params', []);
        $id = (string)($params[0] ?? '');
        if ($id === '') return Response::json(['error' => 'id requerido'], 422);
        $call = $this->callAsaas($cfg, "payments/{$id}/refund", 'POST', []);
        $this->logOp($pdo, "payments/{$id}/refund", 'POST', $call['status'], [], $call['body'], null);
        if ($call['status'] >= 200 && $call['status'] < 300) {
            AuditLogger::log('asaas', 'refund_payment', ['payment_id' => $id]);
            $json = json_decode($call['body'] ?? '{}', true);
            return Response::json(['data' => $json]);
        }
        return Response::json(['error' => 'asaas_error', 'status' => $call['status'], 'response' => $call['body']], 502);
    }

    private function ensureAsaasCustomer(\PDO $pdo, array $cfg, int $clientId): ?string {
        // Tenta mapear cliente local -> Asaas
        try {
            $chk = $pdo->prepare('SELECT asaas_customer_id FROM asaas_customer_map WHERE client_id = ? LIMIT 1');
            $chk->execute([$clientId]);
            $id = (string)($chk->fetch()['asaas_customer_id'] ?? '');
            if ($id !== '') return $id;
        } catch (\Throwable $e) {}
        // Buscar dados do cliente local
        $row = null;
        try {
            $q = $pdo->prepare('SELECT id, nome, cpf, cnpj FROM clients WHERE id = ? LIMIT 1');
            $q->execute([$clientId]);
            $row = $q->fetch();
        } catch (\Throwable $e) {}
        if (!$row) return null;
        $name = (string)($row['nome'] ?? 'Cliente');
        $cpfCnpj = (string)($row['cnpj'] ?? ($row['cpf'] ?? ''));
        $payload = ['name' => $name];
        if ($cpfCnpj !== '') $payload['cpfCnpj'] = preg_replace('/\D+/', '', $cpfCnpj);
        $call = $this->callAsaas($cfg, 'customers', 'POST', $payload);
        $this->logOp($pdo, 'customers', 'POST', $call['status'], $payload, $call['body'], null);
        if ($call['status'] >= 200 && $call['status'] < 300) {
            $json = json_decode($call['body'] ?? '{}', true);
            $asaasId = (string)($json['id'] ?? '');
            if ($asaasId !== '') {
                try {
                    $ins = $pdo->prepare('INSERT INTO asaas_customer_map (client_id, asaas_customer_id) VALUES (?, ?)');
                    $ins->execute([$clientId, $asaasId]);
                } catch (\Throwable $e) {}
                return $asaasId;
            }
        }
        return null;
    }

    public function generatePaymentLink(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $saleId = (string)($r->body['sale_id'] ?? '');
        $clientId = (int)($r->body['client_id'] ?? 0);
        $amountCents = (int)($r->body['amount_cents'] ?? 0);
        $dueDate = (string)($r->body['due_date'] ?? '');
        $billingType = (string)($r->body['billing_type'] ?? 'BOLETO');
        if ($saleId === '' || $clientId <= 0 || $amountCents <= 0 || $dueDate === '') {
            return Response::json(['error' => 'sale_id, client_id, amount_cents, due_date requeridos'], 422);
        }
        $value = round($amountCents / 100, 2);
        // Garantir cliente no Asaas
        $asaasCustomer = $this->ensureAsaasCustomer($pdo, $cfg, $clientId);
        if (!$asaasCustomer) return Response::json(['error' => 'Falha ao criar/obter cliente Asaas'], 502);
        // Criar cobrança (payment) imediatamente
        $payReq = [
            'customer' => $asaasCustomer,
            'billingType' => $billingType,
            'value' => $value,
            'dueDate' => $dueDate,
            'description' => 'Pedido ' . $saleId,
            'externalReference' => $saleId
        ];
        $payCall = $this->callAsaas($cfg, 'payments', 'POST', $payReq);
        $this->logOp($pdo, 'payments', 'POST', $payCall['status'], $payReq, $payCall['body'], null);
        if ($payCall['status'] < 200 || $payCall['status'] >= 300) {
            return Response::json(['error' => 'asaas_error', 'stage' => 'create_payment', 'status' => $payCall['status'], 'response' => $payCall['body']], 502);
        }
        $payJson = json_decode($payCall['body'] ?? '{}', true);
        $paymentId = (string)($payJson['id'] ?? '');
        if ($paymentId === '') return Response::json(['error' => 'asaas_sem_id'], 502);

        // Gerar link de pagamento (paymentLinks). Se falhar, usar URL do boleto/nota.
        $linkReq = [
            'name' => 'Pedido ' . $saleId,
            'description' => 'Pagamento do pedido ' . $saleId,
            'billingType' => $billingType,
            'value' => $value,
            'dueDate' => $dueDate,
            'externalReference' => $saleId
        ];
        $plinkCall = $this->callAsaas($cfg, 'paymentLinks', 'POST', $linkReq);
        $this->logOp($pdo, 'paymentLinks', 'POST', $plinkCall['status'], $linkReq, $plinkCall['body'], null);
        $plinkJson = null; $plinkId = null; $linkUrl = null;
        if ($plinkCall['status'] >= 200 && $plinkCall['status'] < 300) {
            $plinkJson = json_decode($plinkCall['body'] ?? '{}', true);
            $plinkId = (string)($plinkJson['id'] ?? '');
            $linkUrl = (string)($plinkJson['url'] ?? '');
        }
        // Fallback para URL do payment (boleto/invoice) se não houver payment link
        if (!$linkUrl) {
            $linkUrl = (string)($payJson['invoiceUrl'] ?? ($payJson['bankSlipUrl'] ?? ''));
        }
        // Registrar na tabela
        try {
            $ins = $pdo->prepare('INSERT INTO asaas_payment_links (sale_id, client_id, asaas_payment_id, asaas_link_id, link_url, status) VALUES (?,?,?,?,?,?)');
            $ins->execute([$saleId, $clientId, $paymentId, ($plinkId ?: null), ($linkUrl ?: null), 'aguardando compensação']);
            $localId = (int)$pdo->lastInsertId();
            AuditLogger::log('asaas', 'generate_payment_link', ['sale_id' => $saleId, 'payment_id' => $paymentId, 'plink_id' => $plinkId]);
            return Response::json(['data' => [
                'id' => $localId,
                'sale_id' => $saleId,
                'asaas_payment_id' => $paymentId,
                'asaas_link_id' => $plinkId,
                'link_url' => $linkUrl,
                'status' => 'aguardando compensação'
            ]], 201);
        } catch (\Throwable $e) {
            return Response::json(['error' => 'db_error', 'message' => $e->getMessage()], 500);
        }
    }

    // Geração de link de pagamento diretamente por Pedido (sales_orders)
    public function generatePaymentLinkForOrder(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $cfg = $this->loadConfig($pdo);
        if (empty($cfg['api_key'])) return Response::json(['error' => 'Configuração ausente (api_key)'], 400);
        $params = $r->get('params', []);
        $orderId = (int)($params[0] ?? 0);
        if ($orderId <= 0) return Response::json(['error' => 'order_id inválido'], 422);

        // Buscar pedido
        $st = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status, created_at FROM sales_orders WHERE id = ? LIMIT 1');
        $st->execute([$orderId]);
        $order = $st->fetch();
        if (!$order) return Response::json(['error' => 'Pedido não encontrado'], 404);
        $clientId = (int)($order['cliente_id'] ?? 0);
        $valorTotal = (float)($order['valor_total'] ?? 0);
        if ($clientId <= 0 || $valorTotal <= 0) {
            return Response::json(['error' => 'Pedido sem cliente ou valor_total inválido'], 422);
        }
        $saleId = (string)($order['numero'] ?? ('ORD-' . $orderId));
        $dueDate = (string)($r->body['due_date'] ?? date('Y-m-d', strtotime('+7 days')));
        $billingType = (string)($r->body['billing_type'] ?? 'BOLETO');
        $amountCents = (int)round($valorTotal * 100);
        $value = round($amountCents / 100, 2);

        // Garantir cliente no Asaas
        $asaasCustomer = $this->ensureAsaasCustomer($pdo, $cfg, $clientId);
        if (!$asaasCustomer) return Response::json(['error' => 'Falha ao criar/obter cliente Asaas'], 502);

        // Criar cobrança
        $payReq = [
            'customer' => $asaasCustomer,
            'billingType' => $billingType,
            'value' => $value,
            'dueDate' => $dueDate,
            'description' => 'Pedido ' . $saleId,
            'externalReference' => $saleId
        ];
        $payCall = $this->callAsaas($cfg, 'payments', 'POST', $payReq);
        $this->logOp($pdo, 'payments', 'POST', $payCall['status'], $payReq, $payCall['body'], null);
        if ($payCall['status'] < 200 || $payCall['status'] >= 300) {
            return Response::json(['error' => 'asaas_error', 'stage' => 'create_payment', 'status' => $payCall['status'], 'response' => $payCall['body']], 502);
        }
        $payJson = json_decode($payCall['body'] ?? '{}', true);
        $paymentId = (string)($payJson['id'] ?? '');
        if ($paymentId === '') return Response::json(['error' => 'asaas_sem_id'], 502);

        // Gerar link de pagamento
        $linkReq = [
            'name' => 'Pedido ' . $saleId,
            'description' => 'Pagamento do pedido ' . $saleId,
            'billingType' => $billingType,
            'value' => $value,
            'dueDate' => $dueDate,
            'externalReference' => $saleId
        ];
        $plinkCall = $this->callAsaas($cfg, 'paymentLinks', 'POST', $linkReq);
        $this->logOp($pdo, 'paymentLinks', 'POST', $plinkCall['status'], $linkReq, $plinkCall['body'], null);
        $plinkJson = null; $plinkId = null; $linkUrl = null;
        if ($plinkCall['status'] >= 200 && $plinkCall['status'] < 300) {
            $plinkJson = json_decode($plinkCall['body'] ?? '{}', true);
            $plinkId = (string)($plinkJson['id'] ?? '');
            $linkUrl = (string)($plinkJson['url'] ?? '');
        }
        if (!$linkUrl) { $linkUrl = (string)($payJson['invoiceUrl'] ?? ($payJson['bankSlipUrl'] ?? '')); }

        // Registrar na tabela local
        try {
            $ins = $pdo->prepare('INSERT INTO asaas_payment_links (sale_id, client_id, asaas_payment_id, asaas_link_id, link_url, status) VALUES (?,?,?,?,?,?)');
            $ins->execute([$saleId, $clientId, $paymentId, ($plinkId ?: null), ($linkUrl ?: null), 'aguardando compensação']);
            $localId = (int)$pdo->lastInsertId();
            AuditLogger::log('asaas', 'generate_payment_link_order', ['order_id' => $orderId, 'sale_id' => $saleId, 'payment_id' => $paymentId, 'plink_id' => $plinkId]);
            return Response::json(['data' => [
                'id' => $localId,
                'order_id' => $orderId,
                'sale_id' => $saleId,
                'client_id' => $clientId,
                'asaas_payment_id' => $paymentId,
                'asaas_link_id' => $plinkId,
                'link_url' => $linkUrl,
                'status' => 'aguardando compensação'
            ]], 201);
        } catch (\Throwable $e) {
            return Response::json(['error' => 'db_error', 'message' => $e->getMessage()], 500);
        }
    }

    public function getPaymentLinkById(Request $r): Response {
        $pdo = $this->pdo(); $this->ensureTables($pdo);
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return Response::json(['error' => 'id inválido'], 422);
        try {
            $q = $pdo->prepare('SELECT * FROM asaas_payment_links WHERE id = ? LIMIT 1');
            $q->execute([$id]);
            $row = $q->fetch();
            if (!$row) return Response::json(['error' => 'não encontrado'], 404);
            return Response::json(['data' => $row]);
        } catch (\Throwable $e) {
            return Response::json(['error' => 'db_error', 'message' => $e->getMessage()], 500);
        }
    }
}