<?php
namespace Modules\Cadastros\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\View\View;
use Gestoreasy\Core\Auth\Rbac;

class CadastrosController {
    private array $entities = [
        'fornecedores' => [
            'table' => 'fornecedores',
            'pk' => 'id',
            'fields' => ['empresa_id','nome','tipo_pessoa','cpf','cnpj','ie','email','telefone','contato','site_url'],
            'required' => ['nome'],
            'relations' => [ ['column' => 'empresa_id', 'ref' => 'empresas(id)'] ],
            'search' => ['nome','cnpj','cpf','email','telefone'],
            'list' => ['id','empresa_id','nome','cnpj','cpf','email','telefone','created_at']
        ],
        'empresas' => [
            'table' => 'empresas',
            'pk' => 'id',
            'fields' => [
                'nome','razao_social','cnpj','ie','im','email','telefone','site_url',
                // complementares essenciais solicitados
                'telefone_alt','whatsapp','classificacao','historico_interacoes',
                // logo
                'logo_url'
            ],
            'required' => ['nome'],
            'search' => ['nome','razao_social','cnpj','email','telefone','whatsapp','classificacao'],
            'list' => ['id','logo_url','nome','cnpj','email','telefone','classificacao','created_at']
        ],
        'transportadoras' => [
            'table' => 'transportadoras',
            'pk' => 'id',
            'fields' => ['empresa_id','nome','cnpj','ie','email','telefone','capacidade'],
            'required' => ['nome'],
            'relations' => [ ['column' => 'empresa_id', 'ref' => 'empresas(id)'] ],
            'search' => ['nome','cnpj','email','telefone'],
            'list' => ['id','empresa_id','nome','cnpj','email','telefone','capacidade','created_at']
        ],
        'usuarios' => [
            'table' => 'users',
            'pk' => 'id',
            'fields' => ['name','email','enabled'],
            'required' => ['name','email'],
            'search' => ['name','email'],
            'list' => ['id','name','email','enabled','created_at']
        ],
        'vendedores' => [
            'table' => 'vendedores',
            'pk' => 'id',
            'fields' => ['empresa_id','cliente_id','user_id','nome','email','telefone','comissao_percent'],
            'required' => ['nome'],
            'relations' => [ ['column' => 'empresa_id', 'ref' => 'empresas(id)'] ],
            'search' => ['nome','email','telefone'],
            'list' => ['id','empresa_id','cliente_id','user_id','nome','email','telefone','comissao_percent','created_at']
        ],
        'tecnicos' => [
            'table' => 'tecnicos',
            'pk' => 'id',
            'fields' => ['empresa_id','nome','email','telefone','especialidade'],
            'required' => ['nome'],
            'relations' => [ ['column' => 'empresa_id', 'ref' => 'empresas(id)'] ],
            'search' => ['nome','email','telefone','especialidade'],
            'list' => ['id','empresa_id','nome','email','telefone','especialidade','created_at']
        ],
    ];

    public function viewEntity(Request $r) {
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade não encontrada']]);
        // Exige permissão de leitura da entidade para exibir a view
        $deny = (Rbac::requirePermissions(["cadastros.$entity.read"]))($r);
        if ($deny instanceof Response) return $deny;
        return View::renderWithLayout('cadastros/entity', [
            'title' => 'Cadastros: ' . ucfirst($entity),
            'entity' => $entity
        ]);
    }

    public function list(Request $r) {
        // RBAC leitura
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $deny = (Rbac::requirePermissions(["cadastros.$entity.read"]))($r);
        if ($deny instanceof Response) return $deny;
        $cfg = $this->cfg($r);
        if (!$cfg) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $pdo = ConnectionManager::forTenant();
        $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;
        $orderBy = in_array(($r->query['sort'] ?? 'id'), $cfg['list'], true) ? ($r->query['sort'] ?? 'id') : 'id';
        $dir = strtoupper((string)($r->query['dir'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
        $where = '';
        $params = [];
        if ($q !== '') {
            $likes = [];
            foreach ($cfg['search'] as $col) { $likes[] = "$col LIKE ?"; $params[] = "%$q%"; }
            $where = 'WHERE ' . implode(' OR ', $likes);
        }
        $cols = implode(',', $cfg['list']);
        $stmt = $pdo->prepare("SELECT $cols FROM {$cfg['table']} $where ORDER BY $orderBy $dir LIMIT $limit OFFSET $offset");
        $stmt->execute($params);
        $rows = $stmt->fetchAll();
        $countStmt = $pdo->prepare("SELECT COUNT(*) AS total FROM {$cfg['table']} " . ($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) {
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $deny = (Rbac::requirePermissions(["cadastros.$entity.read"]))($r);
        if ($deny instanceof Response) return $deny;
        $cfg = $this->cfg($r); if (!$cfg) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $id = (int)($r->get('params', [])[1] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $pdo = ConnectionManager::forTenant();
        $cols = implode(',', $cfg['list']);
        $stmt = $pdo->prepare("SELECT $cols FROM {$cfg['table']} WHERE {$cfg['pk']} = ? LIMIT 1");
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['message' => 'Registro não encontrado']]);
        return Response::json(['data' => $row]);
    }

    public function create(Request $r) {
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $deny = (Rbac::requirePermissions(["cadastros.$entity.create"]))($r);
        if ($deny instanceof Response) return $deny;
        $cfg = $this->cfg($r); if (!$cfg) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $pdo = ConnectionManager::forTenant();
        // Suporta JSON e multipart/form-data
        $data = is_array($r->body) ? $r->body : [];
        foreach ($_POST as $k=>$v) { $data[$k] = $v; }
        foreach ($cfg['required'] as $req) { if (!isset($data[$req]) || trim((string)$data[$req]) === '') return new Response(422, ['error' => ['message' => "Campo obrigatório: $req"]]); }
        // validações simples
        if (isset($data['email']) && $data['email'] !== '' && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) return new Response(422, ['error' => ['message' => 'E-mail inválido']]);
        // Herança e validações de vínculo para vendedores
        if ($this->paramEntity($r) === 'vendedores') {
            $pdo = ConnectionManager::forTenant();
            // Validar empresa obrigatória
            $empresaId = isset($data['empresa_id']) ? (int)$data['empresa_id'] : 0;
            if ($empresaId <= 0) return new Response(422, ['error' => ['message' => 'Empresa é obrigatória para vendedor']]);
            $chkEmp = $pdo->prepare('SELECT id FROM empresas WHERE id = ?'); $chkEmp->execute([$empresaId]); if (!$chkEmp->fetch()) return new Response(422, ['error' => ['message' => 'Empresa inválida']]);
            // Se vinculado a usuário, herdar name/email se ausentes
            $uid = isset($data['user_id']) ? (int)$data['user_id'] : 0;
            if ($uid > 0) {
                try {
                    $u = $pdo->prepare('SELECT id, name, email, enabled FROM users WHERE id = ? LIMIT 1');
                    $u->execute([$uid]);
                    $user = $u->fetch();
                    if (!$user) { return new Response(422, ['error' => ['message' => 'Usuário não encontrado']]); }
                    if (isset($user['enabled']) && (int)$user['enabled'] === 0) { return new Response(422, ['error' => ['message' => 'Usuário inativo não pode ser vinculado']]); }
                    // Impedir duplicidade: um usuário não pode estar vinculado a mais de um vendedor
                    $dup = $pdo->prepare('SELECT id FROM vendedores WHERE user_id = ? LIMIT 1');
                    $dup->execute([$uid]);
                    if ($dup->fetch()) { return new Response(409, ['error' => ['message' => 'Usuário já vinculado a um vendedor']]); }
                    if ($user) {
                        if ((!isset($data['nome']) || trim((string)$data['nome']) === '') && isset($user['name'])) $data['nome'] = (string)$user['name'];
                        if ((!isset($data['email']) || trim((string)$data['email']) === '') && isset($user['email'])) $data['email'] = (string)$user['email'];
                    }
                } catch (\Throwable $e) { /* tolerar falha */ }
            }
            // Se vinculado a cliente, herdar nome/email/telefone se ausentes
            $cid = isset($data['cliente_id']) ? (int)$data['cliente_id'] : 0;
            if ($cid > 0) {
                try {
                    $c = $pdo->prepare('SELECT id, nome, email, telefone_celular, telefone_fixo FROM clients WHERE id = ? LIMIT 1');
                    $c->execute([$cid]);
                    $cli = $c->fetch();
                    if (!$cli) { return new Response(422, ['error' => ['message' => 'Cliente não encontrado']]); }
                    if ($cli) {
                        if ((!isset($data['nome']) || trim((string)$data['nome']) === '') && isset($cli['nome'])) $data['nome'] = (string)$cli['nome'];
                        if ((!isset($data['email']) || trim((string)$data['email']) === '') && isset($cli['email'])) $data['email'] = (string)$cli['email'];
                        if ((!isset($data['telefone']) || trim((string)$data['telefone']) === '')) {
                            $tel = (string)($cli['telefone_celular'] ?? $cli['telefone_fixo'] ?? '');
                            if ($tel !== '') $data['telefone'] = $tel;
                        }
                    }
                } catch (\Throwable $e) { /* tolerar falha */ }
            }
            // Validar comissao_percent (0 a 100)
            if (isset($data['comissao_percent']) && trim((string)$data['comissao_percent']) !== '') {
                $cp = (float)$data['comissao_percent'];
                if ($cp < 0 || $cp > 100) return new Response(422, ['error' => ['message' => 'Comissão deve estar entre 0 e 100%']]);
            }
        }
        $cols = []; $vals = []; $ph = [];
        foreach ($cfg['fields'] as $f) { if (array_key_exists($f, $data)) { $cols[] = $f; $vals[] = $data[$f]; $ph[] = '?'; } }
        $sql = "INSERT INTO {$cfg['table']} (" . implode(',', $cols) . ") VALUES (" . implode(',', $ph) . ")";
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($vals);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => ['message' => 'Falha ao criar registro', 'detail' => $e->getMessage()]]);
        }
        $id = (int)$pdo->lastInsertId();
        // Upload de logo para empresas após inserir
        if ($entity === 'empresas' && isset($_FILES['logo_file']) && is_array($_FILES['logo_file']) && (int)$_FILES['logo_file']['error'] === UPLOAD_ERR_OK) {
            try {
                $tmp = $_FILES['logo_file']['tmp_name'];
                $name = $_FILES['logo_file']['name'];
                $size = (int)$_FILES['logo_file']['size'];
                $type = (string)($_FILES['logo_file']['type'] ?? '');
                if ($size > 5*1024*1024) return new Response(413, ['error' => ['message' => 'Logo acima de 5MB']]);
                $allowed = ['image/png','image/jpeg','image/jpg','image/webp'];
                if ($type && !in_array(strtolower($type), $allowed, true)) return new Response(415, ['error' => ['message' => 'Tipo de imagem não suportado']]);
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION) ?: 'png');
                $root = dirname(__DIR__, 3);
                $dir = $root . '/public/uploads/companies';
                if (!is_dir($dir)) @mkdir($dir, 0775, true);
                $safeExt = in_array($ext, ['png','jpg','jpeg','webp'], true) ? $ext : 'png';
                $fileName = 'company_' . $id . '_logo.' . $safeExt;
                $target = $dir . '/' . $fileName;
                if (!move_uploaded_file($tmp, $target)) throw new \RuntimeException('Falha ao mover upload');
                $url = '/uploads/companies/' . $fileName;
                $upd = $pdo->prepare('UPDATE empresas SET logo_url = ? WHERE id = ?');
                $upd->execute([$url, $id]);
            } catch (\Throwable $e) {
                // não bloqueia criação; apenas informa
                return new Response(201, ['data' => ['id' => $id], 'warning' => ['message' => 'Registro criado; falha no upload de logo']]);
            }
        }
        return new Response(201, ['data' => ['id' => $id]]);
    }

    public function update(Request $r) {
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $deny = (Rbac::requirePermissions(["cadastros.$entity.update"]))($r);
        if ($deny instanceof Response) return $deny;
        $cfg = $this->cfg($r); if (!$cfg) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $id = (int)($r->get('params', [])[1] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $pdo = ConnectionManager::forTenant();
        // Suporta JSON e multipart/form-data
        $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']]);
        // Herança de propriedades para vendedores na atualização (preencher ausentes)
        if ($this->paramEntity($r) === 'vendedores') {
            $pdo = ConnectionManager::forTenant();
            // Validar empresa obrigatória
            $empresaId = isset($data['empresa_id']) ? (int)$data['empresa_id'] : 0;
            if ($empresaId <= 0) return new Response(422, ['error' => ['message' => 'Empresa é obrigatória para vendedor']]);
            $chkEmp = $pdo->prepare('SELECT id FROM empresas WHERE id = ?'); $chkEmp->execute([$empresaId]); if (!$chkEmp->fetch()) return new Response(422, ['error' => ['message' => 'Empresa inválida']]);
            $uid = isset($data['user_id']) ? (int)$data['user_id'] : 0;
            if ($uid > 0) {
                try {
                    $u = $pdo->prepare('SELECT id, name, email, enabled FROM users WHERE id = ? LIMIT 1');
                    $u->execute([$uid]);
                    $user = $u->fetch();
                    if (!$user) { return new Response(422, ['error' => ['message' => 'Usuário não encontrado']]); }
                    if (isset($user['enabled']) && (int)$user['enabled'] === 0) { return new Response(422, ['error' => ['message' => 'Usuário inativo não pode ser vinculado']]); }
                    // Impedir duplicidade: um usuário não pode estar vinculado a mais de um vendedor
                    $dup = $pdo->prepare('SELECT id FROM vendedores WHERE user_id = ? AND id <> ? LIMIT 1');
                    $dup->execute([$uid, $id]);
                    if ($dup->fetch()) { return new Response(409, ['error' => ['message' => 'Usuário já vinculado a outro vendedor']]); }
                    if ($user) {
                        if (!array_key_exists('nome', $data) || trim((string)$data['nome']) === '') $data['nome'] = (string)($user['name'] ?? $data['nome'] ?? '');
                        if (!array_key_exists('email', $data) || trim((string)$data['email']) === '') $data['email'] = (string)($user['email'] ?? $data['email'] ?? '');
                    }
                } catch (\Throwable $e) { /* tolerar falha */ }
            }
            $cid = isset($data['cliente_id']) ? (int)$data['cliente_id'] : 0;
            if ($cid > 0) {
                try {
                    $c = $pdo->prepare('SELECT id, nome, email, telefone_celular, telefone_fixo FROM clients WHERE id = ? LIMIT 1');
                    $c->execute([$cid]);
                    $cli = $c->fetch();
                    if (!$cli) { return new Response(422, ['error' => ['message' => 'Cliente não encontrado']]); }
                    if ($cli) {
                        if (!array_key_exists('nome', $data) || trim((string)$data['nome']) === '') $data['nome'] = (string)($cli['nome'] ?? $data['nome'] ?? '');
                        if (!array_key_exists('email', $data) || trim((string)$data['email']) === '') $data['email'] = (string)($cli['email'] ?? $data['email'] ?? '');
                        if (!array_key_exists('telefone', $data) || trim((string)$data['telefone']) === '') {
                            $tel = (string)($cli['telefone_celular'] ?? $cli['telefone_fixo'] ?? '');
                            if ($tel !== '') $data['telefone'] = $tel;
                        }
                    }
                } catch (\Throwable $e) { /* tolerar falha */ }
            }
            // Validar comissao_percent (0 a 100)
            if (isset($data['comissao_percent']) && trim((string)$data['comissao_percent']) !== '') {
                $cp = (float)$data['comissao_percent'];
                if ($cp < 0 || $cp > 100) return new Response(422, ['error' => ['message' => 'Comissão deve estar entre 0 e 100%']]);
            }
        }
        $sets = []; $vals = [];
        foreach ($cfg['fields'] as $f) { if (array_key_exists($f, $data)) { $sets[] = "$f = ?"; $vals[] = $data[$f]; } }
        if (!$sets) return new Response(422, ['error' => ['message' => 'Nenhuma alteração enviada']]);
        $vals[] = $id;
        $sql = "UPDATE {$cfg['table']} SET " . implode(',', $sets) . " WHERE {$cfg['pk']} = ?";
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($vals);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => ['message' => 'Falha ao atualizar registro', 'detail' => $e->getMessage()]]);
        }
        // Upload de logo para empresas
        if ($entity === 'empresas' && isset($_FILES['logo_file']) && is_array($_FILES['logo_file']) && (int)$_FILES['logo_file']['error'] === UPLOAD_ERR_OK) {
            try {
                $tmp = $_FILES['logo_file']['tmp_name'];
                $name = $_FILES['logo_file']['name'];
                $size = (int)$_FILES['logo_file']['size'];
                $type = (string)($_FILES['logo_file']['type'] ?? '');
                if ($size > 5*1024*1024) return new Response(413, ['error' => ['message' => 'Logo acima de 5MB']]);
                $allowed = ['image/png','image/jpeg','image/jpg','image/webp'];
                if ($type && !in_array(strtolower($type), $allowed, true)) return new Response(415, ['error' => ['message' => 'Tipo de imagem não suportado']]);
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION) ?: 'png');
                $root = dirname(__DIR__, 3);
                $dir = $root . '/public/uploads/companies';
                if (!is_dir($dir)) @mkdir($dir, 0775, true);
                $safeExt = in_array($ext, ['png','jpg','jpeg','webp'], true) ? $ext : 'png';
                $fileName = 'company_' . $id . '_logo.' . $safeExt;
                $target = $dir . '/' . $fileName;
                if (!move_uploaded_file($tmp, $target)) throw new \RuntimeException('Falha ao mover upload');
                $url = '/uploads/companies/' . $fileName;
                $upd = $pdo->prepare('UPDATE empresas SET logo_url = ? WHERE id = ?');
                $upd->execute([$url, $id]);
            } catch (\Throwable $e) {
                return Response::json(['data' => ['id' => $id], 'warning' => ['message' => 'Atualização concluída; falha no upload de logo']]);
            }
        }
        return Response::json(['data' => ['id' => $id]]);
    }

    public function delete(Request $r) {
        $entity = $this->paramEntity($r);
        if (!$entity) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $deny = (Rbac::requirePermissions(["cadastros.$entity.delete"]))($r);
        if ($deny instanceof Response) return $deny;
        $cfg = $this->cfg($r); if (!$cfg) return new Response(404, ['error' => ['message' => 'Entidade inválida']]);
        $id = (int)($r->get('params', [])[1] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['message' => 'ID inválido']]);
        $pdo = ConnectionManager::forTenant();
        try {
            $stmt = $pdo->prepare("DELETE FROM {$cfg['table']} WHERE {$cfg['pk']} = ?");
            $stmt->execute([$id]);
        } 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, '');
    }

    private function paramEntity(Request $r): ?string {
        $params = $r->get('params', []);
        $entity = (string)($params[0] ?? '');
        return isset($this->entities[$entity]) ? $entity : null;
    }
    private function cfg(Request $r): ?array {
        $entity = $this->paramEntity($r);
        return $entity ? $this->entities[$entity] : null;
    }
}