<?php
namespace Modules\SettingsUI\Controllers;

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

class SettingsController {
    private function ensureSystemSettingsTable(\PDO $pdo): void {
        $pdo->exec("CREATE TABLE IF NOT EXISTS system_settings (
            id INT AUTO_INCREMENT PRIMARY KEY,
            currency VARCHAR(10) NULL,
            timezone VARCHAR(50) NULL,
            date_format VARCHAR(20) NULL,
            show_tips TINYINT(1) NOT NULL DEFAULT 1,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        ) ENGINE=InnoDB");
    }

    public function get(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $this->ensureSystemSettingsTable($pdo);
        $stmt = $pdo->query('SELECT currency, timezone, date_format, show_tips FROM system_settings ORDER BY id DESC LIMIT 1');
        $row = $stmt->fetch();
        return Response::json([ 'settings' => [
            'currency' => (string)($row['currency'] ?? 'BRL'),
            'timezone' => (string)($row['timezone'] ?? 'America/Sao_Paulo'),
            'date_format' => (string)($row['date_format'] ?? 'd/m/Y'),
            'show_tips' => (bool)($row['show_tips'] ?? true)
        ]]);
    }

    public function put(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $this->ensureSystemSettingsTable($pdo);
        $body = $r->body;
        $currency = strtoupper(trim((string)($body['currency'] ?? 'BRL')));
        $timezone = (string)($body['timezone'] ?? 'America/Sao_Paulo');
        $dateFormat = (string)($body['date_format'] ?? 'd/m/Y');
        $showTips = isset($body['show_tips']) ? (int)!!$body['show_tips'] : 1;

        // Validações básicas
        if (!preg_match('/^[A-Z]{3}$/', $currency)) {
            return new Response(422, ['error' => 'Moeda inválida (use código ISO de 3 letras, ex.: BRL)']);
        }
        $tzList = \DateTimeZone::listIdentifiers();
        if (!in_array($timezone, $tzList, true)) {
            return new Response(422, ['error' => 'Fuso horário inválido']);
        }
        if ($dateFormat === '' || strlen($dateFormat) > 20) {
            return new Response(422, ['error' => 'Formato de data inválido']);
        }
        $stmt = $pdo->prepare('INSERT INTO system_settings (currency, timezone, date_format, show_tips) VALUES (?,?,?,?)');
        $stmt->execute([$currency, $timezone, $dateFormat, $showTips]);
        return Response::json(['ok' => true]);
    }

    public function getCompany(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        // Usa primeira empresa como perfil da companhia do tenant
        $stmt = $pdo->query('SELECT id, nome, razao_social, cnpj, ie, im, email, telefone, site_url,
            logradouro, numero, complemento, bairro, cidade, estado, cep, pais,
            cert_type, cert_serial, cert_filename, cert_url
            FROM empresas ORDER BY id ASC LIMIT 1');
        $row = $stmt->fetch();
        if (!$row) {
            return Response::json(['company' => [
                'id' => null,
                'nome' => '',
                'razao_social' => '',
                'cnpj' => '',
                'ie' => '',
                'im' => '',
                'email' => '',
                'telefone' => '',
                'site_url' => '',
                'logradouro' => '',
                'numero' => '',
                'complemento' => '',
                'bairro' => '',
                'cidade' => '',
                'estado' => '',
                'cep' => '',
                'pais' => '',
                'cert_type' => '',
                'cert_serial' => '',
                'cert_filename' => '',
                'cert_url' => ''
            ]]);
        }
        return Response::json(['company' => $row]);
    }

    public function putCompany(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $data = $r->body;
        // validações simples
        if (!empty($data['email']) && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            return Response::json(['error' => 'E-mail inválido'], 422);
        }
        // Campos obrigatórios de endereço
        $required = ['logradouro','numero','bairro','cidade','estado','cep','pais','nome','razao_social','cnpj'];
        foreach ($required as $f) {
            if (!isset($data[$f]) || trim((string)$data[$f]) === '') {
                return Response::json(['error' => "Campo obrigatório ausente: $f"], 422);
            }
        }
        // Normalizações e validações BR
        $data['estado'] = strtoupper(substr((string)$data['estado'], 0, 2));
        if (!Br::isCNPJ($data['cnpj'] ?? null)) { return Response::json(['error' => 'CNPJ inválido'], 422); }
        if (!Br::isCEP($data['cep'] ?? null)) { return Response::json(['error' => 'CEP inválido'], 422); }
        // Criptografia da senha do certificado (quando fornecida)
        $encCert = null;
        $certPassword = isset($data['cert_password']) ? (string)$data['cert_password'] : '';
        if ($certPassword !== '') {
            try {
                $encCert = Crypto::encryptJson(['kind' => 'company_cert_pwd', 'password' => $certPassword]);
            } catch (\Throwable $e) {
                return Response::json(['error' => 'Falha ao criptografar senha do certificado'], 500);
            }
            // Não persistir senha em texto puro
            unset($data['cert_password']);
        }
        $select = $pdo->query('SELECT id FROM empresas ORDER BY id ASC LIMIT 1');
        $existing = $select->fetch();
        if ($existing) {
            $id = (int)$existing['id'];
            $sets = [];
            $vals = [];
            $fields = ['nome','razao_social','cnpj','ie','im','email','telefone','site_url',
                       'logradouro','numero','complemento','bairro','cidade','estado','cep','pais',
                       'cert_type','cert_serial'];
            foreach ($fields as $f) { if (array_key_exists($f, $data)) { $sets[] = "$f = ?"; $vals[] = $data[$f]; } }
            if ($encCert) {
                $sets[] = 'cert_password_enc = ?'; $vals[] = $encCert['cipher'];
                $sets[] = 'cert_password_iv = ?'; $vals[] = $encCert['iv'];
                $sets[] = 'cert_password_tag = ?'; $vals[] = $encCert['tag'];
                $sets[] = 'cert_kdf_salt = ?'; $vals[] = $encCert['salt'];
                // Limpar coluna legada se existir
                $sets[] = 'cert_password = NULL';
            }
            if (!$sets) { return Response::json(['error' => 'Nenhuma alteração enviada'], 422); }
            $vals[] = $id;
            $sql = 'UPDATE empresas SET ' . implode(',', $sets) . ' WHERE id = ?';
            $stmt = $pdo->prepare($sql);
            $stmt->execute($vals);
            // Validação de integridade do certificado A1, se aplicável
            if (($data['cert_type'] ?? '') === 'A1' && $encCert) {
                try {
                    $row = $pdo->query('SELECT cert_filename FROM empresas WHERE id = ' . $id)->fetch();
                    $fname = (string)($row['cert_filename'] ?? '');
                    if ($fname !== '') {
                        $path = __DIR__ . '/../../../public/uploads/company/certs/' . $id . '/' . $fname;
                        $blob = is_file($path) ? file_get_contents($path) : false;
                        if ($blob === false) { return Response::json(['error' => ['code' => 'cert_not_found','message' => 'Arquivo do certificado não encontrado']], 422); }
                        $certs = [];
                        $ok = openssl_pkcs12_read($blob, $certs, $certPassword);
                        if (!$ok) { return Response::json(['error' => ['code' => 'cert_invalid','message' => 'Certificado A1 ou senha inválidos']], 422); }
                    }
                } catch (\Throwable $e) {
                    return Response::json(['error' => ['code' => 'cert_check_failed','message' => 'Falha ao validar certificado']], 500);
                }
            }
            // Auditoria
            try { AuditLogger::logUpdate('settings_company', $id, [], array_merge($data, ['user_id' => (int)($r->user['id'] ?? 0)])); } catch (\Throwable $e) {}
            return Response::json(['company' => ['id' => $id]]);
        } else {
            $fields = ['nome','razao_social','cnpj','ie','im','email','telefone','site_url',
                       'logradouro','numero','complemento','bairro','cidade','estado','cep','pais',
                       'cert_type','cert_serial'];
            $cols = [];$ph=[];$vals=[];
            foreach ($fields as $f) { if (array_key_exists($f, $data)) { $cols[] = $f; $ph[] = '?'; $vals[] = $data[$f]; } }
            if (!$cols) { return Response::json(['error' => 'Campos obrigatórios ausentes'], 422); }
            $sql = 'INSERT INTO empresas (' . implode(',', $cols) . ') VALUES (' . implode(',', $ph) . ')';
            $stmt = $pdo->prepare($sql); $stmt->execute($vals);
            $id = (int)$pdo->lastInsertId();
            if ($encCert) {
                $up = $pdo->prepare('UPDATE empresas SET cert_password_enc = ?, cert_password_iv = ?, cert_password_tag = ?, cert_kdf_salt = ?, cert_password = NULL WHERE id = ?');
                $up->execute([$encCert['cipher'],$encCert['iv'],$encCert['tag'],$encCert['salt'],$id]);
            }
            try { AuditLogger::logCreate('settings_company', $id, array_merge($data, ['user_id' => (int)($r->user['id'] ?? 0)])); } catch (\Throwable $e) {}
            return new Response(201, ['company' => ['id' => $id]]);
        }
    }

    public function uploadCompanyCert(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        // Garantir que existe uma empresa para vincular
        $select = $pdo->query('SELECT id FROM empresas ORDER BY id ASC LIMIT 1');
        $existing = $select->fetch();
        if (!$existing) return Response::json(['error' => ['code' => 'no_company', 'message' => 'Nenhuma empresa cadastrada']], 422);
        $companyId = (int)$existing['id'];

        $file = $r->files['file'] ?? null;
        if (!$file || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            return Response::json(['error' => ['code' => 'file_required', 'message' => 'Arquivo do certificado é obrigatório']], 422);
        }
        $size = (int)($file['size'] ?? 0);
        $name = (string)($file['name'] ?? '');
        $tmp = (string)($file['tmp_name'] ?? '');
        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
        $allowed = ['pfx','p12'];
        if (!in_array($ext, $allowed, true)) return Response::json(['error' => ['code' => 'invalid_ext', 'message' => 'Apenas .pfx/.p12 são aceitos']], 422);
        if ($size > (10 * 1024 * 1024)) return Response::json(['error' => ['code' => 'file_too_large', 'message' => 'Arquivo excede 10MB']], 413);

        $uploadDir = __DIR__ . '/../../../public/uploads/company/certs/' . $companyId;
        if (!is_dir($uploadDir)) @mkdir($uploadDir, 0775, true);
        $destName = 'cert_' . $companyId . '_' . date('Ymd_His') . '.' . $ext;
        $dest = $uploadDir . '/' . $destName;
        if (!@move_uploaded_file($tmp, $dest)) return Response::json(['error' => ['code' => 'upload_failed', 'message' => 'Falha ao salvar certificado']], 500);
        $url = '/uploads/company/certs/' . $companyId . '/' . $destName;

        $up = $pdo->prepare('UPDATE empresas SET cert_filename = ?, cert_url = ? WHERE id = ?');
        $up->execute([$destName, $url, $companyId]);

        return Response::json(['ok' => true, 'company_id' => $companyId, 'url' => $url]);
    }
}