<?php
namespace Modules\Tecnicos;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Routing\Router;
use Gestoreasy\Core\Auth\AuthMiddleware;
use Gestoreasy\Core\Auth\Rbac;
use Gestoreasy\Core\View\View;
use Gestoreasy\Core\Audit\AuditLogger;
use Gestoreasy\Core\Auth\UserContext;

class Module {
    private array $allowedUf = ['AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS','MG','PA','PB','PR','PE','PI','RJ','RN','RO','RS','RR','SC','SP','SE','TO'];
    public function register(Router $router): void {
        // View UI
        $router->get('/cadastros/tecnicos', function(Request $r){
            $deny = (Rbac::requirePermissions(['tecnicos.read']))($r);
            if ($deny instanceof Response) return $deny;
            return View::renderWithLayout('tecnicos/index', ['title' => 'Gestão de Técnicos']);
        }, [AuthMiddleware::requireAuthHtml(), Rbac::requirePermissions(['tecnicos.read'])]);

        // API CRUD (base correta: /api/cadastros/tecnicos)
        $router->get('/api/cadastros/tecnicos', [$this, 'list'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.read'])]);
        $router->get('/api/cadastros/tecnicos/{id}', [$this, 'getById'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.read'])]);
        $router->post('/api/cadastros/tecnicos', [$this, 'create'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.create'])]);
        $router->put('/api/cadastros/tecnicos/{id}', [$this, 'update'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.update'])]);
        $router->delete('/api/cadastros/tecnicos/{id}', [$this, 'delete'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.delete'])]);

        // Comissões, histórico e relatórios
        $router->get('/api/cadastros/tecnicos/{id}/comissoes', [$this, 'commissions'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.read'])]);
        $router->post('/api/cadastros/tecnicos/{id}/service-history', [$this, 'appendServiceHistory'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.update'])]);
        $router->get('/api/cadastros/tecnicos/relatorios/produtividade', [$this, 'productivityReport'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.reports'])]);
        $router->get('/api/cadastros/tecnicos/export/payments', [$this, 'exportPayments'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.reports'])]);
        $router->get('/api/cadastros/tecnicos/identify', [$this, 'identifyByPdvCode'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.read'])]);
        $router->get('/api/cadastros/tecnicos/identify/{code}', [$this, 'identifyByPdvCode'], [AuthMiddleware::requireAuth(), Rbac::requirePermissions(['tecnicos.read'])]);
    }

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

    private function appSecret(): string {
        $s = getenv('APP_SECRET');
        return $s ? (string)$s : 'local-dev-secret';
    }

    private function validateCpf(string $cpfRaw): bool {
        $cpf = preg_replace('/\D+/', '', $cpfRaw);
        if (strlen($cpf) !== 11) return false;
        if (preg_match('/^(\d)\1{10}$/', $cpf)) return false; // todos iguais
        // dígitos verificadores
        $sum = 0; for($i=0,$w=10;$i<9;$i++,$w--) { $sum += ((int)$cpf[$i])*$w; }
        $rest = ($sum*10)%11; $d1 = ($rest === 10 ? 0 : $rest);
        if ($d1 !== (int)$cpf[9]) return false;
        $sum = 0; for($i=0,$w=11;$i<10;$i++,$w--) { $sum += ((int)$cpf[$i])*$w; }
        $rest = ($sum*10)%11; $d2 = ($rest === 10 ? 0 : $rest);
        return $d2 === (int)$cpf[10];
    }

    private function validateRg(string $rgRaw, ?string $uf): bool {
        $rg = trim($rgRaw);
        if ($rg === '') return false;
        if ($uf) { $uf = strtoupper($uf); if (!in_array($uf, $this->allowedUf, true)) return false; }
        // Padrão genérico: dígitos com separadores opcionais
        return (bool)preg_match('/^[0-9A-Za-z\.\-]{5,20}$/', $rg);
    }

    private function validateBank(array $data): array {
        $errors = [];
        if (isset($data['bank_agencia']) && trim((string)$data['bank_agencia']) !== '') {
            if (!preg_match('/^\d{4}$/', (string)$data['bank_agencia'])) $errors[] = 'Agência deve ter 4 dígitos';
        }
        if (isset($data['bank_conta']) && trim((string)$data['bank_conta']) !== '') {
            if (!preg_match('/^\d{1,12}(-\d{1})?$/', (string)$data['bank_conta'])) $errors[] = 'Conta deve ser numérica e pode conter dígito verificador';
        }
        if (isset($data['bank_type']) && trim((string)$data['bank_type']) !== '') {
            $bt = strtolower((string)$data['bank_type']);
            if (!in_array($bt, ['corrente','poupanca'], true)) $errors[] = 'Tipo de conta inválido';
        }
        return $errors;
    }

    private function validateCommission(array $data): array {
        $errors = [];
        if (isset($data['comissao_percent']) && trim((string)$data['comissao_percent']) !== '') {
            $cp = (float)$data['comissao_percent']; if ($cp < 1 || $cp > 99) $errors[] = 'Comissão deve estar entre 1 e 99%';
        }
        if (isset($data['commission_method']) && trim((string)$data['commission_method']) !== '') {
            $cm = strtolower((string)$data['commission_method']);
            if (!in_array($cm, ['por_servico','por_valor'], true)) $errors[] = 'Forma de cálculo inválida';
        }
        if (isset($data['monthly_limit']) && trim((string)$data['monthly_limit']) !== '') {
            $ml = (float)$data['monthly_limit']; if ($ml < 0) $errors[] = 'Limite mensal deve ser positivo';
        }
        return $errors;
    }

    private function applyPixEncryption(array &$data, array &$cols, array &$vals, array &$ph): void {
        if (!empty($data['pix_key'])) {
            $plaintext = (string)$data['pix_key'];
            $key = hash('sha256', $this->appSecret(), true);
            $iv = random_bytes(12);
            $tag = '';
            $cipher = openssl_encrypt($plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
            // Gravar campos criptografados e limpar pix_key em texto claro
            $cols[] = 'pix_key_enc'; $vals[] = $cipher; $ph[] = '?';
            $cols[] = 'pix_key_iv'; $vals[] = $iv; $ph[] = '?';
            $cols[] = 'pix_key_tag'; $vals[] = $tag; $ph[] = '?';
            // Não armazenar pix_key em texto
            $data['pix_key'] = null;
        }
    }

    private function decryptPixKey(array $row): string {
        try {
            $enc = $row['pix_key_enc'] ?? null; $iv = $row['pix_key_iv'] ?? null; $tag = $row['pix_key_tag'] ?? null;
            if (!$enc || !$iv || !$tag) return (string)($row['pix_key'] ?? '');
            $key = hash('sha256', $this->appSecret(), true);
            $plain = openssl_decrypt($enc, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
            return $plain !== false ? (string)$plain : '';
        } catch (\Throwable $e) { return ''; }
    }

    public function list(Request $r) {
        $pdo = $this->pdo();
        $q = trim((string)($r->query['q'] ?? ''));
        $limit = max(1, min(100, (int)($r->query['limit'] ?? 20)));
        $page = max(1, (int)($r->query['page'] ?? 1));
        $offset = ($page - 1) * $limit;
        $where = '';
        $params = [];
        if ($q !== '') { $where = 'WHERE nome LIKE ? OR cpf LIKE ? OR rg LIKE ? OR email LIKE ? OR telefone LIKE ?'; $params = ["%$q%","%$q%","%$q%","%$q%","%$q%"]; }
        $stmt = $pdo->prepare("SELECT id, nome, cpf, rg, email, telefone, comissao_percent, enabled, pdv_code, created_at FROM technicians $where ORDER BY id DESC LIMIT $limit OFFSET $offset");
        $stmt->execute($params);
        $rows = $stmt->fetchAll();
        $countStmt = $pdo->prepare("SELECT COUNT(*) AS total FROM technicians " . ($where ?: ''));
        $countStmt->execute($params);
        $total = (int)($countStmt->fetch()['total'] ?? 0);
        return Response::json(['data' => $rows, 'page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    public function getById(Request $r) {
        $pdo = $this->pdo();
        $id = (int)($r->get('params', [])[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $stmt = $pdo->prepare('SELECT * FROM technicians WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['message' => 'Técnico não encontrado']]);
        try { AuditLogger::logRead('technicians', $id, ['endpoint' => 'tecnicos.get']); } catch (\Throwable $e) {}
        return Response::json(['data' => $row]);
    }

    public function create(Request $r) {
        $pdo = $this->pdo();
        $data = is_array($r->body) ? $r->body : [];
        foreach ($_POST as $k=>$v) { $data[$k] = $v; }
        // Validações básicas
        $required = ['nome','cpf','rg','telefone'];
        foreach ($required as $req) { if (!isset($data[$req]) || trim((string)$data[$req]) === '') return new Response(422, ['error' => ['message' => "Campo obrigatório: $req"]]); }
        if (mb_strlen(trim((string)$data['nome'])) < 3) return new Response(422, ['error' => ['message' => 'Nome deve ter ao menos 3 caracteres']]);
        if (isset($data['email']) && $data['email'] !== '' && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) return new Response(422, ['error' => ['message' => 'E-mail inválido']]);
        if (!$this->validateCpf((string)$data['cpf'])) return new Response(422, ['error' => ['message' => 'CPF inválido']]);
        $rgUf = isset($data['rg_uf']) ? strtoupper((string)$data['rg_uf']) : null;
        if (!$this->validateRg((string)$data['rg'], $rgUf)) return new Response(422, ['error' => ['message' => 'RG/UF inválidos']]);
        $bankErrors = $this->validateBank($data); if ($bankErrors) return new Response(422, ['error' => ['message' => implode('; ', $bankErrors)]]);
        $commErrors = $this->validateCommission($data); if ($commErrors) return new Response(422, ['error' => ['message' => implode('; ', $commErrors)]]);
        // Status e inativação
        if (array_key_exists('enabled', $data)) {
            $enabled = (int)$data['enabled'];
            if ($enabled !== 0 && $enabled !== 1) return new Response(422, ['error' => ['message' => 'Status inválido (ativo/inativo)']]);
            if ($enabled === 0 && empty($data['inactivated_at'])) { $data['inactivated_at'] = date('Y-m-d H:i:s'); }
        }
        // Campos
        $fields = ['nome','cpf','rg','rg_uf','email','telefone','bank_name','bank_agencia','bank_conta','bank_type','pix_key','comissao_percent','commission_method','monthly_limit','enabled','inactivated_at','pdv_code','rfid_code'];
        $cols = []; $vals = []; $ph = [];
        foreach ($fields as $f) { if (array_key_exists($f, $data)) { $cols[] = $f; $vals[] = $data[$f]; $ph[] = '?'; } }
        // Criptografia de PIX
        $this->applyPixEncryption($data, $cols, $vals, $ph);
        try {
            $stmt = $pdo->prepare("INSERT INTO technicians (".implode(',', $cols).") VALUES (".implode(',', $ph).")");
            $stmt->execute($vals);
            $id = (int)$pdo->lastInsertId();
            // Auditoria
            try {
                $user = UserContext::get($r) ?: [];
                $uid = (int)($user['sub'] ?? 0);
                // Não logar dados sensíveis (PIX criptografado)
                $clean = $data;
                unset($clean['pix_key'], $clean['pix_key_enc'], $clean['pix_key_iv'], $clean['pix_key_tag']);
                AuditLogger::logCreate('technicians', $id, $clean, ['user_id' => $uid, 'endpoint' => 'tecnicos.create']);
            } catch (\Throwable $e) { /* noop */ }
            return new Response(201, ['data' => ['id' => $id]]);
        } catch (\Throwable $e) { return new Response(500, ['error' => ['message' => 'Falha ao criar técnico', 'detail' => $e->getMessage()]]); }
    }

    public function update(Request $r) {
        $pdo = $this->pdo();
        $id = (int)($r->get('params', [])[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $data = is_array($r->body) ? $r->body : [];
        foreach ($_POST as $k=>$v) { $data[$k] = $v; }
        if (isset($data['email']) && $data['email'] !== '' && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) return new Response(422, ['error' => ['message' => 'E-mail inválido']]);
        if (isset($data['cpf']) && trim((string)$data['cpf']) !== '') { if (!$this->validateCpf((string)$data['cpf'])) return new Response(422, ['error' => ['message' => 'CPF inválido']]); }
        if ((isset($data['rg']) || isset($data['rg_uf'])) ) {
            $rgUf = isset($data['rg_uf']) ? strtoupper((string)$data['rg_uf']) : null;
            $rgVal = (string)($data['rg'] ?? ''); if ($rgVal !== '' && !$this->validateRg($rgVal, $rgUf)) return new Response(422, ['error' => ['message' => 'RG/UF inválidos']]);
        }
        $bankErrors = $this->validateBank($data); if ($bankErrors) return new Response(422, ['error' => ['message' => implode('; ', $bankErrors)]]);
        $commErrors = $this->validateCommission($data); if ($commErrors) return new Response(422, ['error' => ['message' => implode('; ', $commErrors)]]);
        if (array_key_exists('enabled', $data)) { $enabled = (int)$data['enabled']; if ($enabled !== 0 && $enabled !== 1) return new Response(422, ['error' => ['message' => 'Status inválido (ativo/inativo)']]); if ($enabled === 0 && empty($data['inactivated_at'])) { $data['inactivated_at'] = date('Y-m-d H:i:s'); } }
        $fields = ['nome','cpf','rg','rg_uf','email','telefone','bank_name','bank_agencia','bank_conta','bank_type','pix_key','comissao_percent','commission_method','monthly_limit','enabled','inactivated_at','pdv_code','rfid_code'];
        $sets = []; $vals = [];
        foreach ($fields as $f) { if (array_key_exists($f, $data)) { $sets[] = "$f = ?"; $vals[] = $data[$f]; } }
        // Criptografia de PIX em update
        if (!empty($data['pix_key'])) {
            $key = hash('sha256', $this->appSecret(), true);
            $iv = random_bytes(12); $tag = '';
            $cipher = openssl_encrypt((string)$data['pix_key'], 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
            $sets[] = 'pix_key = ?'; $vals[] = null;
            $sets[] = 'pix_key_enc = ?'; $vals[] = $cipher;
            $sets[] = 'pix_key_iv = ?'; $vals[] = $iv;
            $sets[] = 'pix_key_tag = ?'; $vals[] = $tag;
        }
        if (!$sets) return new Response(422, ['error' => ['message' => 'Nenhuma alteração enviada']]);
        $vals[] = $id;
        try {
            // Carregar estado anterior para auditoria
            $prevStmt = $pdo->prepare('SELECT * FROM technicians WHERE id = ? LIMIT 1');
            $prevStmt->execute([$id]);
            $oldRow = $prevStmt->fetch() ?: [];
            $stmt = $pdo->prepare("UPDATE technicians SET ".implode(',', $sets)." WHERE id = ?");
            $stmt->execute($vals);
            // Estado após atualização
            $curStmt = $pdo->prepare('SELECT * FROM technicians WHERE id = ? LIMIT 1');
            $curStmt->execute([$id]);
            $newRow = $curStmt->fetch() ?: [];
            // Auditoria
            try {
                $user = UserContext::get($r) ?: [];
                $uid = (int)($user['sub'] ?? 0);
                // Não logar dados sensíveis
                foreach (['pix_key','pix_key_enc','pix_key_iv','pix_key_tag'] as $k) { unset($oldRow[$k], $newRow[$k]); }
                AuditLogger::logUpdate('technicians', $id, $oldRow, $newRow, ['user_id' => $uid, 'endpoint' => 'tecnicos.update']);
            } catch (\Throwable $e) { /* noop */ }
            return Response::json(['data' => ['id' => $id]]);
        } catch (\Throwable $e) { return new Response(500, ['error' => ['message' => 'Falha ao atualizar técnico', 'detail' => $e->getMessage()]]); }
    }

    public function delete(Request $r) {
        $pdo = $this->pdo();
        $id = (int)($r->get('params', [])[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        try {
            // Capturar registro para auditoria antes de excluir
            $sel = $pdo->prepare('SELECT * FROM technicians WHERE id = ? LIMIT 1');
            $sel->execute([$id]);
            $row = $sel->fetch() ?: [];
            $stmt = $pdo->prepare('DELETE FROM technicians WHERE id = ?');
            $stmt->execute([$id]);
            // Auditoria
            try {
                $user = UserContext::get($r) ?: [];
                $uid = (int)($user['sub'] ?? 0);
                foreach (['pix_key','pix_key_enc','pix_key_iv','pix_key_tag'] as $k) { unset($row[$k]); }
                AuditLogger::logDelete('technicians', $id, $row, ['user_id' => $uid, 'endpoint' => 'tecnicos.delete']);
            } catch (\Throwable $e) { /* noop */ }
        } catch (\Throwable $e) { return new Response(409, ['error' => ['message' => 'Não foi possível excluir: vínculo existente ou restrição de integridade']]); }
        return new Response(204, '');
    }

    public function appendServiceHistory(Request $r) {
        $pdo = $this->pdo();
        $id = (int)($r->get('params', [])[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $data = is_array($r->body) ? $r->body : [];
        $required = ['description','amount','executed_at'];
        foreach ($required as $req) { if (!isset($data[$req]) || trim((string)$data[$req]) === '') return new Response(422, ['error' => ['message' => "Campo obrigatório: $req"]]); }
        $tech = $pdo->prepare('SELECT comissao_percent FROM technicians WHERE id = ?'); $tech->execute([$id]); $row = $tech->fetch();
        if (!$row) return new Response(404, ['error' => ['message' => 'Técnico não encontrado']]);
        $amount = (float)$data['amount']; if ($amount <= 0) return new Response(422, ['error' => ['message' => 'Valor do serviço deve ser positivo']]);
        $cp = (float)($row['comissao_percent'] ?? 0.0);
        $commission = round($amount * ($cp/100.0), 2);
        $fields = ['technician_id','service_id','description','amount','commission_amount','executed_at','source','reference_id'];
        $vals = [$id, ($data['service_id'] ?? null), (string)$data['description'], $amount, $commission, (string)$data['executed_at'], ($data['source'] ?? 'servico'), ($data['reference_id'] ?? null)];
        try {
            $stmt = $pdo->prepare('INSERT INTO technician_service_history (technician_id, service_id, description, amount, commission_amount, executed_at, source, reference_id) VALUES (?,?,?,?,?,?,?,?)');
            $stmt->execute($vals);
            // Auditoria
            try {
                $user = UserContext::get($r) ?: [];
                $uid = (int)($user['sub'] ?? 0);
                AuditLogger::logCreate('technician_service_history', (int)$pdo->lastInsertId(), [
                    'technician_id' => $id,
                    'service_id' => $data['service_id'] ?? null,
                    'description' => (string)$data['description'],
                    'amount' => $amount,
                    'commission_amount' => $commission,
                    'executed_at' => (string)$data['executed_at'],
                    'source' => $data['source'] ?? 'servico',
                    'reference_id' => $data['reference_id'] ?? null
                ], ['user_id' => $uid, 'endpoint' => 'tecnicos.service_history.append']);
            } catch (\Throwable $e) { /* noop */ }
            return new Response(201, ['data' => ['commission_amount' => $commission]]);
        } catch (\Throwable $e) { return new Response(500, ['error' => ['message' => 'Falha ao registrar serviço', 'detail' => $e->getMessage()]]); }
    }

    public function commissions(Request $r) {
        $pdo = $this->pdo();
        $id = (int)($r->get('params', [])[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $from = (string)($r->query['from'] ?? date('Y-m-01'));
        $to = (string)($r->query['to'] ?? date('Y-m-t'));
        $stmt = $pdo->prepare('SELECT COUNT(*) AS services, SUM(amount) AS total_amount, SUM(commission_amount) AS total_commission FROM technician_service_history WHERE technician_id = ? AND DATE(executed_at) BETWEEN ? AND ?');
        $stmt->execute([$id, $from, $to]);
        $row = $stmt->fetch() ?: ['services' => 0, 'total_amount' => 0, 'total_commission' => 0];
        return Response::json(['data' => $row, 'from' => $from, 'to' => $to]);
    }

    public function productivityReport(Request $r) {
        $pdo = $this->pdo();
        $from = (string)($r->query['from'] ?? date('Y-m-01'));
        $to = (string)($r->query['to'] ?? date('Y-m-t'));
        $stmt = $pdo->prepare('SELECT t.id AS technician_id, t.nome, COUNT(h.id) AS services, COALESCE(SUM(h.amount),0) AS total_amount, COALESCE(SUM(h.commission_amount),0) AS total_commission FROM technicians t LEFT JOIN technician_service_history h ON h.technician_id = t.id AND DATE(h.executed_at) BETWEEN ? AND ? GROUP BY t.id, t.nome ORDER BY total_commission DESC');
        $stmt->execute([$from, $to]);
        $rows = $stmt->fetchAll();
        return Response::json(['data' => $rows, 'from' => $from, 'to' => $to]);
    }

    public function exportPayments(Request $r) {
        $pdo = $this->pdo();
        $from = (string)($r->query['from'] ?? date('Y-m-01'));
        $to = (string)($r->query['to'] ?? date('Y-m-t'));
        $stmt = $pdo->prepare('SELECT t.nome, t.bank_name, t.bank_agencia, t.bank_conta, t.pix_key, t.pix_key_enc, t.pix_key_iv, t.pix_key_tag, COALESCE(SUM(h.commission_amount),0) AS commission_total FROM technicians t LEFT JOIN technician_service_history h ON h.technician_id = t.id AND DATE(h.executed_at) BETWEEN ? AND ? GROUP BY t.id, t.nome, t.bank_name, t.bank_agencia, t.bank_conta, t.pix_key, t.pix_key_enc, t.pix_key_iv, t.pix_key_tag ORDER BY t.nome ASC');
        $stmt->execute([$from, $to]);
        $rows = $stmt->fetchAll();
        // CSV simples
        $csv = "nome,bank_name,bank_agencia,bank_conta,pix_key,commission_total\n";
        foreach ($rows as $r) {
            $pix = $this->decryptPixKey($r);
            $csv .= sprintf("%s,%s,%s,%s,%s,%.2f\n", $r['nome'] ?? '', $r['bank_name'] ?? '', $r['bank_agencia'] ?? '', $r['bank_conta'] ?? '', $pix, (float)($r['commission_total'] ?? 0));
        }
        return new Response(200, $csv, ['content-type' => 'text/csv; charset=utf-8']);
    }

    public function identifyByPdvCode(Request $r) {
        $pdo = $this->pdo();
        $code = trim((string)($r->query['code'] ?? ($r->get('params', [])[0] ?? '')));
        if ($code === '') return new Response(422, ['error' => ['message' => 'Código PDV é obrigatório']]);
        $stmt = $pdo->prepare('SELECT id, nome, enabled FROM technicians WHERE pdv_code = ? LIMIT 1');
        $stmt->execute([$code]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['message' => 'Técnico não encontrado']]);
        if ((int)($row['enabled'] ?? 1) === 0) return new Response(422, ['error' => ['message' => 'Técnico inativo']]);
        try { AuditLogger::logRead('technicians', (int)$row['id'], ['endpoint' => 'tecnicos.identify', 'pdv_code' => $code]); } catch (\Throwable $e) {}
        return Response::json(['data' => $row]);
    }

    public function name(): string { return 'Tecnicos'; }
    public function version(): string { return '0.1.0'; }
    public function dependencies(): array { return ['Vendas','Servicos','PDV']; }
}