<?php
require_once __DIR__ . '/../bootstrap.php';

use Gestoreasy\Core\Kernel;
use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Auth\Jwt;
use Gestoreasy\Core\Tenant\TenantAdmin;
use Gestoreasy\Core\Tenant\ConnectionManager;

function assertTrue($cond, $msg) { if (!$cond) { echo "FAIL: $msg\n"; exit(1); } }

$tenant = 'default';
TenantAdmin::runMigrations($tenant);
$pdo = ConnectionManager::forTenant($tenant);
TenantAdmin::seedDefaultAdmin($pdo);

// Identificar admin padrão e garantir RBAC para fiscal.sped.read
$email = getenv('DEFAULT_ADMIN_EMAIL') ?: 'admin@local';
$rowAdmin = $pdo->query("SELECT id, email, name FROM users WHERE email='" . addslashes($email) . "' LIMIT 1")->fetch();
assertTrue(!!$rowAdmin, 'Admin padrão deve existir');
$uid = (int)$rowAdmin['id'];
$roleId = (int)($pdo->query("SELECT id FROM roles WHERE name='admin' LIMIT 1")->fetch()['id'] ?? 0);
$pdo->prepare('INSERT IGNORE INTO permissions (name) VALUES (?)')->execute(['fiscal.sped.read']);
$permId = (int)($pdo->query("SELECT id FROM permissions WHERE name='fiscal.sped.read' LIMIT 1")->fetch()['id'] ?? 0);
if ($roleId && $permId) {
  $pdo->prepare('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)')->execute([$roleId, $permId]);
  $pdo->prepare('INSERT IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)')->execute([$uid, $roleId]);
}

// Seed empresa mínima válida para validação e geração
$pdo->exec("INSERT INTO empresas (nome, razao_social, cnpj, ie, email, telefone, estado, cep, logradouro, numero, bairro, cidade, created_at)
  SELECT 'Empresa Teste', 'Empresa Teste Ltda', '12.345.678/0001-99', '123456789', 'contato@empresa.test', '(21)3333-3333', 'RJ', '20000000', 'Rua Teste', '100', 'Centro', 'Rio de Janeiro', NOW()
  WHERE NOT EXISTS (SELECT 1 FROM empresas)");

// Seed mínimo para geração ter algo a consolidar
$pdo->exec("INSERT INTO products (codigo, descricao, ean, unidade, ncm) VALUES ('SKU-SPED-T1','Produto SPED T1','1234567890123','UN','00000000')");
$prodId = (int)($pdo->query("SELECT id FROM products WHERE codigo='SKU-SPED-T1' ORDER BY id DESC LIMIT 1")->fetch()['id'] ?? 0);
$pdo->exec("INSERT INTO clients (nome, cnpj) VALUES ('Cliente SPED','11.222.333/0001-44')");
$cliId = (int)($pdo->query("SELECT id FROM clients WHERE nome='Cliente SPED' ORDER BY id DESC LIMIT 1")->fetch()['id'] ?? 0);
$pdo->exec("INSERT INTO sales_orders (numero, cliente_id, valor_total, status, created_at) VALUES ('SO-SPED-001', $cliId, 100.00, 'aberto', NOW())");
$orderId = (int)($pdo->query("SELECT id FROM sales_orders WHERE numero='SO-SPED-001' ORDER BY id DESC LIMIT 1")->fetch()['id'] ?? 0);
if ($orderId && $prodId) {
  $pdo->exec("INSERT INTO sales_order_items (order_id, product_id, quantidade, preco, total) VALUES ($orderId, $prodId, 2.000, 50.00, 100.00)");
}

$kernel = new Kernel();

// Headers com impersonation (bypass de RBAC), e headers normais com permissão efetiva
$payloadImpersonated = [ 'sub' => $uid, 'email' => $rowAdmin['email'], 'name' => $rowAdmin['name'], 'tenant' => $tenant, 'roles' => ['admin'], 'impersonated' => true ];
$tokenImp = Jwt::sign($payloadImpersonated);
$hdrImp = [ 'x-tenant-id' => $tenant, 'authorization' => 'Bearer ' . $tokenImp ];

$payloadAdmin = [ 'sub' => $uid, 'email' => $rowAdmin['email'], 'name' => $rowAdmin['name'], 'tenant' => $tenant, 'roles' => ['admin'] ];
$tokenAdm = Jwt::sign($payloadAdmin);
$hdrAdm = [ 'x-tenant-id' => $tenant, 'authorization' => 'Bearer ' . $tokenAdm ];
// Usuário admin não-superadmin para testar RBAC sem bypass
$uid2 = 9998;
$pdo->prepare('INSERT IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)')->execute([$uid2, $roleId]);
$payloadAdmin2 = [ 'sub' => $uid2, 'email' => 'adm2@local', 'name' => 'Admin 2', 'tenant' => $tenant, 'roles' => ['admin'] ];
$tokenAdm2 = Jwt::sign($payloadAdmin2);
$hdrAdm2 = [ 'x-tenant-id' => $tenant, 'authorization' => 'Bearer ' . $tokenAdm2 ];

// 1) POST /api/fiscal/sped/validate deve retornar 200 e ok=true quando empresa válida
$companyId = (int)($pdo->query("SELECT id FROM empresas ORDER BY id LIMIT 1")->fetch()['id'] ?? 0);
// Garantir dados completos da empresa antes da primeira validação
if ($companyId) {
  $stmt = $pdo->prepare('UPDATE empresas SET ie=?, im=?, logradouro=?, numero=?, bairro=?, cidade=?, cep=? WHERE id=?');
  $stmt->execute(['123456789', '12345', 'Rua Teste', '100', 'Centro', 'Rio de Janeiro', '20000000', $companyId]);
}
$reqVal = new Request($hdrImp, [], [], [], 'POST', '/api/fiscal/sped/validate');
$resVal = $kernel->handle($reqVal);
assertTrue($resVal->status === 200, 'POST /api/fiscal/sped/validate deve retornar 200');
assertTrue(is_array($resVal->body), 'Resposta de validate deve ser JSON');
assertTrue(isset($resVal->body['ok']) && $resVal->body['ok'] === true, 'Validate ok=true para empresa válida');

// 2) POST /api/fiscal/sped/generate deve retornar 200 com lines/txt
$bodyGen = [ 'year' => (int)date('Y'), 'month' => (int)date('n'), 'perfil' => 'A', 'ind_ativ' => 1, 'cod_fin' => 0, 'cod_ver' => '017' ];
$reqGen = new Request($hdrImp, [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resGen = $kernel->handle($reqGen);
assertTrue($resGen->status === 200, 'POST /api/fiscal/sped/generate deve retornar 200');
assertTrue(is_array($resGen->body['lines'] ?? null), 'Resposta de generate deve conter lines como array');
assertTrue(is_string($resGen->body['txt'] ?? null) && strlen($resGen->body['txt']) > 0, 'Resposta de generate deve conter txt não vazio');

// 3) POST /api/fiscal/sped/check deve retornar 200 com ok/issues
$reqChk = new Request($hdrImp, [], $bodyGen, [], 'POST', '/api/fiscal/sped/check');
$resChk = $kernel->handle($reqChk);
assertTrue($resChk->status === 200, 'POST /api/fiscal/sped/check deve retornar 200');
assertTrue(is_array($resChk->body), 'Resposta de check deve ser JSON');
assertTrue(array_key_exists('ok', $resChk->body), 'Resposta de check deve conter ok');
assertTrue(is_array($resChk->body['issues'] ?? null), 'Resposta de check deve conter issues como array');

// 3.1) Validação com dados faltantes: limpar IE/IM e endereço para checar mensagens
if ($companyId) {
  $stmt = $pdo->prepare('UPDATE empresas SET ie=?, im=?, logradouro=?, numero=?, bairro=?, cidade=?, cep=? WHERE id=?');
  $stmt->execute(['', '', '', '', '', '', '', $companyId]);
}
$reqVal2 = new Request($hdrImp, [], [], [], 'POST', '/api/fiscal/sped/validate');
$resVal2 = $kernel->handle($reqVal2);
assertTrue($resVal2->status === 200, 'POST /api/fiscal/sped/validate com dados faltantes deve retornar 200');
assertTrue(($resVal2->body['ok'] ?? true) === false, 'Validate deve retornar ok=false quando houver issues');
$issues2 = $resVal2->body['issues'] ?? [];
assertTrue(in_array('IE não informada', $issues2, true), 'Deve reportar IE não informada');
assertTrue(in_array('IM não informada', $issues2, true), 'Deve reportar IM não informada');
assertTrue(in_array('Logradouro não informado', $issues2, true), 'Deve reportar Logradouro não informado');
assertTrue(in_array('Número não informado', $issues2, true), 'Deve reportar Número não informado');
assertTrue(in_array('Bairro não informado', $issues2, true), 'Deve reportar Bairro não informado');
assertTrue(in_array('Cidade não informada', $issues2, true), 'Deve reportar Cidade não informada');
assertTrue(in_array('CEP inválido', $issues2, true), 'Deve reportar CEP inválido');
// Restaurar dados básicos para próximos testes
if ($companyId) {
  $stmt = $pdo->prepare('UPDATE empresas SET ie=?, im=?, logradouro=?, numero=?, bairro=?, cidade=?, cep=? WHERE id=?');
  $stmt->execute(['123456789', '12345', 'Rua Teste', '100', 'Centro', 'Rio de Janeiro', '20000000', $companyId]);
}

// 4) RBAC: sem Authorization -> 401 (endpoints permitem API Key, mas aqui sem chave)
$reqNoAuth = new Request(['x-tenant-id' => $tenant], [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resNoAuth = $kernel->handle($reqNoAuth);
assertTrue($resNoAuth->status === 401, 'POST /api/fiscal/sped/generate sem auth/key deve retornar 401');

// 4.1) Com X-API-Key válida -> 200 nos endpoints que permitem
putenv('SPED_API_KEY=TEST-SPED-KEY');
$_ENV['SPED_API_KEY'] = 'TEST-SPED-KEY';
$hdrApi = [ 'x-tenant-id' => $tenant, 'x-api-key' => 'TEST-SPED-KEY' ];
// validate permite API Key
$reqValApi = new Request($hdrApi, [], [], [], 'POST', '/api/fiscal/sped/validate');
$resValApi = $kernel->handle($reqValApi);
assertTrue($resValApi->status === 200, 'POST /api/fiscal/sped/validate com X-API-Key deve retornar 200');
assertTrue(isset($resValApi->body['ok']), 'Resposta de validate com X-API-Key deve conter ok');
// generate permite API Key
$reqGenApi = new Request($hdrApi, [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resGenApi = $kernel->handle($reqGenApi);
assertTrue($resGenApi->status === 200, 'POST /api/fiscal/sped/generate com X-API-Key deve retornar 200');
// check permite API Key
$reqChkApi = new Request($hdrApi, [], $bodyGen, [], 'POST', '/api/fiscal/sped/check');
$resChkApi = $kernel->handle($reqChkApi);
assertTrue($resChkApi->status === 200, 'POST /api/fiscal/sped/check com X-API-Key deve retornar 200');

// 4.2) X-API-Key inválida -> 401
$hdrBadApi = [ 'x-tenant-id' => $tenant, 'x-api-key' => 'WRONG-KEY' ];
$reqBadApi = new Request($hdrBadApi, [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resBadApi = $kernel->handle($reqBadApi);
assertTrue($resBadApi->status === 401, 'POST /api/fiscal/sped/generate com X-API-Key inválida deve retornar 401');

// 5) RBAC: com token de usuário sem permissões efetivas -> 403
$tokenNoPerm = Jwt::sign([ 'sub' => 9999, 'email' => 'user@local', 'name' => 'User', 'tenant' => $tenant, 'roles' => ['user'] ]);
$hdrNoPerm = [ 'x-tenant-id' => $tenant, 'authorization' => 'Bearer ' . $tokenNoPerm ];
$reqForbidden = new Request($hdrNoPerm, [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resForbidden = $kernel->handle($reqForbidden);
assertTrue($resForbidden->status === 403, 'POST /api/fiscal/sped/generate com usuário sem permissões deve retornar 403');

// 6) RBAC: com admin sem impersonation e permissões efetivas -> 200
$reqGenAdm = new Request($hdrAdm, [], $bodyGen, [], 'POST', '/api/fiscal/sped/generate');
$resGenAdm = $kernel->handle($reqGenAdm);
assertTrue($resGenAdm->status === 200, 'POST /api/fiscal/sped/generate com admin e permissões deve retornar 200');

// 7) Endpoint auto: requer fiscal.sped.manage
// 7.1) Sem manage vinculado ao admin role -> 403
$pdo->prepare('INSERT IGNORE INTO permissions (name) VALUES (?)')->execute(['fiscal.sped.manage']);
// garantir que NÃO esteja vinculado ainda: remover se existir
$pdo->prepare('DELETE rp FROM role_permissions rp JOIN roles r ON r.id = rp.role_id JOIN permissions p ON p.id = rp.permission_id WHERE r.name = ? AND p.name = ?')->execute(['admin', 'fiscal.sped.manage']);
$reqAuto403 = new Request($hdrAdm2, [], $bodyGen, [], 'POST', '/api/fiscal/sped/auto');
$resAuto403 = $kernel->handle($reqAuto403);
assertTrue($resAuto403->status === 403, 'POST /api/fiscal/sped/auto sem fiscal.sped.manage deve retornar 403');

// 7.2) Vincular fiscal.sped.manage ao admin role -> 200
$permManageId = (int)($pdo->query("SELECT id FROM permissions WHERE name='fiscal.sped.manage' LIMIT 1")->fetch()['id'] ?? 0);
if ($roleId && $permManageId) {
  $pdo->prepare('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)')->execute([$roleId, $permManageId]);
}
$reqAuto200 = new Request($hdrAdm2, [], $bodyGen, [], 'POST', '/api/fiscal/sped/auto');
$resAuto200 = $kernel->handle($reqAuto200);
assertTrue(in_array($resAuto200->status, [200,201], true), 'POST /api/fiscal/sped/auto com manage deve retornar 200/201');

// 7.3) Endpoint auto não aceita X-API-Key -> 401
$reqAutoApi = new Request([ 'x-tenant-id' => $tenant, 'x-api-key' => 'TEST-SPED-KEY' ], [], $bodyGen, [], 'POST', '/api/fiscal/sped/auto');
$resAutoApi = $kernel->handle($reqAutoApi);
assertTrue($resAutoApi->status === 401, 'POST /api/fiscal/sped/auto com X-API-Key deve retornar 401');

echo "PASS: SpedEndpointsTest (validate/generate/check + RBAC + API-Key).\n";

?>