<?php
declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}
require_once dirname(__DIR__) . '/includes/whatsapp_helper.php';
require_once dirname(__DIR__) . '/api/_device_access.php';
require_once dirname(__DIR__) . '/api/_auth_common.php';
require_once dirname(__DIR__) . '/api/_multiserver.php';
require_once dirname(__DIR__) . '/api/_trial_service.php';
require_once dirname(__DIR__) . '/api/_app_guard.php';
require_once dirname(__DIR__) . '/includes/profile_pairing.php';

$dbPath = dirname(__DIR__) . "/api/db.db";

try {
    $db = new PDO("sqlite:" . $dbPath, null, null, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
} catch (Throwable $e) {
    die("Erro SQLite");
}
try {
    $db->exec('PRAGMA busy_timeout = 30000');
    $db->exec('PRAGMA journal_mode = WAL');
    $db->exec('PRAGMA synchronous = NORMAL');
} catch (Throwable $e) {}

require_once dirname(__DIR__) . '/includes/revenda_schema.php';
revenda_ensure_schema($db);
ms_ensure_schema($db);
pp_ensure_schema($db);
auth_ensure_schema($db);

/* ============================================================
 * APP API ACTIONS (sem sessão, autenticadas via app_token)
 *  - Os apps de TV e Celular consultam users.php?api_action=...
 *  - mac_lookup       : retorna DNS/usuário/senha/vencimento por MAC
 *  - device_pulse     : retorna mudanças instantâneas (<5s) do
 *                       Controle de Acesso vinculadas a um MAC
 *  - device_pulse + push: registra dispositivo (tv/mobile) online
 * ============================================================ */
$apiAction = strtolower(trim((string)($_REQUEST['api_action'] ?? '')));
if ($apiAction !== '') {
    if (empty($_REQUEST['unified_api']) && function_exists('app_guard_validate')) {
        app_guard_validate();
    }
    auth_ensure_schema($db);

    $appMac = auth_normalize_mac((string)($_REQUEST['mac'] ?? ''));
    $appDevice = da_normalize_device_type((string)($_REQUEST['device_type'] ?? 'tv'));
    $appDeviceMeta = [
        'nome_cliente' => trim((string)($_REQUEST['nome_cliente'] ?? $_REQUEST['name'] ?? '')),
        'whatsapp' => trim((string)($_REQUEST['whatsapp'] ?? $_REQUEST['phone'] ?? '')),
        'device_name' => trim((string)($_REQUEST['device_name'] ?? '')),
        'device_brand' => trim((string)($_REQUEST['device_brand'] ?? '')),
        'device_model' => trim((string)($_REQUEST['device_model'] ?? '')),
        'android_version' => trim((string)($_REQUEST['android_version'] ?? '')),
        'app_version' => trim((string)($_REQUEST['app_version'] ?? '')),
        'android_id' => trim((string)($_REQUEST['android_id'] ?? '')),
    ];
    $appPendingReason = trim((string)($_REQUEST['reason'] ?? $_REQUEST['pending_reason'] ?? ''));
    if ($appMac === '' || strlen(str_replace(':', '', $appMac)) !== 12) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['status' => 'error', 'message' => 'mac_required'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($apiAction === 'pairing_create') {
        header('Content-Type: application/json; charset=utf-8');
        try {
            $pair = pp_create_pairing_code($db, $appMac, $appDevice);
            echo json_encode([
                'status' => 'ok',
                'code' => (string)$pair['code'],
                'mac' => (string)$pair['mac'],
                'device_type' => (string)$pair['device_type'],
                'expires_at' => (int)$pair['expires_at'],
                'expires_in' => max(0, (int)$pair['expires_at'] - time()),
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        } catch (Throwable $e) {
            echo json_encode(['status'=>'error','message'=>$e->getMessage()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        exit;
    }

    if ($apiAction === 'pairing_status') {
        header('Content-Type: application/json; charset=utf-8');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        $pair = pp_pairing_status($db, $appMac, (string)($_REQUEST['code'] ?? ''));
        $pairStatus = (string)($pair['status'] ?? 'not_found');
        $payload = [
            'status' => $pairStatus,
            'claimed' => $pairStatus === 'claimed',
            'access_ready' => false,
            'revendedor_id' => (int)($pair['revendedor_id'] ?? 0),
            'expires_at' => (int)($pair['expires_at'] ?? 0),
            'mac' => $appMac,
        ];
        if ($pairStatus === 'claimed') {
            $stPairUser = $db->prepare('SELECT * FROM usuarios WHERE mac=? ORDER BY rowid DESC LIMIT 1');
            $stPairUser->execute([$appMac]);
            $pairUser = $stPairUser->fetch(PDO::FETCH_ASSOC) ?: null;
            $pairServers = $pairUser ? ms_get_servers($db, $appMac, $pairUser) : [];
            $pairPrimary = $pairServers[0] ?? null;
            $pairDns = $pairPrimary
                ? ms_normalize_dns((string)($pairPrimary['dns'] ?? ''))
                : auth_normalize_dns((string)($pairUser['dns'] ?? ''));
            $pairUsername = trim((string)($pairPrimary['username'] ?? $pairUser['username'] ?? ''));
            $pairPassword = (string)($pairPrimary['password'] ?? $pairUser['password'] ?? '');
            $pairReady = $pairUser !== null && $pairDns !== '' && $pairUsername !== '' && trim($pairPassword) !== '';
            $pairOwner = (int)($pairUser['usuario_id'] ?? $pair['revendedor_id'] ?? 0);
            $pairDnsList = [];
            foreach ($pairServers as $server) {
                $serverDns = ms_normalize_dns((string)($server['dns'] ?? ''));
                if ($serverDns !== '' && !in_array($serverDns, $pairDnsList, true)) $pairDnsList[] = $serverDns;
            }
            if ($pairDns !== '' && !in_array($pairDns, $pairDnsList, true)) array_unshift($pairDnsList, $pairDns);
            $payload = array_merge($payload, auth_branding_payload($db, $pairUser ?: ['usuario_id' => $pairOwner]), [
                'login_status' => $pairReady ? 'ok' : 'waiting',
                'access_ready' => $pairReady,
                'mac_registered' => $pairUser !== null,
                'mac_pending' => !$pairReady,
                'mac_has_credentials' => $pairReady,
                'dns' => $pairDns,
                'dns_list' => array_values($pairDnsList),
                'username' => $pairUsername,
                'password' => $pairPassword,
                'nome_cliente' => (string)($pairUser['nome_cliente'] ?? ''),
                'whatsapp' => (string)($pairUser['whatsapp'] ?? ''),
                'vencimento' => (string)($pairUser['vencimento'] ?? ''),
                'revendedor_id' => $pairOwner,
                'owner_id' => $pairOwner,
                'usuario_id' => $pairOwner,
                'servers' => $pairServers,
                'server_count' => count($pairServers),
                'multi_server_enabled' => count($pairServers) > 1,
                'message' => $pairReady ? 'Acesso liberado.' : 'Código confirmado. Aguardando DNS, usuário e senha.',
            ]);
        }
        echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($apiAction === 'pairing_cancel') {
        header('Content-Type: application/json; charset=utf-8');
        $cancelled = pp_cancel_pairing($db, $appMac, (string)($_REQUEST['code'] ?? ''));
        echo json_encode(['status'=>$cancelled ? 'cancelled' : 'not_found','cancelled'=>$cancelled], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($apiAction === 'pairing_complete') {
        header('Content-Type: application/json; charset=utf-8');
        $completed = pp_complete_pairing($db, $appMac, (string)($_REQUEST['code'] ?? ''));
        echo json_encode(['status'=>$completed ? 'completed' : 'not_found','completed'=>$completed], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    $appSessionCheck = ((string)($_REQUEST['session_check'] ?? $_REQUEST['logged_session'] ?? '') === '1');
    $appPanelLogin = ((string)($_REQUEST['panel_login'] ?? $_REQUEST['fast_panel_login'] ?? '') === '1');
    $formatPanelVencimento = function ($value): string {
        $raw = trim((string)$value);
        if ($raw === '' || $raw === '0') return '';
        $ts = auth_parse_vencimento_to_timestamp($raw);
        if ($ts > 0) return auth_format_exp($ts);
        return $raw;
    };

    $ensureBlockConfig = function () use ($db): void {
        $db->exec("CREATE TABLE IF NOT EXISTS block_config (id INTEGER PRIMARY KEY CHECK (id=1), title TEXT NOT NULL DEFAULT 'Acesso bloqueado', message TEXT NOT NULL DEFAULT 'Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.', show_qrcode INTEGER NOT NULL DEFAULT 1, updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')) )");
        $db->exec("INSERT OR IGNORE INTO block_config (id,title,message,show_qrcode,updated_at) VALUES (1,'Acesso bloqueado','Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.',1,strftime('%s','now'))");
    };
    $blockPublicBase = function (): string {
        $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
        $scheme = $https ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        $script = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? ''));
        $base = preg_replace('#/api/app\.php$#', '', $script);
        $base = preg_replace('#/telas/users\.php$#', '', $base);
        $base = preg_replace('#/users\.php$#', '', $base);
        return $scheme . '://' . $host . rtrim((string)$base, '/');
    };
    $getBlockConfig = function (?array $userRow = null) use ($db, $ensureBlockConfig, $blockPublicBase): array {
        try { $ensureBlockConfig(); } catch (Throwable $e) {}
        $cfg = [];
        try { $cfg = $db->query('SELECT * FROM block_config WHERE id=1 LIMIT 1')->fetch(PDO::FETCH_ASSOC) ?: []; } catch (Throwable $e) {}
        $branding = function_exists('auth_branding_payload') ? auth_branding_payload($db, $userRow ?: []) : [];
        $qrPath = dirname(__DIR__) . '/api/img/qrcode.png';
        $qrUrl = (string)($branding['qrcode_url'] ?? '');
        if ($qrUrl === '' && is_file($qrPath)) $qrUrl = rtrim($blockPublicBase(), '/') . '/api/img/qrcode.png?v=' . filemtime($qrPath);
        $payload = (string)($branding['qrcode_payload'] ?? '');
        if ($payload === '') $payload = $qrUrl;
        $maintenance = pp_maintenance_payload($db);
        return array_merge($branding, [
            'block_title' => trim((string)($cfg['title'] ?? 'Acesso bloqueado')) ?: 'Acesso bloqueado',
            'block_message' => trim((string)($cfg['message'] ?? 'Seu acesso está bloqueado ou vencido.')),
            'block_show_qrcode' => ((int)($cfg['show_qrcode'] ?? 0) === 1),
            'block_qrcode_url' => $qrUrl,
            'block_qrcode_payload' => $payload,
            'block_background' => (string)($maintenance['maintenance_background'] ?? ''),
            'block_updated_at' => (int)($cfg['updated_at'] ?? 0),
        ], $maintenance);
    };
    $getBrandingConfig = static function (?array $userRow = null) use ($db): array {
        return function_exists('auth_branding_payload') ? auth_branding_payload($db, $userRow ?: []) : [];
    };
    $panelVencExpired = function ($value): bool { $raw=trim((string)$value); if($raw===''||$raw==='0') return false; $ts=auth_parse_vencimento_to_timestamp($raw); return $ts>0 && $ts<time(); };

    $maintenanceNow = pp_maintenance_payload($db);
    if (!empty($maintenanceNow['maintenance_enabled']) && in_array($apiAction, ['mac_lookup', 'device_pulse'], true)) {
        $stMaintenance = $db->prepare('SELECT * FROM usuarios WHERE mac=? ORDER BY rowid DESC LIMIT 1');
        $stMaintenance->execute([$appMac]);
        $maintenanceUser = $stMaintenance->fetch(PDO::FETCH_ASSOC) ?: [];
        $maintenanceServers = $maintenanceUser ? ms_payload($db, $appMac, $maintenanceUser) : ['servers'=>[], 'server_count'=>0];
        echo json_encode(array_merge([
            'status' => 'ok',
            'mac_registered' => !empty($maintenanceUser),
            'mac_pending' => empty($maintenanceUser) || !auth_user_has_credentials($maintenanceUser),
            'mac_has_credentials' => !empty($maintenanceUser) && auth_user_has_credentials($maintenanceUser),
            'logout' => false,
            'blocked' => true,
            'block_reason' => 'maintenance',
            'message' => (string)($maintenanceNow['maintenance_message'] ?? ''),
            'dns' => (string)($maintenanceUser['dns'] ?? ''),
            'username' => (string)($maintenanceUser['username'] ?? ''),
            'password' => (string)($maintenanceUser['password'] ?? ''),
            'vencimento' => $formatPanelVencimento($maintenanceUser['vencimento'] ?? ''),
            'nome_cliente' => (string)($maintenanceUser['nome_cliente'] ?? ''),
            'whatsapp' => (string)($maintenanceUser['whatsapp'] ?? ''),
            'server_ts' => time(),
        ], pp_profile_payload($maintenanceUser), $maintenanceServers, $getBrandingConfig($maintenanceUser ?: null), $getBlockConfig($maintenanceUser ?: null)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if (in_array($apiAction, ['register_pending_device', 'ensure_pending_device', 'device_pending'], true)) {
        header('Content-Type: application/json; charset=utf-8');
        try {
            $rowPending = auth_upsert_pending_device(
                $db,
                $appMac,
                $appDevice,
                $appDeviceMeta,
                false,
                $appPendingReason !== '' ? $appPendingReason : 'login_screen'
            );
            $hasCredentials = $rowPending ? auth_user_has_credentials($rowPending) : false;
            echo json_encode([
                'status' => $hasCredentials ? 'ok' : 'waiting',
                'registered' => (bool)$rowPending,
                'mac_registered' => (bool)$rowPending,
                'mac_pending' => !$hasCredentials,
                'mac_has_credentials' => $hasCredentials,
                'logout' => false,
                'force_login' => !$hasCredentials,
                'mac' => $appMac,
                'message' => $hasCredentials
                    ? 'Aparelho já está ativo no painel.'
                    : 'Aparelho enviado ao painel e aguardando ativação.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        } catch (Throwable $e) {
            echo json_encode([
                'status' => 'error',
                'registered' => false,
                'message' => 'Não foi possível registrar o aparelho pendente.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        exit;
    }

    if (in_array($apiAction, ['clear_mac_credentials', 'clear_credentials', 'logout_clear'], true)) {
        header('Content-Type: application/json; charset=utf-8');
        try {
            $rowClear = auth_upsert_pending_device(
                $db,
                $appMac,
                $appDevice,
                $appDeviceMeta,
                true,
                $appPendingReason !== '' ? $appPendingReason : 'logout'
            );
            try { ms_delete_servers($db, $appMac); } catch (Throwable $e) {}
            try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}

            echo json_encode([
                'status' => 'waiting',
                'cleared' => true,
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'logout' => false,
                'force_login' => true,
                'mac' => $appMac,
                'nome_cliente' => (string)($rowClear['nome_cliente'] ?? ''),
                'whatsapp' => (string)($rowClear['whatsapp'] ?? ''),
                'message' => 'DNS, usuário e senha removidos. Aparelho aguardando nova ativação.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        } catch (Throwable $e) {
            echo json_encode([
                'status' => 'error',
                'cleared' => false,
                'message' => 'Não foi possível colocar o aparelho como pendente.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
    }

    if ($apiAction === 'mac_lookup') {
        header('Content-Type: application/json; charset=utf-8');
        $activeMode = auth_get_active_login_mode($db);
        $testCfg = auth_get_test_config($db);
        $pendingReasonStored = '';
        try {
            $stPendingReason = $db->prepare('SELECT pending_reason FROM device_pending_info WHERE mac=? LIMIT 1');
            $stPendingReason->execute([$appMac]);
            $pendingReasonStored = strtolower(trim((string)($stPendingReason->fetchColumn() ?: '')));
        } catch (Throwable $e) {}
        $manualReactivationReasons = ['user_deleted','mac_not_registered','credentials_removed','app_logout','startup_panel_check','panel_reactivation'];
        $requiresManualReactivation = in_array($pendingReasonStored, $manualReactivationReasons, true);
        $autoMacEnabled = !$requiresManualReactivation
            && $activeMode === 'mac'
            && (int)($testCfg['auto_mac_enabled'] ?? 0) === 1
            && (int)($testCfg['enabled'] ?? 0) === 1;

        $stLookupMac = $db->prepare('SELECT * FROM usuarios WHERE mac = ? ORDER BY rowid DESC LIMIT 1');
        $stLookupMac->execute([$appMac]);
        $row = $stLookupMac->fetch(PDO::FETCH_ASSOC) ?: null;

        // Uma sessão que já estava logada e foi apagada no painel nunca deve
        // cair no auto-teste. Primeiro invalida o acesso e recria apenas o MAC
        // pendente para nova ativação manual.
        if (!$row && $appSessionCheck) {
            $row = auth_upsert_pending_device($db, $appMac, $appDevice, $appDeviceMeta, false, 'user_deleted');
            echo json_encode([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'logout' => true,
                'force_login' => true,
                'reason' => 'user_deleted',
                'message' => 'Registro removido. Aparelho reenviado e aguardando nova ativação.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        if (!$row && $autoMacEnabled) {
            try {
                $trial = gerar_teste_service($db, $appMac, $testCfg, [
                    'nome' => 'Cliente MAC ' . strtoupper(substr(str_replace(':', '', $appMac), -4)),
                    'whatsapp' => '',
                    'device_type' => $appDevice,
                ]);
                if (($trial['status'] ?? '') === 'success') {
                    $stLookupMac->execute([$appMac]);
                    $row = $stLookupMac->fetch(PDO::FETCH_ASSOC) ?: null;
                } else {
                    auth_ensure_pending_mac($db, $appMac, $appDevice);
                    echo json_encode([
                        'status' => 'waiting',
                        'mac_registered' => true,
                        'mac_pending' => true,
                        'mac_has_credentials' => false,
                        'can_auto_trial' => false,
                        'auto_trial_status' => (string)($trial['status'] ?? 'error'),
                        'logout' => false,
                        'message' => (string)($trial['message'] ?? 'Não foi possível gerar o teste automático agora.') . ' Aguardando liberação.'
                    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                    exit;
                }
            } catch (Throwable $e) {
                auth_ensure_pending_mac($db, $appMac, $appDevice);
                echo json_encode([
                    'status' => 'waiting',
                    'mac_registered' => true,
                    'mac_pending' => true,
                    'mac_has_credentials' => false,
                    'can_auto_trial' => false,
                    'logout' => false,
                    'message' => 'Não foi possível gerar o teste automático agora. Aguardando liberação.'
                ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                exit;
            }
        }

        if (!$row) {
            auth_upsert_pending_device($db, $appMac, $appDevice, $appDeviceMeta, false, 'login_waiting');
            echo json_encode([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'can_auto_trial' => false,
                'logout' => false,
                'message' => 'MAC cadastrado. Aguardando DNS, usuário e senha.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        $hasCreds = auth_user_has_credentials($row);
        if (!$hasCreds && $autoMacEnabled) {
            try {
                $trial = gerar_teste_service($db, $appMac, $testCfg, [
                    'nome' => 'Cliente MAC ' . strtoupper(substr(str_replace(':', '', $appMac), -4)),
                    'whatsapp' => '',
                    'device_type' => $appDevice,
                ]);
                if (($trial['status'] ?? '') === 'success') {
                    $stLookupMac->execute([$appMac]);
                    $row = $stLookupMac->fetch(PDO::FETCH_ASSOC) ?: $row;
                    $hasCreds = auth_user_has_credentials($row);
                }
            } catch (Throwable $e) {
            }
        }

        if (!$hasCreds) {
            $row = auth_upsert_pending_device($db, $appMac, $appDevice, $appDeviceMeta, false, 'credentials_removed') ?: $row;
            try { ms_delete_servers($db, $appMac); } catch (Throwable $e) {}
            try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}
            echo json_encode(array_merge([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'can_auto_trial' => $autoMacEnabled,
                'logout' => false,
                'force_login' => true,
                'message' => $autoMacEnabled ? 'Gerando teste automático ou aguardando DNS, usuário e senha.' : 'Aguardando DNS, usuário e senha.'
            ], pp_profile_payload($row), $getBrandingConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        auth_mark_device_active($db, $appMac);
        $multiServerPayload = ms_payload($db, $appMac, $row);
        // Com múltiplos servidores, o app valida o servidor escolhido localmente.
        // Evita bloquear a lista inteira quando apenas um servidor secundário estiver offline.
        if ((int)($multiServerPayload['server_count'] ?? 0) > 1) { $appPanelLogin = true; }

        $statusUser = strtolower(trim((string)($row['status'] ?? '')));
        $panelInactive = in_array($statusUser, ['disabled', 'banned', 'bloqueado', 'inativo'], true);
        $panelVencimentoExpired = $panelVencExpired($row['vencimento'] ?? '');
        if ($panelInactive || $panelVencimentoExpired) {
            $blockCfg = $getBlockConfig($row);
            // Inativação manual continua sendo autoritativa. O vencimento, por
            // outro lado, é tratado pelo modal configurável de /vencimentos.php.
            $panelBlockReason = $panelInactive ? 'inactive' : 'expired';
            echo json_encode(array_merge([
                'status' => 'ok', 'mac_registered' => true, 'mac_pending' => false, 'mac_has_credentials' => true, 'logout' => false,
                'blocked' => true, 'block_reason' => $panelBlockReason,
                'message' => $panelInactive ? 'Acesso bloqueado pelo painel.' : 'Conta vencida no painel.',
                'vencimento' => $formatPanelVencimento($row['vencimento'] ?? ''), 'vencimento_painel' => $formatPanelVencimento($row['vencimento'] ?? ''),
                'dns' => auth_normalize_dns((string)$row['dns']), 'username' => (string)$row['username'], 'password' => (string)$row['password'], 'server_ts' => time()
            ], pp_profile_payload($row), $multiServerPayload, $getBrandingConfig($row), $blockCfg), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        if ($appPanelLogin) {
            $userKey = da_user_key((string)$row['username'], (string)$row['password']);
            $profile = da_get_profile_by_key($db, $userKey);
            $blocked = false;
            $blockReason = '';
            if ($appDevice === 'mobile' && empty($profile['allow_mobile'])) {
                $blocked = true; $blockReason = 'mobile_blocked';
            } elseif ($appDevice === 'tv' && empty($profile['allow_tv'])) {
                $blocked = true; $blockReason = 'tv_blocked';
            }

            $dnsList = auth_get_dns_list($db);
            $dnsUser = auth_normalize_dns((string)$row['dns']);
            if ($dnsUser !== '' && !in_array($dnsUser, $dnsList, true)) array_unshift($dnsList, $dnsUser);
            try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}

            echo json_encode(array_merge([
                'status'              => 'ok',
                'mac_registered'      => true,
                'mac_pending'         => false,
                'mac_has_credentials' => true,
                'logout'              => false,
                'mac'                 => $appMac,
                'dns'                 => $dnsUser,
                'dns_list'            => array_values(array_unique(array_filter($dnsList))),
                'username'            => (string)$row['username'],
                'password'            => (string)$row['password'],
                'vencimento'          => $formatPanelVencimento($row['vencimento'] ?? ''),
                'vencimento_painel'   => $formatPanelVencimento($row['vencimento'] ?? ''),
                'nome_cliente'        => (string)($row['nome_cliente'] ?? ''),
                'whatsapp'            => (string)($row['whatsapp'] ?? ''),
                'device_type'         => $appDevice,
                'last_device_type'    => (string)($row['last_device_type'] ?? ''),
                'allow_tv'            => !empty($profile['allow_tv']),
                'allow_mobile'        => !empty($profile['allow_mobile']),
                'allow_movies'        => !empty($profile['allow_movies']),
                'allow_series'        => !empty($profile['allow_series']),
                'allow_live'          => !empty($profile['allow_live']),
                'profile_updated_at'  => (int)($profile['updated_at'] ?? 0),
                'blocked'             => $blocked,
                'block_reason'        => $blockReason,
                'server_ts'           => time(),
                'panel_login'         => true,
            ], pp_profile_payload($row), $multiServerPayload, $getBrandingConfig($row), $getBlockConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        $validation = auth_validate_xtream_credentials((string)$row['dns'], (string)$row['username'], (string)$row['password']);
        if (empty($validation['ok'])) {
            if (!empty($validation['expired'])) {
                echo json_encode(array_merge(['status'=>'ok','mac_registered'=>true,'mac_pending'=>false,'mac_has_credentials'=>true,'logout'=>false,'blocked'=>true,'block_reason'=>'expired','message'=>'Conta vencida.','vencimento'=>auth_format_exp((int)($validation['exp'] ?? 0)),'vencimento_xui'=>auth_format_exp((int)($validation['exp'] ?? 0)),'dns'=>auth_normalize_dns((string)$row['dns']),'username'=>(string)$row['username'],'password'=>(string)$row['password'],'server_ts'=>time()], pp_profile_payload($row), $multiServerPayload, $getBrandingConfig($row), $getBlockConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            } else {
                echo json_encode(array_merge(['status'=>'invalid','mac_registered'=>true,'mac_pending'=>false,'mac_has_credentials'=>true,'logout'=>false,'message'=>'Usuário, senha ou DNS inválidos.'], pp_profile_payload($row), $getBrandingConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            }
            exit;
        }

        $row = auth_refresh_user_after_validation($db, $row, $validation, $appDevice);
        $userKey = da_user_key((string)$row['username'], (string)$row['password']);
        $profile = da_get_profile_by_key($db, $userKey);
        $blocked = false;
        $blockReason = '';
        if ($appDevice === 'mobile' && empty($profile['allow_mobile'])) {
            $blocked = true; $blockReason = 'mobile_blocked';
        } elseif ($appDevice === 'tv' && empty($profile['allow_tv'])) {
            $blocked = true; $blockReason = 'tv_blocked';
        }
        $dnsList = auth_get_dns_list($db);
        $dnsUser = auth_normalize_dns((string)$row['dns']);
        if ($dnsUser !== '' && !in_array($dnsUser, $dnsList, true)) array_unshift($dnsList, $dnsUser);
        try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}

        echo json_encode(array_merge([
            'status'           => 'ok',
            'mac_registered'   => true,
            'mac_pending'      => false,
            'mac_has_credentials' => true,
            'logout'           => false,
            'mac'              => $appMac,
            'dns'              => $dnsUser,
            'dns_list'         => array_values(array_unique(array_filter($dnsList))),
            'username'         => (string)$row['username'],
            'password'         => (string)$row['password'],
            'vencimento'       => $formatPanelVencimento($row['vencimento'] ?? '') ?: auth_format_exp((int)($validation['exp'] ?? 0)),
            'vencimento_painel' => $formatPanelVencimento($row['vencimento'] ?? ''),
            'vencimento_xui'   => auth_format_exp((int)($validation['exp'] ?? 0)),
            'nome_cliente'     => (string)($row['nome_cliente'] ?? ''),
            'whatsapp'         => (string)($row['whatsapp'] ?? ''),
            'device_type'      => $appDevice,
            'last_device_type' => (string)($row['last_device_type'] ?? ''),
            'allow_tv'         => !empty($profile['allow_tv']),
            'allow_mobile'     => !empty($profile['allow_mobile']),
            'allow_movies'     => !empty($profile['allow_movies']),
            'allow_series'     => !empty($profile['allow_series']),
            'allow_live'       => !empty($profile['allow_live']),
            'profile_updated_at' => (int)($profile['updated_at'] ?? 0),
            'blocked'          => $blocked,
            'block_reason'     => $blockReason,
            'server_ts'        => time(),
        ], pp_profile_payload($row), $multiServerPayload, $getBrandingConfig($row), $getBlockConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($apiAction === 'device_pulse') {
        $st = $db->prepare("SELECT * FROM usuarios WHERE mac = ? ORDER BY rowid DESC LIMIT 1");
        $st->execute([$appMac]);
        $row = $st->fetch(PDO::FETCH_ASSOC) ?: null;
        $serverTs = time();
        header('Content-Type: application/json; charset=utf-8');
        if (!$row) {
            $row = auth_upsert_pending_device($db, $appMac, $appDevice, $appDeviceMeta, false, 'user_deleted');
            echo json_encode([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'logout' => true,
                'force_login' => true,
                'reason' => 'mac_not_registered',
                'message' => 'Registro removido. Aparelho reenviado e aguardando nova ativação.',
                'server_ts' => $serverTs,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        if (!auth_user_has_credentials($row)) {
            auth_upsert_pending_device($db, $appMac, $appDevice, $appDeviceMeta, false, 'credentials_removed');
            try { ms_delete_servers($db, $appMac); } catch (Throwable $e) {}
            try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}
            echo json_encode([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'mac_has_credentials' => false,
                'logout' => false,
                'force_login' => true,
                'reason' => 'mac_pending',
                'message' => 'DNS, usuário ou senha removidos. Aguardando nova ativação.',
                'server_ts' => $serverTs,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        auth_mark_device_active($db, $appMac);
        $userKey = da_user_key((string)$row['username'], (string)$row['password']);
        $profile = da_get_profile_by_key($db, $userKey);
        $pulseMultiServerPayload = ms_payload($db, $appMac, $row);
        $rulesUpdatedAt = (int)($profile['updated_at'] ?? 0);
        try {
            $stRule = $db->prepare('SELECT updated_at FROM access_rules WHERE mac=? ORDER BY updated_at DESC LIMIT 1');
            $stRule->execute([$appMac]);
            $ruleRow = $stRule->fetch(PDO::FETCH_ASSOC);
            if ($ruleRow && (int)$ruleRow['updated_at'] > $rulesUpdatedAt) {
                $rulesUpdatedAt = (int)$ruleRow['updated_at'];
            }
        } catch (Throwable $e) {}
        $statusUser = strtolower(trim((string)($row['status'] ?? '')));
        $blocked = false; $reason = '';
        if ($statusUser !== '' && !in_array($statusUser, ['ativo', 'active'], true)) { $blocked = true; $reason = 'inactive'; }
        elseif ($panelVencExpired($row['vencimento'] ?? '')) { $blocked = true; $reason = 'expired'; }
        if ($appDevice === 'mobile' && empty($profile['allow_mobile'])) {
            $blocked = true; $reason = 'mobile_blocked';
        } elseif ($appDevice === 'tv' && empty($profile['allow_tv'])) {
            $blocked = true; $reason = 'tv_blocked';
        }
        try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}
        echo json_encode(array_merge([
            'status'           => 'ok',
            'mac_registered'   => true,
            'mac_pending'      => false,
            'mac_has_credentials' => true,
            'force_login'      => false,
            'logout'           => false,
            'blocked'          => $blocked,
            'block_reason'     => $reason,
            'allow_tv'         => !empty($profile['allow_tv']),
            'allow_mobile'     => !empty($profile['allow_mobile']),
            'allow_movies'     => !empty($profile['allow_movies']),
            'allow_series'     => !empty($profile['allow_series']),
            'allow_live'       => !empty($profile['allow_live']),
            'rules_updated_at' => $rulesUpdatedAt,
            'vencimento'       => $formatPanelVencimento($row['vencimento'] ?? ''),
            'vencimento_painel' => $formatPanelVencimento($row['vencimento'] ?? ''),
            'dns'              => auth_normalize_dns((string)$row['dns']),
            'username'         => (string)$row['username'],
            'password'         => (string)$row['password'],
            'device_type'      => $appDevice,
            'server_ts'        => $serverTs,
        ], pp_profile_payload($row), $pulseMultiServerPayload, $getBrandingConfig($row), $getBlockConfig($row)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['status' => 'error', 'message' => 'unknown_api_action'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

$tipoUser = (string)($_SESSION["tipo_user"] ?? "");
$isAdmin = ($tipoUser === "admin");
$isRev = ($tipoUser === "revendedor");
$revId = (int)($_SESSION["revendedor_id"] ?? 0);

if (!$isAdmin && !$isRev) {
    header("Location: index.php");
    exit;
}

if ($isRev && $revId <= 0) {
    header("Location: sair.php");
    exit;
}

$pairingFlashStatus = trim((string)($_GET['pairing_status'] ?? ''));
$pairingFlashMessage = trim((string)($_GET['pairing_message'] ?? ''));
$pairingAutoActivateMac = pp_normalize_mac((string)($_GET['activate_mac'] ?? ''));

if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && isset($_POST['claim_pairing_code'])) {
    if (!$isAdmin && (!$isRev || $revId <= 0)) {
        header('Location: users.php?pairing_status=error&pairing_message=' . urlencode('Sessão sem permissão para ativar por código.'));
        exit;
    }
    try {
        $claimOwnerId = $isRev ? $revId : 0;
        $claimed = pp_claim_pairing($db, (string)($_POST['pairing_code'] ?? ''), $claimOwnerId, $isAdmin);
        header('Location: users.php?pairing_status=success&pairing_message=' . urlencode('Cliente vinculado. Agora informe o link M3U para concluir a ativação.') . '&activate_mac=' . urlencode((string)($claimed['mac'] ?? '')));
        exit;
    } catch (Throwable $e) {
        header('Location: users.php?pairing_status=error&pairing_message=' . urlencode($e->getMessage()));
        exit;
    }
}

function jsonOut(array $arr): void {
    header("Content-Type: application/json; charset=utf-8");
    echo json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function normalizeMac(string $mac): string {
    $mac = preg_replace('/[^A-Fa-f0-9]/', '', trim($mac));
    $mac = strtoupper(substr($mac, 0, 12));
    if (strlen($mac) !== 12) {
        return '';
    }
    return implode(':', str_split($mac, 2));
}

function normalizeCsvText(string $text): string {
    $parts = preg_split('/[\r\n,;]+/', $text) ?: [];
    $clean = [];
    foreach ($parts as $part) {
        $value = trim((string)$part);
        if ($value !== '') {
            $clean[] = $value;
        }
    }
    $clean = array_values(array_unique($clean));
    return implode(',', $clean);
}


function parseM3uActivationUrl(string $url): array {
    $url = trim($url);
    $parts = parse_url($url);
    if (empty($parts['scheme']) || empty($parts['host']) || !in_array(strtolower((string)$parts['scheme']), ['http', 'https'], true)) {
        throw new RuntimeException('Link M3U inválido. Use uma URL começando com http ou https.');
    }
    parse_str((string)($parts['query'] ?? ''), $query);
    $username = trim((string)($query['username'] ?? ''));
    $password = trim((string)($query['password'] ?? ''));
    if ($username === '' || $password === '') {
        throw new RuntimeException('O link M3U precisa conter username e password.');
    }
    $port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
    return [
        'dns' => strtolower((string)$parts['scheme']) . '://' . $parts['host'] . $port,
        'username' => $username,
        'password' => $password,
    ];
}

function parseActivationExpiry(string $value): int {
    $value = trim($value);
    if ($value !== '') {
        $ts = strtotime($value);
        if ($ts !== false && $ts > 0) return $ts;
    }
    return time() + (30 * 86400);
}

function splitCsv(string $text): array {
    $parts = preg_split('/\s*,\s*/', trim($text)) ?: [];
    $out = [];
    foreach ($parts as $part) {
        $value = trim((string)$part);
        if ($value !== '') {
            $out[] = $value;
        }
    }
    return array_values(array_unique($out));
}

function currentOwnerId(bool $isRev, int $revId): int {
    return $isRev ? $revId : 0;
}

function hasColumn(PDO $db, string $table, string $column): bool {
    $st = $db->query("PRAGMA table_info($table)");
    if (!$st) {
        return false;
    }
    foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $col) {
        if (($col['name'] ?? '') === $column) {
            return true;
        }
    }
    return false;
}


function columnExists(PDO $db, string $table, string $column): bool {
    return hasColumn($db, $table, $column);
}
function ensureFeatureTables(PDO $db): void {
    $db->exec("CREATE TABLE IF NOT EXISTS mac_messages (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        mac TEXT NOT NULL,
        title TEXT DEFAULT 'Mensagem do servidor',
        message TEXT NOT NULL,
        tempo INTEGER NOT NULL DEFAULT 5,
        active INTEGER NOT NULL DEFAULT 1,
        shown_once INTEGER NOT NULL DEFAULT 0,
        usuario_id INTEGER DEFAULT 0,
        created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
        updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
    )");

    if (!hasColumn($db, 'mac_messages', 'title')) {
        $db->exec("ALTER TABLE mac_messages ADD COLUMN title TEXT DEFAULT 'Mensagem do servidor'");
    }

    if (!hasColumn($db, 'mac_messages', 'updated_at')) {
        $db->exec("ALTER TABLE mac_messages ADD COLUMN updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))");
        $db->exec("UPDATE mac_messages SET updated_at = COALESCE(created_at, strftime('%s','now')) WHERE updated_at IS NULL OR updated_at = 0");
    }

    $db->exec("CREATE INDEX IF NOT EXISTS idx_mac_messages_mac_owner_active
        ON mac_messages(mac, usuario_id, active)");

    $db->exec("CREATE INDEX IF NOT EXISTS idx_mac_messages_mac_active_updated
        ON mac_messages(mac, active, updated_at DESC, id DESC)");

    $db->exec("CREATE TABLE IF NOT EXISTS mac_message_views (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        message_id INTEGER NOT NULL,
        mac TEXT NOT NULL,
        device_type TEXT NOT NULL DEFAULT 'app',
        message_token TEXT NOT NULL,
        viewed_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
        UNIQUE(message_id, mac, device_type, message_token)
    )");

    if (!hasColumn($db, 'mac_message_views', 'message_token')) {
        $db->exec("ALTER TABLE mac_message_views ADD COLUMN message_token TEXT NOT NULL DEFAULT ''");
        $db->exec("UPDATE mac_message_views SET message_token = CAST(message_id AS TEXT) WHERE message_token = '' OR message_token IS NULL");
    }

    $schemaViews = (string)($db->query("SELECT sql FROM sqlite_master WHERE type='table' AND name='mac_message_views'")->fetchColumn() ?: '');
    if (!hasColumn($db, 'mac_message_views', 'device_type') || stripos($schemaViews, 'UNIQUE(message_id, mac, message_token)') !== false) {
        $db->exec("BEGIN IMMEDIATE");
        try {
            $db->exec("CREATE TABLE IF NOT EXISTS mac_message_views_new (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                message_id INTEGER NOT NULL,
                mac TEXT NOT NULL,
                device_type TEXT NOT NULL DEFAULT 'app',
                message_token TEXT NOT NULL,
                viewed_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
                UNIQUE(message_id, mac, device_type, message_token)
            )");
            $db->exec("INSERT OR IGNORE INTO mac_message_views_new (id, message_id, mac, device_type, message_token, viewed_at)
                SELECT id, message_id, mac, 'legacy', message_token, viewed_at FROM mac_message_views");
            $db->exec("DROP TABLE mac_message_views");
            $db->exec("ALTER TABLE mac_message_views_new RENAME TO mac_message_views");
            $db->exec("COMMIT");
        } catch (Throwable $e) {
            $db->exec("ROLLBACK");
            throw $e;
        }
    }

    $db->exec("CREATE INDEX IF NOT EXISTS idx_mac_message_views_lookup
        ON mac_message_views(message_id, mac, device_type, message_token)");

    $db->exec("CREATE TABLE IF NOT EXISTS block_config (id INTEGER PRIMARY KEY CHECK (id=1), title TEXT NOT NULL DEFAULT 'Acesso bloqueado', message TEXT NOT NULL DEFAULT 'Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.', show_qrcode INTEGER NOT NULL DEFAULT 1, updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')) )");
    $db->exec("INSERT OR IGNORE INTO block_config (id,title,message,show_qrcode,updated_at) VALUES (1,'Acesso bloqueado','Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.',1,strftime('%s','now'))");

    $db->exec("CREATE TABLE IF NOT EXISTS access_rules (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        mac TEXT NOT NULL,
        allow_all_live INTEGER NOT NULL DEFAULT 1,
        allow_all_movies INTEGER NOT NULL DEFAULT 1,
        allow_all_series INTEGER NOT NULL DEFAULT 1,
        blocked_live_ids TEXT DEFAULT '',
        blocked_movie_ids TEXT DEFAULT '',
        blocked_series_ids TEXT DEFAULT '',
        blocked_live_categories TEXT DEFAULT '',
        blocked_movie_categories TEXT DEFAULT '',
        blocked_series_categories TEXT DEFAULT '',
        usuario_id INTEGER DEFAULT 0,
        allow_tab_movies INTEGER NOT NULL DEFAULT 1,
        allow_tab_series INTEGER NOT NULL DEFAULT 1,
        allow_tab_kids INTEGER NOT NULL DEFAULT 1,
        allow_tab_anime INTEGER NOT NULL DEFAULT 1,
        allow_tab_novelas INTEGER NOT NULL DEFAULT 1,
        allow_tab_esportes INTEGER NOT NULL DEFAULT 1,
        updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
    )");

    foreach ([
        'allow_tab_movies' => 'INTEGER NOT NULL DEFAULT 1',
        'allow_tab_series' => 'INTEGER NOT NULL DEFAULT 1',
        'allow_tab_kids' => 'INTEGER NOT NULL DEFAULT 1',
        'allow_tab_anime' => 'INTEGER NOT NULL DEFAULT 1',
        'allow_tab_novelas' => 'INTEGER NOT NULL DEFAULT 1',
        'allow_tab_esportes' => 'INTEGER NOT NULL DEFAULT 1',
    ] as $col => $sql) {
        if (!columnExists($db, 'access_rules', $col)) {
            $db->exec("ALTER TABLE access_rules ADD COLUMN $col $sql");
        }
    }

    $db->exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_access_rules_mac_owner
        ON access_rules(mac, usuario_id)");
}

auth_ensure_schema($db);
ensureFeatureTables($db);
wa_ensure_schema($db);
auth_ensure_schema($db);
$waTemplates = wa_get_templates($db);

function moneyBr(float $value): string {
    return number_format($value, 2, ',', '.');
}

function fetchPaymentPlans(PDO $db): array {
    try {
        $rows = $db->query("SELECT id, nome, dias, valor FROM pagamentos_app ORDER BY valor ASC, id ASC")->fetchAll(PDO::FETCH_ASSOC);
        return is_array($rows) ? $rows : [];
    } catch (Throwable $e) {
        return [];
    }
}

function currentPanelBaseUrl(): string {
    return wa_detect_panel_url();
}

function postLocalJson(string $url, array $payload, int $timeout = 60): array {
    if (function_exists('app_guard_secret') && empty($payload['app_token'])) {
        $payload['app_token'] = app_guard_secret();
    }

    return wa_http_json($url, $payload, [], 'POST', $timeout);
}

$paymentPlans = fetchPaymentPlans($db);
$ownerId = currentOwnerId($isRev, $revId);

function parseM3uUrlForActivation(string $url): array {
    $url = trim($url);
    $parts = parse_url($url);
    if (empty($parts['scheme']) || empty($parts['host']) || !in_array(strtolower((string)$parts['scheme']), ['http', 'https'], true)) {
        throw new RuntimeException('Link M3U inválido. Use uma URL começando com http:// ou https://.');
    }

    parse_str((string)($parts['query'] ?? ''), $query);
    $username = trim((string)($query['username'] ?? ''));
    $password = trim((string)($query['password'] ?? ''));

    if ($username === '' || $password === '') {
        throw new RuntimeException('O link M3U precisa conter username e password.');
    }

    $port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
    $dns = strtolower((string)$parts['scheme']) . '://' . (string)$parts['host'] . $port;

    return [
        'dns' => rtrim($dns, '/'),
        'username' => $username,
        'password' => $password,
    ];
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["activate_pending_user"])) {
    $txStarted = false;
    try {
        $mac = normalizeMac((string)($_POST['mac'] ?? ''));
        if ($mac === '') {
            throw new RuntimeException('MAC inválido.');
        }

        $parsed = parseM3uUrlForActivation((string)($_POST['m3u_url'] ?? ''));
        $vencRaw = trim((string)($_POST['vencimento'] ?? ''));
        $vencTs = $vencRaw !== '' ? strtotime($vencRaw) : false;
        if ($vencTs === false || $vencTs <= 0) {
            $vencTs = time() + (30 * 86400);
        }

        // BEGIN IMMEDIATE impede duas revendas de reivindicarem o mesmo MAC simultaneamente.
        $db->exec('BEGIN IMMEDIATE');
        $txStarted = true;

        $stCurrent = $db->prepare('SELECT rowid AS _rowid, * FROM usuarios WHERE mac=? ORDER BY rowid DESC LIMIT 1');
        $stCurrent->execute([$mac]);
        $current = $stCurrent->fetch(PDO::FETCH_ASSOC);
        if (!$current) {
            throw new RuntimeException('Cliente pendente não encontrado.');
        }

        $currentOwner = (int)($current['usuario_id'] ?? 0);
        $currentStatus = mb_strtolower(trim((string)($current['status'] ?? '')), 'UTF-8');
        $currentHasCredentials = trim((string)($current['dns'] ?? '')) !== ''
            && trim((string)($current['username'] ?? '')) !== ''
            && trim((string)($current['password'] ?? '')) !== '';
        $isPending = !$currentHasCredentials || in_array($currentStatus, ['pending', 'pendente', 'aguardando', 'waiting'], true);

        if (!$isPending) {
            throw new RuntimeException('Este MAC já está ativado.');
        }

        $ownerId = 0;
        if ($isRev) {
            if ($currentOwner !== $revId) {
                throw new RuntimeException('Este cliente ainda não foi vinculado à sua revenda. Use o botão Ativar por código.');
            }
            $ownerId = $revId;
        } elseif ($currentOwner > 0) {
            throw new RuntimeException('Este MAC pertence a uma revenda e não pode ser ativado pelo cadastro global.');
        }

        $hasUpdatedAt = columnExists($db, 'usuarios', 'updated_at');
        $fields = 'dns=?, username=?, password=?, status=?, vencimento=?, usuario_id=?';
        $params = [$parsed['dns'], $parsed['username'], $parsed['password'], 'ativo', (int)$vencTs, $ownerId];
        if ($hasUpdatedAt) {
            $fields .= ', updated_at=?';
            $params[] = time();
        }
        $rowId = (int)($current['_rowid'] ?? 0);
        if ($rowId <= 0) {
            throw new RuntimeException('Não foi possível identificar o registro do MAC pendente.');
        }
        $params[] = $rowId;

        // O BEGIN IMMEDIATE já serializa as ativações. Atualizamos pelo rowid e
        // confirmamos o estado final, pois rowCount() do PDO/SQLite pode retornar
        // zero mesmo quando o registro ficou exatamente como solicitado.
        $st = $db->prepare("UPDATE usuarios SET {$fields} WHERE rowid=?");
        $st->execute($params);

        $stVerify = $db->prepare('SELECT usuario_id, status, dns, username, password FROM usuarios WHERE rowid=? LIMIT 1');
        $stVerify->execute([$rowId]);
        $verified = $stVerify->fetch(PDO::FETCH_ASSOC);
        $activationOk = $verified
            && (int)($verified['usuario_id'] ?? 0) === $ownerId
            && mb_strtolower(trim((string)($verified['status'] ?? '')), 'UTF-8') === 'ativo'
            && trim((string)($verified['dns'] ?? '')) === $parsed['dns']
            && trim((string)($verified['username'] ?? '')) === $parsed['username']
            && trim((string)($verified['password'] ?? '')) === $parsed['password'];

        if (!$activationOk) {
            throw new RuntimeException('Não foi possível confirmar a ativação do MAC. Atualize a página e tente novamente.');
        }

        if ($isRev) {
            $db->prepare('DELETE FROM revendedor_clientes WHERE mac=?')->execute([$mac]);
            $db->prepare('INSERT OR IGNORE INTO revendedor_clientes (mac, revendedor_id, created_at) VALUES (?, ?, ?)')
                ->execute([$mac, $revId, time()]);
        }

        $db->exec('COMMIT');
        $txStarted = false;

        try {
            ms_save_servers($db, $mac, $ownerId, [[
                'name' => 'Servidor principal',
                'dns' => $parsed['dns'],
                'username' => $parsed['username'],
                'password' => $parsed['password'],
            ]]);
        } catch (Throwable $syncError) {
            error_log('[users] Falha ao sincronizar servidor do MAC ' . $mac . ': ' . $syncError->getMessage());
        }

        jsonOut(['status' => 'success', 'message' => 'Cliente ativado e vinculado com sucesso.']);
    } catch (Throwable $e) {
        if ($txStarted) {
            try { $db->exec('ROLLBACK'); } catch (Throwable $ignored) {}
        }
        jsonOut(['status' => 'error', 'message' => $e->getMessage()]);
    }
}


if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["delete"])) {
    $macs = (array)$_POST["delete"];

    if ($isRev) {
        $st = $db->prepare("DELETE FROM usuarios WHERE mac = ? AND usuario_id = ?");
        foreach ($macs as $mac) {
            $mac = trim((string)$mac);
            if ($mac !== "") {
                $st->execute([$mac, $revId]);
                if ($st->rowCount() > 0) ms_delete_servers($db, $mac);
            }
        }

        $st2 = $db->prepare("DELETE FROM revendedor_clientes WHERE mac=? AND revendedor_id=?");
        foreach ($macs as $mac) {
            $mac = trim((string)$mac);
            if ($mac !== "") {
                $st2->execute([$mac, $revId]);
            }
        }

        $st3 = $db->prepare("DELETE FROM mac_messages WHERE mac=? AND usuario_id=?");
        $st4 = $db->prepare("DELETE FROM access_rules WHERE mac=? AND usuario_id=?");
        foreach ($macs as $mac) {
            $mac = trim((string)$mac);
            if ($mac !== "") {
                $st3->execute([$mac, $revId]);
                $st4->execute([$mac, $revId]);
            }
        }

        jsonOut(["status" => "success"]);
    }

    $st = $db->prepare("DELETE FROM usuarios WHERE mac = ? AND (usuario_id IS NULL OR usuario_id=0)");
    $st3 = $db->prepare("DELETE FROM mac_messages WHERE mac=? AND usuario_id=0");
    $st4 = $db->prepare("DELETE FROM access_rules WHERE mac=? AND usuario_id=0");
    foreach ($macs as $mac) {
        $mac = trim((string)$mac);
        if ($mac !== "") {
            $st->execute([$mac]);
            if ($st->rowCount() > 0) ms_delete_servers($db, $mac);
            $st3->execute([$mac]);
            $st4->execute([$mac]);
        }
    }

    jsonOut(["status" => "success"]);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["renew"])) {
    $mac = trim((string)($_POST["renew"] ?? ""));
    if ($mac !== "") {
        if ($isRev) {
            $db->prepare("
                UPDATE usuarios
                SET vencimento = CASE
                    WHEN vencimento IS NULL OR vencimento < strftime('%s','now')
                    THEN strftime('%s','now') + (30*86400)
                    ELSE vencimento + (30*86400)
                END,
                    whatsapp_activated_at = strftime('%s','now')
                WHERE mac=? AND usuario_id=?
            ")->execute([$mac, $revId]);
            $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1");
            $stUser->execute([$mac, $revId]);
        } else {
            $db->prepare("
                UPDATE usuarios
                SET vencimento = CASE
                    WHEN vencimento IS NULL OR vencimento < strftime('%s','now')
                    THEN strftime('%s','now') + (30*86400)
                    ELSE vencimento + (30*86400)
                END,
                    whatsapp_activated_at = strftime('%s','now')
                WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0)
            ")->execute([$mac]);
            $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1");
            $stUser->execute([$mac]);
        }

        $userRow = $stUser->fetch(PDO::FETCH_ASSOC) ?: [];
        $sendInfo = ['sent' => false, 'reason' => 'whatsapp_nao_informado'];

        if (!empty($userRow['whatsapp'])) {
            $sendResult = wa_send_template_to_user(
                $db,
                $userRow,
                'confirmacao_renovacao',
                [],
                '30 dias'
            );

            $sendInfo = [
                'sent' => $sendResult['ok'],
                'message' => $sendResult['message'],
                'details' => $sendResult['response']['data'] ?? null,
            ];
        }

        jsonOut(["status" => "success", 'whatsapp' => $sendInfo]);
    }

    jsonOut(["status" => "success"]);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["send_manual_due"])) {
    $mac = trim((string)($_POST["send_manual_due"] ?? ""));
    if ($mac === '') {
        jsonOut(['status' => 'error', 'message' => 'MAC não informado.']);
    }

    if ($isRev) {
        $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1");
        $stUser->execute([$mac, $revId]);
    } else {
        $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1");
        $stUser->execute([$mac]);
    }

    $userRow = $stUser->fetch(PDO::FETCH_ASSOC) ?: null;
    if (!$userRow) {
        jsonOut(['status' => 'error', 'message' => 'Usuário não encontrado.']);
    }
    if (empty($userRow['whatsapp'])) {
        jsonOut(['status' => 'error', 'message' => 'Cliente sem WhatsApp cadastrado.']);
    }

    $sendResult = wa_send_template_to_user($db, $userRow, 'cobranca_manual');

if ($sendResult['ok']) {
    jsonOut([
        'status' => 'success',
        'message' => 'Mensagem enviada com sucesso.'
    ]);
}

jsonOut([
    'status' => 'error',
    'message' => $sendResult['message'] !== '' ? $sendResult['message'] : 'Falha ao enviar mensagem.'
]);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["generate_pix_whatsapp"])) {
    $mac = trim((string)($_POST["mac"] ?? ""));
    $planId = (int)($_POST["plan_id"] ?? 0);

    if ($mac === '' || $planId <= 0) {
        jsonOut(['status' => 'error', 'message' => 'Cliente ou plano inválido.']);
    }

    if ($isRev) {
        $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1");
        $stUser->execute([$mac, $revId]);
    } else {
        $stUser = $db->prepare("SELECT * FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1");
        $stUser->execute([$mac]);
    }

    $userRow = $stUser->fetch(PDO::FETCH_ASSOC) ?: null;
    if (!$userRow) {
        jsonOut(['status' => 'error', 'message' => 'Usuário não encontrado.']);
    }

    if (empty($userRow['whatsapp'])) {
        jsonOut(['status' => 'error', 'message' => 'Cliente sem WhatsApp cadastrado.']);
    }

    $stPlan = $db->prepare("SELECT id, nome, dias, valor FROM pagamentos_app WHERE id=? LIMIT 1");
    $stPlan->execute([$planId]);
    $planRow = $stPlan->fetch(PDO::FETCH_ASSOC) ?: null;

    if (!$planRow) {
        jsonOut(['status' => 'error', 'message' => 'Plano não encontrado.']);
    }

    $planName = trim((string)($planRow['nome'] ?? 'Pagamento via PIX'));
    $planDays = (int)($planRow['dias'] ?? 0);
    $planValue = (float)($planRow['valor'] ?? 0);

    if ($planValue <= 0) {
        jsonOut(['status' => 'error', 'message' => 'O valor do plano é inválido.']);
    }

    $botPayload = [
        'mac' => (string)($userRow['mac'] ?? ''),
        'usuario_id' => (int)($userRow['usuario_id'] ?? 0),
        'nome_cliente' => trim((string)($userRow['nome_cliente'] ?? 'Cliente')),
        'whatsapp' => (string)($userRow['whatsapp'] ?? ''),
        'username' => (string)($userRow['username'] ?? ''),
        'status' => (string)($userRow['status'] ?? 'ativo'),
        'vencimento' => (string)($userRow['vencimento'] ?? ''),
        'plan' => [
            'id' => (int)($planRow['id'] ?? 0),
            'nome' => $planName,
            'dias' => $planDays,
            'valor' => $planValue,
        ],
    ];

    $botPayload['phone'] = (string)($userRow['whatsapp'] ?? '');
    $botPayload['template_key'] = 'cobranca_manual';
    $botPayload['source'] = 'cobranca_manual';
    $botResult = wa_bot_request($db, 'POST', '/admin/generate-pix', $botPayload);
    $botData = is_array($botResult['data'] ?? null) ? $botResult['data'] : [];

    if (empty($botResult['success']) || empty($botData['success'])) {
        $message = (string)($botData['message'] ?? $botResult['message'] ?? 'Não foi possível gerar o PIX.');
        jsonOut([
            'status' => 'error',
            'message' => $message,
            'details' => $botData,
        ]);
    }

    $pixData = is_array($botData['pix'] ?? null) ? $botData['pix'] : [];

    jsonOut([
        'status' => 'success',
        'message' => (string)($botData['message'] ?? 'PIX gerado com sucesso.'),
        'whatsapp' => $botData['whatsapp'] ?? null,
        'pix' => [
            'payment_id' => (string)($pixData['payment_id'] ?? ''),
            'gateway_nome' => (string)($pixData['gateway_nome'] ?? ''),
            'valor_gerado' => (float)($pixData['valor_gerado'] ?? $planValue),
            'pix_code' => (string)($pixData['pix_code'] ?? ''),
            'qr_code_base64' => (string)($pixData['qr_code_base64'] ?? ''),
        ],
    ]);
}
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["export_backup_json"])) {
    if ($isRev) {
        $st = $db->prepare("
            SELECT mac, username, password, dns, status, vencimento, nome_cliente, whatsapp, teste, usuario_id, login_mode, activation_code
            FROM usuarios
            WHERE usuario_id = ?
          ORDER BY rowid ASC
        ");
        $st->execute([$revId]);
        $clientes = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } else {
        $st = $db->query("
            SELECT mac, username, password, dns, status, vencimento, nome_cliente, whatsapp, teste, usuario_id, login_mode, activation_code
            FROM usuarios
            WHERE (usuario_id IS NULL OR usuario_id=0)
          ORDER BY rowid ASC
        ");
        $clientes = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    }

    $payload = [
        'type' => 'clientes_backup',
        'version' => 1,
        'exported_at' => date('c'),
        'scope' => $isRev ? 'revendedor' : 'admin',
        'owner_id' => $isRev ? $revId : 0,
        'total' => count($clientes),
        'clientes' => $clientes,
    ];

    $filename = 'backup_clientes_' . date('Y-m-d_H-i-s') . '.json';

    header('Content-Type: application/json; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    echo json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["import_backup_json"])) {
    if (
        !isset($_FILES['backup_file']) ||
        !is_uploaded_file($_FILES['backup_file']['tmp_name'])
    ) {
        jsonOut(['status' => 'error', 'message' => 'Arquivo JSON não enviado.']);
    }

    $raw = file_get_contents($_FILES['backup_file']['tmp_name']);
    if ($raw === false || trim($raw) === '') {
        jsonOut(['status' => 'error', 'message' => 'Não foi possível ler o arquivo enviado.']);
    }

    $json = json_decode($raw, true);
    if (!is_array($json) || !isset($json['clientes']) || !is_array($json['clientes'])) {
        jsonOut(['status' => 'error', 'message' => 'JSON inválido para backup de clientes.']);
    }

    $clientes = $json['clientes'];
    $importados = 0;
    $atualizados = 0;
    $ignorados = 0;

    try {
        $db->beginTransaction();

        foreach ($clientes as $cliente) {
            if (!is_array($cliente)) {
                $ignorados++;
                continue;
            }

            $mac = trim((string)($cliente['mac'] ?? ''));
            if ($mac === '') {
                $ignorados++;
                continue;
            }

            $username    = trim((string)($cliente['username'] ?? ''));
            $password    = (string)($cliente['password'] ?? '');
            $dns         = trim((string)($cliente['dns'] ?? ''));
            $status      = ((string)($cliente['status'] ?? '') === 'ativo') ? 'ativo' : 'inativo';
            $nomeCliente = trim((string)($cliente['nome_cliente'] ?? ''));
            $whatsapp    = trim((string)($cliente['whatsapp'] ?? ''));
            $testeValue  = !empty($cliente['teste']) ? 1 : null;
            $loginModeValue = trim((string)($cliente['login_mode'] ?? 'normal'));
            if (!in_array($loginModeValue, ['normal', 'activation_code'], true)) { $loginModeValue = 'normal'; }
            $activationCodeValue = trim((string)($cliente['activation_code'] ?? ''));

            $vencimento = $cliente['vencimento'] ?? null;
            if ($vencimento === '' || $vencimento === null) {
                $vencTs = null;
            } elseif (is_numeric($vencimento)) {
                $vencTs = (int)$vencimento;
            } else {
                $tmp = strtotime((string)$vencimento);
                $vencTs = $tmp !== false ? $tmp : null;
            }

            if ($isRev) {
                $stCheck = $db->prepare("SELECT mac FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1");
                $stCheck->execute([$mac, $revId]);
                $exists = $stCheck->fetch(PDO::FETCH_ASSOC);

                if ($exists) {
                    $stUp = $db->prepare("
                        UPDATE usuarios
                        SET username=?, password=?, dns=?, status=?, vencimento=?, nome_cliente=?, whatsapp=?, teste=?, login_mode=?, activation_code=?
                        WHERE mac=? AND usuario_id=?
                    ");
                    $stUp->execute([
                        $username,
                        $password,
                        $dns,
                        $status,
                        $vencTs,
                        $nomeCliente,
                        $whatsapp,
                        $testeValue,
                        $loginModeValue,
                        $activationCodeValue,
                        $mac,
                        $revId
                    ]);
                    $atualizados++;
                } else {
                    if (!revenda_can_add_mac($db, $revId, 1)) {
                        $ignorados++;
                        continue;
                    }
                    $stIn = $db->prepare("
                        INSERT INTO usuarios (mac, username, password, dns, status, vencimento, nome_cliente, whatsapp, teste, usuario_id, login_mode, activation_code)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    ");
                    $stIn->execute([
                        $mac,
                        $username,
                        $password,
                        $dns,
                        $status,
                        $vencTs,
                        $nomeCliente,
                        $whatsapp,
                        $testeValue,
                        $revId,
                        $loginModeValue,
                        $activationCodeValue
                    ]);
                    $importados++;
                }

                try {
                    $stLink = $db->prepare("INSERT OR IGNORE INTO revendedor_clientes (mac, revendedor_id) VALUES (?, ?)");
                    $stLink->execute([$mac, $revId]);
                } catch (Throwable $e) {
                }
            } else {
                $stCheck = $db->prepare("SELECT mac FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1");
                $stCheck->execute([$mac]);
                $exists = $stCheck->fetch(PDO::FETCH_ASSOC);

                if ($exists) {
                    $stUp = $db->prepare("
                        UPDATE usuarios
                        SET username=?, password=?, dns=?, status=?, vencimento=?, nome_cliente=?, whatsapp=?, teste=?, login_mode=?, activation_code=?
                        WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0)
                    ");
                    $stUp->execute([
                        $username,
                        $password,
                        $dns,
                        $status,
                        $vencTs,
                        $nomeCliente,
                        $whatsapp,
                        $testeValue,
                        $loginModeValue,
                        $activationCodeValue,
                        $mac
                    ]);
                    $atualizados++;
                } else {
                    $stIn = $db->prepare("
                        INSERT INTO usuarios (mac, username, password, dns, status, vencimento, nome_cliente, whatsapp, teste, usuario_id, login_mode, activation_code)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)
                    ");
                    $stIn->execute([
                        $mac,
                        $username,
                        $password,
                        $dns,
                        $status,
                        $vencTs,
                        $nomeCliente,
                        $whatsapp,
                        $testeValue,
                        $loginModeValue,
                        $activationCodeValue
                    ]);
                    $importados++;
                }
            }
        }

        $db->commit();

        jsonOut([
            'status' => 'success',
            'message' => 'Backup importado com sucesso.',
            'importados' => $importados,
            'atualizados' => $atualizados,
            'ignorados' => $ignorados
        ]);
    } catch (Throwable $e) {
        if ($db->inTransaction()) {
            $db->rollBack();
        }

        jsonOut([
            'status' => 'error',
            'message' => 'Erro ao importar backup: ' . $e->getMessage()
        ]);
    }
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["edit_save"])) {
    $pendingProfileUpload = '';
    try {
        $mac = normalizeMac((string)($_POST["mac"] ?? ""));
        if ($mac === '') throw new RuntimeException('MAC inválido.');

        $username = trim((string)($_POST["username"] ?? ""));
        $password = (string)($_POST["password"] ?? "");
        $dns = ms_normalize_dns((string)($_POST["dns"] ?? ""));
        $status = ((string)($_POST["status"] ?? "") === "ativo") ? "ativo" : "inativo";
        $vencimento = trim((string)($_POST["vencimento"] ?? ""));
        $nomeCliente = trim((string)($_POST["nome_cliente"] ?? ""));
        $whatsapp = trim((string)($_POST["whatsapp"] ?? ""));
        $tipoRegistro = trim((string)($_POST["tipo_registro"] ?? "cliente"));
        $loginMode = trim((string)($_POST["login_mode"] ?? 'normal'));
        if (!in_array($loginMode, ['normal', 'activation_code'], true)) $loginMode = 'normal';
        $activationCode = trim((string)($_POST["activation_code"] ?? ''));
        if ($loginMode !== 'activation_code') $activationCode = '';
        $vencTs = $vencimento !== '' ? strtotime($vencimento) : null;
        if ($vencTs === false) $vencTs = null;
        $testeValue = ($tipoRegistro === "teste") ? 1 : null;
        $ownerId = $isRev ? $revId : 0;

        // Confere se o usuário pertence ao painel/revenda antes de alterar servidores.
        $checkSql = $isRev
            ? 'SELECT * FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1'
            : 'SELECT * FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1';
        $check = $db->prepare($checkSql);
        $check->execute($isRev ? [$mac, $revId] : [$mac]);
        $currentUser = $check->fetch(PDO::FETCH_ASSOC) ?: null;
        if (!$currentUser) throw new RuntimeException('Usuário não encontrado ou sem permissão.');

        $oldProfileImage = (string)($currentUser['profile_image'] ?? '');
        $profileImage = $oldProfileImage;
        $profileUpdatedAt = (int)($currentUser['profile_updated_at'] ?? 0);
        $profileImageMime = (string)($currentUser['profile_image_mime'] ?? '');
        $profileImageHash = (string)($currentUser['profile_image_hash'] ?? '');
        $profileImageSize = (int)($currentUser['profile_image_size'] ?? 0);
        $hasProfileUpload = isset($_FILES['profile_image'])
            && is_array($_FILES['profile_image'])
            && (int)($_FILES['profile_image']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE;
        if ($hasProfileUpload) {
            $storedProfile = pp_store_image_meta($_FILES['profile_image'], 'api/uploads/profiles', 'profile_' . str_replace(':', '', $mac));
            $pendingProfileUpload = (string)($storedProfile['path'] ?? '');
            if ($pendingProfileUpload === '') throw new RuntimeException('Não foi possível gravar a foto do perfil.');
            $profileImage = $pendingProfileUpload;
            $profileUpdatedAt = media_monotonic_version($profileUpdatedAt);
            $profileImageMime = (string)($storedProfile['mime'] ?? '');
            $profileImageHash = (string)($storedProfile['sha256'] ?? '');
            $profileImageSize = (int)($storedProfile['size'] ?? 0);
        } elseif (((string)($_POST['delete_profile_image'] ?? '')) === '1') {
            $profileImage = '';
            $profileUpdatedAt = media_monotonic_version($profileUpdatedAt);
            $profileImageMime = '';
            $profileImageHash = '';
            $profileImageSize = 0;
        }

        $serversRaw = [];
        $serversJson = trim((string)($_POST['servers_json'] ?? ''));
        if ($serversJson !== '') {
            $decoded = json_decode($serversJson, true);
            if (is_array($decoded)) $serversRaw = $decoded;
        }
        if (!$serversRaw && $dns !== '' && $username !== '' && $password !== '') {
            $serversRaw[] = [
                'key' => '',
                'name' => 'Servidor principal',
                'dns' => $dns,
                'username' => $username,
                'password' => $password,
            ];
        }

        $savedServers = ms_save_servers($db, $mac, $ownerId, $serversRaw);
        $primary = $savedServers[0] ?? ['dns'=>'','username'=>'','password'=>''];

        $where = $isRev ? 'mac=? AND usuario_id=?' : 'mac=? AND (usuario_id IS NULL OR usuario_id=0)';
        $params = [
            (string)($primary['username'] ?? ''),
            (string)($primary['password'] ?? ''),
            (string)($primary['dns'] ?? ''),
            $status,
            $vencTs,
            $nomeCliente,
            $whatsapp,
            $testeValue,
            $loginMode,
            $activationCode,
            $mac,
        ];
        if ($isRev) $params[] = $revId;
        $params = array_slice($params, 0, -1 * (1 + ($isRev ? 1 : 0)));
        $params[] = $profileImage;
        $params[] = $profileUpdatedAt;
        $params[] = $profileImageMime;
        $params[] = $profileImageHash;
        $params[] = $profileImageSize;
        $params[] = $mac;
        if ($isRev) $params[] = $revId;
        $up = $db->prepare("UPDATE usuarios SET username=?, password=?, dns=?, status=?, vencimento=?, nome_cliente=?, whatsapp=?, teste=?, login_mode=?, activation_code=?, profile_image=?, profile_updated_at=?, profile_image_mime=?, profile_image_hash=?, profile_image_size=? WHERE {$where}");
        $ok = $up->execute($params);
        if ($ok) {
            $primaryComplete = trim((string)($primary['dns'] ?? '')) !== ''
                && trim((string)($primary['username'] ?? '')) !== ''
                && trim((string)($primary['password'] ?? '')) !== '';
            if ($primaryComplete) {
                auth_mark_device_active($db, $mac);
            } else {
                // Ao remover qualquer parte da credencial, normaliza o registro
                // imediatamente como pendente. Assim o próximo pulso do app já
                // recebe force_login e não existe janela com dados parciais.
                $pendingDeviceType = da_normalize_device_type((string)($currentUser['last_device_type'] ?? $currentUser['device_type'] ?? 'tv'));
                auth_upsert_pending_device(
                    $db,
                    $mac,
                    $pendingDeviceType,
                    ['nome_cliente' => $nomeCliente, 'whatsapp' => $whatsapp],
                    true,
                    'credentials_removed'
                );
                try { ms_delete_servers($db, $mac); } catch (Throwable $e) {}
            }
            if ($oldProfileImage !== '' && $oldProfileImage !== $profileImage) pp_remove_uploaded_file($oldProfileImage);
            $pendingProfileUpload = '';
        }

        jsonOut([
            'status' => $ok ? 'success' : 'error',
            'server_count' => count($savedServers),
            'message' => $ok ? 'Usuário e servidores atualizados.' : 'Não foi possível salvar as alterações.',
        ]);
    } catch (Throwable $e) {
        if ($pendingProfileUpload !== '') pp_remove_uploaded_file($pendingProfileUpload);
        jsonOut(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["save_message"])) {
    $mac = normalizeMac((string)($_POST['mac'] ?? ''));
    $title = trim((string)($_POST['title'] ?? 'Mensagem do servidor'));
    $message = trim((string)($_POST['message'] ?? ''));
    $tempo = max(1, min(30, (int)($_POST['tempo'] ?? 5)));
    $active = ((int)($_POST['active'] ?? 1) === 1) ? 1 : 0;
    $shownOnce = ((int)($_POST['shown_once'] ?? 1) === 1) ? 1 : 0;

    if ($mac === '' || $message === '') {
        jsonOut(['status' => 'error', 'message' => 'MAC ou mensagem inválida']);
    }

    $existsSql = $isRev
        ? "SELECT id FROM mac_messages WHERE mac=? AND usuario_id=? ORDER BY id DESC LIMIT 1"
        : "SELECT id FROM mac_messages WHERE mac=? AND usuario_id=0 ORDER BY id DESC LIMIT 1";
    $exists = $db->prepare($existsSql);
    $exists->execute($isRev ? [$mac, $revId] : [$mac]);
    $row = $exists->fetch();

    if ($row) {
        $update = $db->prepare("UPDATE mac_messages
            SET title=?, message=?, tempo=?, active=?, shown_once=?, updated_at=strftime('%s','now')
            WHERE id=?");
        $ok = $update->execute([$title, $message, $tempo, $active, $shownOnce, (int)$row['id']]);
        if ($ok) {
            $db->prepare("DELETE FROM mac_message_views WHERE message_id=?")->execute([(int)$row['id']]);
        }
        jsonOut(['status' => $ok ? 'success' : 'error']);
    }

    $insert = $db->prepare("INSERT INTO mac_messages
        (mac, title, message, tempo, active, shown_once, usuario_id, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, strftime('%s','now'), strftime('%s','now'))");
    $ok = $insert->execute([$mac, $title, $message, $tempo, $active, $shownOnce, $ownerId]);
    jsonOut(['status' => $ok ? 'success' : 'error']);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["clear_message"])) {
    $mac = normalizeMac((string)($_POST['mac'] ?? ''));
    if ($mac === '') {
        jsonOut(['status' => 'error']);
    }

    $sql = $isRev
        ? "UPDATE mac_messages SET active=0, updated_at=strftime('%s','now') WHERE mac=? AND usuario_id=?"
        : "UPDATE mac_messages SET active=0, updated_at=strftime('%s','now') WHERE mac=? AND usuario_id=0";

    $st = $db->prepare($sql);
    $ok = $st->execute($isRev ? [$mac, $revId] : [$mac]);
    jsonOut(['status' => $ok ? 'success' : 'error']);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["save_block_config"])) {
    $db->exec("CREATE TABLE IF NOT EXISTS block_config (id INTEGER PRIMARY KEY CHECK (id=1), title TEXT NOT NULL DEFAULT 'Acesso bloqueado', message TEXT NOT NULL DEFAULT 'Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.', show_qrcode INTEGER NOT NULL DEFAULT 1, updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')) )");
    $title = trim((string)($_POST['block_title'] ?? 'Acesso bloqueado')) ?: 'Acesso bloqueado';
    $message = trim((string)($_POST['block_message'] ?? ''));
    if ($message === '') $message = 'Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.';
    $showQr = ((int)($_POST['block_show_qrcode'] ?? 0) === 1) ? 1 : 0;
    $st = $db->prepare("INSERT INTO block_config (id,title,message,show_qrcode,updated_at) VALUES (1,?,?,?,strftime('%s','now')) ON CONFLICT(id) DO UPDATE SET title=excluded.title,message=excluded.message,show_qrcode=excluded.show_qrcode,updated_at=strftime('%s','now')");
    jsonOut(['status' => $st->execute([$title, $message, $showQr]) ? 'success' : 'error']);
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["save_access_rules"])) {
    $mac = normalizeMac((string)($_POST['mac'] ?? ''));
    if ($mac === '') {
        jsonOut(['status' => 'error', 'message' => 'MAC inválido']);
    }

    $allowTv = ((int)($_POST['allow_tv'] ?? 1) === 1);
    $allowMobile = ((int)($_POST['allow_mobile'] ?? 1) === 1);
    $allowContentMovies = ((int)($_POST['allow_content_movies'] ?? 1) === 1);
    $allowContentSeries = ((int)($_POST['allow_content_series'] ?? 1) === 1);
    $allowContentLive = ((int)($_POST['allow_content_live'] ?? 1) === 1);

    $allowAllLive = $allowContentLive ? (((int)($_POST['allow_all_live'] ?? 1) === 1) ? 1 : 0) : 0;
    $allowAllMovies = $allowContentMovies ? (((int)($_POST['allow_all_movies'] ?? 1) === 1) ? 1 : 0) : 0;
    $allowAllSeries = $allowContentSeries ? (((int)($_POST['allow_all_series'] ?? 1) === 1) ? 1 : 0) : 0;
    $allowTabMovies = ((int)($_POST['allow_tab_movies'] ?? $allowAllMovies) === 1) ? 1 : 0;
    $allowTabSeries = ((int)($_POST['allow_tab_series'] ?? $allowAllSeries) === 1) ? 1 : 0;
    $allowTabKids = ((int)($_POST['allow_tab_kids'] ?? 1) === 1) ? 1 : 0;
    $allowTabAnime = ((int)($_POST['allow_tab_anime'] ?? 1) === 1) ? 1 : 0;
    $allowTabNovelas = ((int)($_POST['allow_tab_novelas'] ?? $allowTabSeries) === 1) ? 1 : 0;
    $allowTabEsportes = ((int)($_POST['allow_tab_esportes'] ?? 1) === 1) ? 1 : 0;

    $blockedLiveIds = normalizeCsvText((string)($_POST['blocked_live_ids'] ?? ''));
    $blockedMovieIds = normalizeCsvText((string)($_POST['blocked_movie_ids'] ?? ''));
    $blockedSeriesIds = normalizeCsvText((string)($_POST['blocked_series_ids'] ?? ''));
    $blockedLiveCategories = normalizeCsvText((string)($_POST['blocked_live_categories'] ?? ''));
    $blockedMovieCategories = normalizeCsvText((string)($_POST['blocked_movie_categories'] ?? ''));
    $blockedSeriesCategories = normalizeCsvText((string)($_POST['blocked_series_categories'] ?? ''));

    if ($allowAllLive === 1) {
        $blockedLiveIds = '';
        $blockedLiveCategories = '';
    }
    if ($allowAllMovies === 1) {
        $blockedMovieIds = '';
        $blockedMovieCategories = '';
    }
    if ($allowAllSeries === 1) {
        $blockedSeriesIds = '';
        $blockedSeriesCategories = '';
    }

    $stUserAccess = $isRev
        ? $db->prepare("SELECT username, password FROM usuarios WHERE mac=? AND usuario_id=? LIMIT 1")
        : $db->prepare("SELECT username, password FROM usuarios WHERE mac=? AND (usuario_id IS NULL OR usuario_id=0) LIMIT 1");
    $stUserAccess->execute($isRev ? [$mac, $revId] : [$mac]);
    $userAccess = $stUserAccess->fetch(PDO::FETCH_ASSOC) ?: null;
    if (!$userAccess) {
        jsonOut(['status' => 'error', 'message' => 'Usuário não encontrado']);
    }
    $userKey = da_user_key($userAccess['username'] ?? '', $userAccess['password'] ?? '');
    $prevProfileTs = 0;
    if ($userKey !== '') {
        $stProfileTs = $db->prepare("SELECT updated_at FROM user_access_profiles WHERE user_key=? LIMIT 1");
        $stProfileTs->execute([$userKey]);
        $prevProfileTs = (int)($stProfileTs->fetchColumn() ?: 0);
    }
    $stRuleTs = $isRev
        ? $db->prepare("SELECT updated_at FROM access_rules WHERE mac=? AND usuario_id=? LIMIT 1")
        : $db->prepare("SELECT updated_at FROM access_rules WHERE mac=? AND usuario_id=0 LIMIT 1");
    $stRuleTs->execute($isRev ? [$mac, $revId] : [$mac]);
    $prevRuleTs = (int)($stRuleTs->fetchColumn() ?: 0);
    $rulesUpdatedAt = max(time(), $prevProfileTs + 1, $prevRuleTs + 1);
    da_upsert_profile($db, $userKey, (string)($userAccess['username'] ?? ''), (string)($userAccess['password'] ?? ''), [
        'allow_tv' => $allowTv,
        'allow_mobile' => $allowMobile,
        'allow_movies' => $allowContentMovies,
        'allow_series' => $allowContentSeries,
        'allow_live' => $allowContentLive,
        'allow_tab_movies' => $allowTabMovies === 1,
        'allow_tab_series' => $allowTabSeries === 1,
        'allow_tab_kids' => $allowTabKids === 1,
        'allow_tab_anime' => $allowTabAnime === 1,
        'allow_tab_novelas' => $allowTabNovelas === 1,
        'allow_tab_esportes' => $allowTabEsportes === 1,
        'updated_at' => $rulesUpdatedAt,
    ]);

    $existsSql = $isRev
        ? "SELECT id FROM access_rules WHERE mac=? AND usuario_id=? LIMIT 1"
        : "SELECT id FROM access_rules WHERE mac=? AND usuario_id=0 LIMIT 1";
    $exists = $db->prepare($existsSql);
    $exists->execute($isRev ? [$mac, $revId] : [$mac]);
    $row = $exists->fetch();

    if ($row) {
        $up = $db->prepare("UPDATE access_rules SET
            allow_all_live=?,
            allow_all_movies=?,
            allow_all_series=?,
            blocked_live_ids=?,
            blocked_movie_ids=?,
            blocked_series_ids=?,
            blocked_live_categories=?,
            blocked_movie_categories=?,
            blocked_series_categories=?,
            allow_tab_movies=?,
            allow_tab_series=?,
            allow_tab_kids=?,
            allow_tab_anime=?,
            allow_tab_novelas=?,
            allow_tab_esportes=?,
            updated_at=?
            WHERE id=?");
        $ok = $up->execute([
            $allowAllLive,
            $allowAllMovies,
            $allowAllSeries,
            $blockedLiveIds,
            $blockedMovieIds,
            $blockedSeriesIds,
            $blockedLiveCategories,
            $blockedMovieCategories,
            $blockedSeriesCategories,
            $allowTabMovies,
            $allowTabSeries,
            $allowTabKids,
            $allowTabAnime,
            $allowTabNovelas,
            $allowTabEsportes,
            $rulesUpdatedAt,
            (int)$row['id']
        ]);
        jsonOut(['status' => $ok ? 'success' : 'error']);
    }

    $ins = $db->prepare("INSERT INTO access_rules (
        mac,
        allow_all_live,
        allow_all_movies,
        allow_all_series,
        blocked_live_ids,
        blocked_movie_ids,
        blocked_series_ids,
        blocked_live_categories,
        blocked_movie_categories,
        blocked_series_categories,
        allow_tab_movies,
        allow_tab_series,
        allow_tab_kids,
        allow_tab_anime,
        allow_tab_novelas,
        allow_tab_esportes,
        usuario_id,
        updated_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    $ok = $ins->execute([
        $mac,
        $allowAllLive,
        $allowAllMovies,
        $allowAllSeries,
        $blockedLiveIds,
        $blockedMovieIds,
        $blockedSeriesIds,
        $blockedLiveCategories,
        $blockedMovieCategories,
        $blockedSeriesCategories,
        $allowTabMovies,
        $allowTabSeries,
        $allowTabKids,
        $allowTabAnime,
        $allowTabNovelas,
        $allowTabEsportes,
        $ownerId,
        $rulesUpdatedAt
    ]);
    jsonOut(['status' => $ok ? 'success' : 'error']);
}

if ($isRev) {
    $stTotal = $db->prepare("SELECT COUNT(*) FROM usuarios WHERE usuario_id = ?");
    $stTotal->execute([$revId]);
    $totalClientes = (int)$stTotal->fetchColumn();

    $stTeste = $db->prepare("SELECT COUNT(*) FROM usuarios WHERE usuario_id = ? AND teste IS NOT NULL");
    $stTeste->execute([$revId]);
    $totalTestes = (int)$stTeste->fetchColumn();

    $stAtivos = $db->prepare("SELECT COUNT(*) FROM usuarios WHERE usuario_id = ? AND status = 'ativo'");
    $stAtivos->execute([$revId]);
    $totalAtivos = (int)$stAtivos->fetchColumn();
} else {
    $totalClientes = (int)$db->query("SELECT COUNT(*) FROM usuarios WHERE (usuario_id IS NULL OR usuario_id=0)")->fetchColumn();
    $totalTestes   = (int)$db->query("SELECT COUNT(*) FROM usuarios WHERE (usuario_id IS NULL OR usuario_id=0) AND teste IS NOT NULL")->fetchColumn();
    $totalAtivos   = (int)$db->query("SELECT COUNT(*) FROM usuarios WHERE (usuario_id IS NULL OR usuario_id=0) AND status='ativo'")->fetchColumn();
}

$messageMap = [];
$ruleMap = [];
$profileMap = [];

if ($isRev) {
    $msgSt = $db->prepare("SELECT mac, id, title, message, tempo, active, shown_once, updated_at FROM mac_messages WHERE usuario_id=? ORDER BY id DESC");
    $msgSt->execute([$revId]);
} else {
    $msgSt = $db->query("SELECT mac, id, title, message, tempo, active, shown_once, updated_at FROM mac_messages WHERE usuario_id=0 ORDER BY id DESC");
}
while ($msg = $msgSt->fetch()) {
    $mac = (string)($msg['mac'] ?? '');
    if ($mac !== '' && !isset($messageMap[$mac])) {
        $messageMap[$mac] = $msg;
    }
}

if ($isRev) {
    $ruleSt = $db->prepare("SELECT * FROM access_rules WHERE usuario_id=? ORDER BY id DESC");
    $ruleSt->execute([$revId]);
} else {
    $ruleSt = $db->query("SELECT * FROM access_rules WHERE usuario_id=0 ORDER BY id DESC");
}
while ($rule = $ruleSt->fetch()) {
    $mac = (string)($rule['mac'] ?? '');
    if ($mac !== '' && !isset($ruleMap[$mac])) {
        $ruleMap[$mac] = $rule;
    }
}

if ($isRev) {
    $stProfiles = $db->prepare("SELECT mac, username, password, device_type, last_device_type FROM usuarios WHERE usuario_id = ? ORDER BY rowid DESC");
    $stProfiles->execute([$revId]);
 } else {
    $stProfiles = $db->query("SELECT mac, username, password, device_type, last_device_type FROM usuarios WHERE (usuario_id IS NULL OR usuario_id=0) ORDER BY rowid DESC");
}
while ($prow = $stProfiles->fetch(PDO::FETCH_ASSOC)) {
    $pmac = (string)($prow['mac'] ?? '');
    if ($pmac === '' || isset($profileMap[$pmac])) continue;
    $profileMap[$pmac] = da_get_profile_by_key($db, da_user_key($prow['username'] ?? '', $prow['password'] ?? '')) + [
        'device_type' => da_normalize_device_type((string)($prow['device_type'] ?? $prow['last_device_type'] ?? 'tv')),
    ];
}

$serverMap = ms_get_servers_map($db, $isRev ? $revId : null);

require_once __DIR__ . '/../includes/header.php';
?>
<div class="dns-wrap">
    <div class="card radius-10 dns-card">
        <div class="card-header bg-transparent dns-header">
            <div class="dns-title">
                <div class="ic"><i class="bi bi-people-fill"></i></div>
                <div>
                    <h5>Usuários</h5>
                    <span>Gerencie clientes, testes, renovações, mensagens, PIX e controle de acesso</span>
                </div>
            </div>

            <div class="dns-actions">
                <span class="badge-soft"><i class="bi bi-shield-check"></i> Seguro</span>
            </div>
        </div>

        <div class="card-body users-page">

    <?php if ($pairingFlashMessage !== ''): ?>
        <div class="alert alert-<?= $pairingFlashStatus === 'success' ? 'success' : 'danger' ?> d-flex align-items-center gap-2">
            <i class="bi <?= $pairingFlashStatus === 'success' ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill' ?>"></i>
            <span><?= htmlspecialchars($pairingFlashMessage, ENT_QUOTES, 'UTF-8') ?></span>
        </div>
    <?php endif; ?>

    <div class="stats-grid">
        <div class="stat-card">
            <div class="stat-icon primary"><i class="bi bi-people"></i></div>
            <div class="stat-meta">
                <small>Total</small>
                <strong><?= $totalClientes ?></strong>
                <span><?= $isRev ? "clientes da sua revenda" : "clientes do admin" ?></span>
            </div>
        </div>
        <div class="stat-card">
            <div class="stat-icon warning"><i class="bi bi-lightning-charge"></i></div>
            <div class="stat-meta">
                <small>Testes</small>
                <strong><?= $totalTestes ?></strong>
                <span>registros marcados como teste</span>
            </div>
        </div>
        <div class="stat-card">
            <div class="stat-icon success"><i class="bi bi-shield-check"></i></div>
            <div class="stat-meta">
                <small>Ativos</small>
                <strong><?= $totalAtivos ?></strong>
                <span>usuários com status ativo</span>
            </div>
        </div>
    </div>

    <div class="users-toolbar">
        <div class="toolbar-left">
            <div class="premium-filter">
                <i class="bi bi-funnel"></i>
                <select id="filterTipo">
                    <option value="">Todos os tipos</option>
                    <option value="teste">Somente testes</option>
                    <option value="cliente">Somente clientes</option>
                </select>
            </div>
            <div class="premium-filter">
                <i class="bi bi-search"></i>
                <input type="text" id="searchTable" placeholder="Buscar por MAC, usuário, nome, DNS...">
            </div>
        </div>
        <div class="toolbar-right">
            <?php if ($isAdmin || $isRev): ?>
            <button type="button" class="btn btn-primary-gradient btn-premium" data-bs-toggle="modal" data-bs-target="#claimPairingModal" id="btnClaimPairingCode">
                <i class="bi bi-key-fill"></i> Ativar por código
            </button>
            <?php endif; ?>
             <form method="post" class="m-0">
    <input type="hidden" name="export_backup_json" value="1">
    <button type="submit" class="btn btn-soft-secondary btn-premium" title="Salvar backup em JSON">
        <i class="bi bi-download"></i> Fazer Backup
    </button>
</form>

<input type="file" id="backupJsonInput" accept=".json,application/json" hidden>

<button type="button" class="btn btn-soft-secondary btn-premium" id="btnImportBackupJson" title="Carregar backup JSON">
    <i class="bi bi-upload"></i> Carregar Backup
</button>
            <button type="button" class="btn btn-soft-danger btn-premium" id="btnDeleteSelected" title="Apagar selecionados">
                <i class="bi bi-trash-fill"></i> Apagar
            </button>
         
        </div>
    </div>

    <div class="users-card">
        <div class="users-card-header">
            <div>
                <h5 class="users-card-title">Lista de usuários</h5>
                <p class="users-card-sub">Agora com envio de mensagem remota por MAC por usuário.</p>
            </div>
            <span class="total-pill"><i class="bi bi-stack"></i><?= $totalClientes ?> registro(s)</span>
        </div>

        <div class="card-body table-responsive p-0">
            <table class="table table-hover table-sm align-middle" id="usersTable">
                <thead>
                <tr>
                    <th class="selection-column"><input type="checkbox" id="checkAll"></th>
                    <th>MAC</th>
                    <th>Usuário</th>
                    <th>Tipo</th>
                    <th>Dispositivo</th>
                    <th>Nome</th>
                    <th>WhatsApp</th>
                    <th>Status</th>
                    <th>Vencimento</th>
                  
                    <th class="text-center">Ações</th>
                </tr>
                </thead>
                <tbody>
                <?php
                if ($isRev) {
                    $st = $db->prepare("
                        SELECT u.rowid, u.*,
                               dpi.device_name AS pending_device_name,
                               dpi.device_brand AS pending_device_brand,
                               dpi.device_model AS pending_device_model,
                               dpi.android_version AS pending_android_version,
                               dpi.app_version AS pending_app_version,
                               dpi.pending_reason AS pending_reason,
                               dpi.updated_at AS pending_updated_at
                        FROM usuarios u
                        LEFT JOIN device_pending_info dpi ON dpi.mac = u.mac
                        WHERE u.usuario_id = ?
                        ORDER BY u.rowid DESC
                    ");
                    $st->execute([$revId]);
                    $res = $st;
                } else {
                    $res = $db->query("SELECT u.rowid, u.*,
                        dpi.device_name AS pending_device_name, dpi.device_brand AS pending_device_brand,
                        dpi.device_model AS pending_device_model, dpi.android_version AS pending_android_version,
                        dpi.app_version AS pending_app_version, dpi.pending_reason AS pending_reason,
                        dpi.updated_at AS pending_updated_at
                        FROM usuarios u
                        LEFT JOIN device_pending_info dpi ON dpi.mac = u.mac
                        WHERE (u.usuario_id IS NULL OR u.usuario_id=0)
                        ORDER BY u.rowid DESC");
                }

                $hasRows = false;
                while ($u = $res->fetch()) {
                    $hasRows = true;
                    $macRow = (string)($u['mac'] ?? '');
                    $isTesteRow = !empty($u["teste"]);
                    $tipoTexto = $isTesteRow ? "teste" : "cliente";
                    $loginModeRow = trim((string)($u['login_mode'] ?? 'normal'));
                    $activationCodeRow = trim((string)($u['activation_code'] ?? ''));
                    $venc = "—";
                    $vencBadgeClass = "ok";
                    $vencTextPlain = "Sem vencimento";

                    if (!empty($u["vencimento"])) {
                        $vencDate = date("d/m/Y H:i", (int)$u["vencimento"]);
                        $vencTextPlain = $vencDate;
                        if ((int)$u["vencimento"] < time()) {
                            $vencBadgeClass = "expired";
                        }
                        $venc = '<span class="exp-badge ' . $vencBadgeClass . '"><i class="bi bi-clock-history"></i> ' . htmlspecialchars($vencDate) . '</span>';
                    }

                    $nomeCli = trim((string)($u["nome_cliente"] ?? ""));
                    $revendaLabelRow = trim((string)($u["revenda_nome"] ?? ""));
                    if ($revendaLabelRow === '') $revendaLabelRow = trim((string)($u["revenda_username"] ?? ""));
                    $whatsappNum = preg_replace('/\D/', '', (string)($u["whatsapp"] ?? ""));
                    $msgZap = urlencode("Olá {$nomeCli}, seu acesso vence em {$vencTextPlain}.");
                    $zap = "https://wa.me/55{$whatsappNum}?text={$msgZap}";

                    $msgRow = $messageMap[$macRow] ?? null;
                    $ruleRow = $ruleMap[$macRow] ?? null;
                    $profileRow = $profileMap[$macRow] ?? da_default_profile();
                    $serversRow = $serverMap[$macRow] ?? [];
                    if (!$serversRow && trim((string)($u['dns'] ?? '')) !== '' && trim((string)($u['username'] ?? '')) !== '' && trim((string)($u['password'] ?? '')) !== '') {
                        $serversRow = [[
                            'key' => 'legacy_' . substr(hash('sha256', $macRow . '|' . (string)$u['dns'] . '|' . (string)$u['username']), 0, 20),
                            'name' => 'Servidor principal',
                            'dns' => ms_normalize_dns((string)$u['dns']),
                            'username' => (string)$u['username'],
                            'password' => (string)$u['password'],
                            'order' => 0,
                            'primary' => true,
                        ]];
                    }
                    $serversJsonRow = json_encode($serversRow, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '[]';
                    $deviceLabel = (($profileRow['device_type'] ?? 'tv') === 'mobile') ? 'Mobile' : 'TV';
                    $isPendingActivationRow = trim((string)($u['dns'] ?? '')) === '' || trim((string)($u['username'] ?? '')) === '' || trim((string)($u['password'] ?? '')) === '' || in_array(strtolower(trim((string)($u['status'] ?? ''))), ['pending','pendente','aguardando','waiting'], true);
                    $rowOwnerId = (int)($u['usuario_id'] ?? 0);
                    $isUnclaimedPendingRow = $isRev && $rowOwnerId <= 0 && $isPendingActivationRow;
                    $statusLabelRow = $isUnclaimedPendingRow ? 'Disponível para ativar' : ($isPendingActivationRow ? 'Aguardando' : (string)($u['status'] ?? ''));
                    $profileUrlRow = pp_asset_url((string)($u['profile_image'] ?? ''), (int)($u['profile_updated_at'] ?? 0));


                    $msgBadgeClass = (!empty($msgRow) && (int)($msgRow['active'] ?? 0) === 1) ? 'msg-on' : 'msg-off';
                    $msgBadgeText = (!empty($msgRow) && (int)($msgRow['active'] ?? 0) === 1) ? 'Mensagem ativa' : 'Sem mensagem';

                    $blockedCount = 0;
                    if ($ruleRow) {
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_live_ids'] ?? '')));
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_movie_ids'] ?? '')));
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_series_ids'] ?? '')));
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_live_categories'] ?? '')));
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_movie_categories'] ?? '')));
                        $blockedCount += count(splitCsv((string)($ruleRow['blocked_series_categories'] ?? '')));
                    }
                    $ruleBadgeClass = ($blockedCount > 0 || (int)($ruleRow['allow_all_live'] ?? 1) === 0 || (int)($ruleRow['allow_all_movies'] ?? 1) === 0 || (int)($ruleRow['allow_all_series'] ?? 1) === 0) ? 'rule-off' : 'rule-on';
                    $ruleBadgeText = $blockedCount > 0 ? ($blockedCount . ' bloqueio(s)') : 'Sem bloqueios';
                ?>
                    <tr
                        data-tipo="<?= htmlspecialchars($tipoTexto) ?>"
                        data-search="<?= htmlspecialchars(mb_strtolower(
                            (string)($u["mac"] ?? "") . ' ' .
                            (string)($u["username"] ?? "") . ' ' .
                            (string)($u["dns"] ?? "") . ' ' .
                            (string)($u["nome_cliente"] ?? "") . ' ' .
                            (string)($u["whatsapp"] ?? "")
                        )) ?>"
                        data-mac="<?= htmlspecialchars($macRow) ?>"
                        data-username="<?= htmlspecialchars((string)$u["username"]) ?>"
                        data-password="<?= htmlspecialchars((string)$u["password"]) ?>"
                        data-dns="<?= htmlspecialchars((string)$u["dns"]) ?>"
                        data-servers="<?= htmlspecialchars($serversJsonRow, ENT_QUOTES, 'UTF-8') ?>"
                        data-status="<?= htmlspecialchars((string)$u["status"]) ?>"
                        data-venc="<?= !empty($u["vencimento"]) ? date("Y-m-d\TH:i", (int)$u["vencimento"]) : "" ?>"
                        data-nome="<?= htmlspecialchars((string)$u["nome_cliente"]) ?>"
                        data-whatsapp="<?= htmlspecialchars((string)$u["whatsapp"]) ?>"
                        data-profile-url="<?= htmlspecialchars($profileUrlRow, ENT_QUOTES, 'UTF-8') ?>"
                        data-tipo-registro="<?= htmlspecialchars($tipoTexto) ?>"
                        data-login-mode="<?= htmlspecialchars($loginModeRow) ?>"
                        data-activation-code="<?= htmlspecialchars($activationCodeRow) ?>"
                        data-message-title="<?= htmlspecialchars((string)($msgRow['title'] ?? 'Mensagem do servidor')) ?>"
                        data-message-text="<?= htmlspecialchars((string)($msgRow['message'] ?? '')) ?>"
                        data-message-tempo="<?= htmlspecialchars((string)($msgRow['tempo'] ?? '5')) ?>"
                        data-message-active="<?= htmlspecialchars((string)($msgRow['active'] ?? '0')) ?>"
                        data-message-shown-once="<?= htmlspecialchars((string)($msgRow['shown_once'] ?? '1')) ?>"
                        data-device-type="<?= htmlspecialchars((string)($profileRow['device_type'] ?? 'tv')) ?>"
                        data-allow-tv="<?= !empty($profileRow['allow_tv']) ? '1' : '0' ?>"
                        data-allow-mobile="<?= !empty($profileRow['allow_mobile']) ? '1' : '0' ?>"
                        data-allow-content-live="<?= !empty($profileRow['allow_live']) ? '1' : '0' ?>"
                        data-allow-content-movies="<?= !empty($profileRow['allow_movies']) ? '1' : '0' ?>"
                        data-allow-content-series="<?= !empty($profileRow['allow_series']) ? '1' : '0' ?>"
                        data-allow-live="<?= htmlspecialchars((string)($ruleRow['allow_all_live'] ?? '1')) ?>"
                        data-allow-movies="<?= htmlspecialchars((string)($ruleRow['allow_all_movies'] ?? '1')) ?>"
                        data-allow-series="<?= htmlspecialchars((string)($ruleRow['allow_all_series'] ?? '1')) ?>"
                        data-allow-tab-movies="<?= htmlspecialchars((string)($ruleRow['allow_tab_movies'] ?? ($ruleRow['allow_all_movies'] ?? '1'))) ?>"
                        data-allow-tab-series="<?= htmlspecialchars((string)($ruleRow['allow_tab_series'] ?? ($ruleRow['allow_all_series'] ?? '1'))) ?>"
                        data-allow-tab-kids="<?= htmlspecialchars((string)($ruleRow['allow_tab_kids'] ?? '1')) ?>"
                        data-allow-tab-anime="<?= htmlspecialchars((string)($ruleRow['allow_tab_anime'] ?? '1')) ?>"
                        data-allow-tab-novelas="<?= htmlspecialchars((string)($ruleRow['allow_tab_novelas'] ?? ($ruleRow['allow_tab_series'] ?? '1'))) ?>"
                        data-allow-tab-esportes="<?= htmlspecialchars((string)($ruleRow['allow_tab_esportes'] ?? '1')) ?>"
                        data-blocked-live-ids="<?= htmlspecialchars((string)($ruleRow['blocked_live_ids'] ?? '')) ?>"
                        data-blocked-movie-ids="<?= htmlspecialchars((string)($ruleRow['blocked_movie_ids'] ?? '')) ?>"
                        data-blocked-series-ids="<?= htmlspecialchars((string)($ruleRow['blocked_series_ids'] ?? '')) ?>"
                        data-blocked-live-categories="<?= htmlspecialchars((string)($ruleRow['blocked_live_categories'] ?? '')) ?>"
                        data-blocked-movie-categories="<?= htmlspecialchars((string)($ruleRow['blocked_movie_categories'] ?? '')) ?>"
                        data-blocked-series-categories="<?= htmlspecialchars((string)($ruleRow['blocked_series_categories'] ?? '')) ?>"
                    >
                        <td><input type="checkbox" class="chk" value="<?= htmlspecialchars($macRow) ?>" <?= $isUnclaimedPendingRow ? 'disabled title="Ative o MAC antes de selecioná-lo"' : '' ?>></td>
                        <td class="mac"><?= htmlspecialchars($macRow) ?></td>
                        <td>
                            <div class="user-main">
                                <strong><?= htmlspecialchars((string)$u["username"]) ?></strong>
                                <span>Senha: <?= htmlspecialchars((string)$u["password"]) ?></span>
                                <span class="server-count-badge"><i class="bi bi-hdd-network"></i> <?= count($serversRow) ?> servidor(es)</span>
                            </div>
                        </td>
                        <td>
                            <span class="type-badge <?= $isTesteRow ? 'teste' : 'cliente' ?>">
                                <i class="bi <?= $isTesteRow ? 'bi-lightning-charge-fill' : 'bi-person-badge-fill' ?>"></i>
                                <?= $isTesteRow ? 'Teste' : 'Cliente' ?>
                            </span><br><small>Login app: <?= htmlspecialchars($loginModeRow) ?><?php if ($activationCodeRow !== '') { ?><br>Código: <strong><?= htmlspecialchars($activationCodeRow) ?></strong><?php } ?></small>
                        </td>
                        <td>
                            <span class="pill"><i class="bi bi-phone"></i> <?= htmlspecialchars($deviceLabel) ?></span>
                            <?php if (trim((string)($u['pending_device_name'] ?? '')) !== ''): ?>
                                <br><small class="text-muted"><?= htmlspecialchars((string)$u['pending_device_name']) ?></small>
                            <?php endif; ?>
                            <?php if (trim((string)($u['pending_android_version'] ?? '')) !== '' || trim((string)($u['pending_app_version'] ?? '')) !== ''): ?>
                                <br><small class="text-muted"><?= htmlspecialchars(trim((string)($u['pending_android_version'] ?? '')) . (trim((string)($u['pending_app_version'] ?? '')) !== '' ? ' • App ' . trim((string)$u['pending_app_version']) : '')) ?></small>
                            <?php endif; ?>
                        </td>
                        <td>
                            <div class="profile-user-cell">
                                <?php if ($profileUrlRow !== ''): ?>
                                    <img class="profile-user-avatar" src="<?= htmlspecialchars($profileUrlRow, ENT_QUOTES, 'UTF-8') ?>" alt="Foto do perfil" data-image-error="show-next">
                                    <div class="profile-user-avatar profile-user-fallback is-hidden"><i class="bi bi-person-fill"></i></div>
                                <?php else: ?>
                                    <div class="profile-user-avatar profile-user-fallback"><i class="bi bi-person-fill"></i></div>
                                <?php endif; ?>
                                <div>
                                    <?= htmlspecialchars((string)$u["nome_cliente"]) ?>
                                    <?php if ($isUnclaimedPendingRow): ?>
                                        <br><small class="text-warning fw-bold"><i class="bi bi-person-plus-fill"></i> MAC pendente sem revenda</small>
                                    <?php elseif ($isAdmin && $revendaLabelRow !== ''): ?>
                                        <br><small class="text-muted">Revenda: <?= htmlspecialchars($revendaLabelRow, ENT_QUOTES, 'UTF-8') ?></small>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </td>
                        <td><?= htmlspecialchars((string)$u["whatsapp"]) ?></td>
                        <td>
                            <span class="status-badge <?= $isPendingActivationRow ? 'aguardando' : (((string)($u["status"] ?? '') === 'ativo') ? 'ativo' : 'inativo') ?>">
                                <i class="bi <?= $isPendingActivationRow ? 'bi-hourglass-split' : (((string)($u["status"] ?? '') === 'ativo') ? 'bi-check-circle-fill' : 'bi-x-circle-fill') ?>"></i>
                                <?= htmlspecialchars($statusLabelRow) ?>
                            </span>
                        </td>
                        <td><?= $venc ?></td>
                       
                        <td class="text-center">
                            <div class="actions">
                                <?php if (!$isUnclaimedPendingRow): ?>
                                    <button type="button" class="action-btn btn-edit" title="Editar">
                                        <i class="bi bi-pencil-square text-primary"></i>
                                    </button>
                                    <button type="button" class="action-btn btn-access" title="Bloqueios e acesso">
                                        <i class="bi bi-shield-lock text-warning"></i>
                                    </button>
                                <?php endif; ?>
                                <?php if ($isPendingActivationRow): ?>
                                    <button type="button" class="action-btn btn-activate-user action-wide" title="Ativar e vincular à sua revenda" data-mac="<?= htmlspecialchars($macRow) ?>">
                                        <i class="bi bi-lightning-charge-fill text-success"></i><span><?= $isUnclaimedPendingRow ? 'Ativar e vincular' : 'Ativar' ?></span>
                                    </button>
                                <?php endif; ?>
                                <?php if (!$isUnclaimedPendingRow): ?>
                                    <button type="button" class="action-btn btn-delete" data-mac="<?= htmlspecialchars($macRow) ?>" title="Apagar">
                                        <i class="bi bi-trash text-danger"></i>
                                    </button>
                                <?php endif; ?>
                            </div>
                        </td>
                    </tr>
                <?php } ?>

                <?php if (!$hasRows) { ?>
                    <tr>
                        <td colspan="12">
                            <div class="empty-state">
                                <i class="bi bi-inboxes"></i>
                                <div>Nenhum usuário encontrado.</div>
                            </div>
                        </td>
                    </tr>
                <?php } ?>
                </tbody>
            </table>
        </div>
    </div>

</div>
</div>
</div>
<div class="modal fade" id="editModal" tabindex="-1">
    <div class="modal-dialog modal-lg modal-dialog-centered">
        <div class="modal-content">
            <form id="editForm" enctype="multipart/form-data">
                <div class="modal-header">
                    <h5 class="modal-title"><i class="bi bi-pencil-square"></i> Editar usuário</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <input type="hidden" name="edit_save" value="1">
                    <input type="hidden" name="mac">
                    <input type="hidden" name="login_mode">
                    <input type="hidden" name="activation_code">
                    <input type="hidden" name="username">
                    <input type="hidden" name="password">
                    <input type="hidden" name="dns">
                    <input type="hidden" name="servers_json" id="serversJsonInput" value="[]">

                    <div class="multi-server-editor">
                        <div class="multi-server-head">
                            <div>
                                <div class="multi-server-title"><i class="bi bi-hdd-network"></i> Multisservidor</div>
                                <div class="multi-server-sub">Cadastre DNS, usuário e senha independentes. O primeiro servidor será o padrão.</div>
                            </div>
                            <button type="button" class="btn btn-primary-gradient btn-sm" id="btnAddServer"><i class="bi bi-plus-lg"></i> Adicionar servidor</button>
                        </div>
                        <div id="serverEditorList" class="server-editor-list"></div>
                    </div>

                    <div class="row g-3 mt-1">
                        <div class="col-md-6"><label class="modal-label">Status</label><select name="status" class="form-select"><option value="ativo">Ativo</option><option value="inativo">Inativo</option></select></div>
                        <div class="col-md-6"><label class="modal-label">Vencimento</label><input type="datetime-local" name="vencimento" class="form-control"></div>
                        <div class="col-md-6"><label class="modal-label">WhatsApp</label><input name="whatsapp" class="form-control" placeholder="WhatsApp"></div>
                        <div class="col-md-6"><label class="modal-label">Tipo</label><select name="tipo_registro" class="form-select"><option value="cliente">Cliente</option><option value="teste">Teste</option></select></div>
                        <div class="col-md-6"><label class="modal-label">MAC</label><input class="form-control" id="macReadonly" readonly></div>
                        <div class="col-12"><label class="modal-label">Nome do cliente</label><input name="nome_cliente" class="form-control" placeholder="Nome do Cliente"></div>
                        <div class="col-12">
                            <label class="modal-label">Foto do perfil sincronizada</label>
                            <div class="profile-edit-box">
                                <img id="editProfilePreview" class="profile-edit-preview" alt="Prévia do perfil">
                                <div class="flex-grow-1">
                                    <input type="file" name="profile_image" id="editProfileImage" class="form-control" accept="image/jpeg,image/png,image/webp">
                                    <div class="form-check mt-2"><input class="form-check-input" type="checkbox" name="delete_profile_image" value="1" id="deleteProfileImage"><label class="form-check-label" for="deleteProfileImage">Apagar foto atual nos dois apps</label></div>
                                    <small class="text-muted">JPG, PNG ou WEBP, até 5 MB.</small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-light btn-premium" data-bs-dismiss="modal">Cancelar</button>
                    <button type="submit" class="btn btn-primary-gradient btn-premium"><i class="bi bi-check2"></i> Salvar alterações</button>
                </div>
            </form>
        </div>
    </div>
</div>

<!-- ╔══════════════════════════════════════════════════════╗
     ║  Modal de Bloqueios — Interface limpa (só switches)  ║
     ╚══════════════════════════════════════════════════════╝ -->
<div class="modal fade" id="accessModal" tabindex="-1">
    <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
        <div class="modal-content">
            <form id="accessForm">
                <div class="modal-header access-modal-header">
                    <div class="d-flex align-items-center gap-2">
                        <div class="access-modal-icon">
                            <i class="bi bi-shield-lock"></i>
                        </div>
                        <div>
                            <h5 class="modal-title mb-0 fw-900">Controle de Acesso</h5>
                            <p class="mb-0 access-mac-label" id="accessMacLabel">—</p>
                        </div>
                    </div>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>

                <div class="modal-body access-modal-body">
                    <input type="hidden" name="save_access_rules" value="1">
                    <input type="hidden" name="mac" id="accessMac">
                    <input type="hidden" name="allow_tv" id="allowTv" value="1">
                    <input type="hidden" name="allow_mobile" id="allowMobile" value="1">
                    <input type="hidden" name="allow_content_movies" id="allowContentMovies" value="1">
                    <input type="hidden" name="allow_content_series" id="allowContentSeries" value="1">
                    <input type="hidden" name="allow_content_live" id="allowContentLive" value="1">
                    <input type="hidden" name="allow_all_live" id="allowAllLive" value="1">
                    <input type="hidden" name="allow_all_movies" id="allowAllMovies" value="1">
                    <input type="hidden" name="allow_all_series" id="allowAllSeries" value="1">
                    <input type="hidden" name="allow_tab_movies" id="allowTabMovies" value="1">
                    <input type="hidden" name="allow_tab_series" id="allowTabSeries" value="1">
                    <input type="hidden" name="allow_tab_kids" id="allowTabKids" value="1">
                    <input type="hidden" name="allow_tab_anime" id="allowTabAnime" value="1">
                    <input type="hidden" name="allow_tab_novelas" id="allowTabNovelas" value="1">
                    <input type="hidden" name="allow_tab_esportes" id="allowTabEsportes" value="1">
                    <input type="hidden" name="blocked_live_ids" id="blockedLiveIds" value="">
                    <input type="hidden" name="blocked_movie_ids" id="blockedMovieIds" value="">
                    <input type="hidden" name="blocked_series_ids" id="blockedSeriesIds" value="">
                    <input type="hidden" name="blocked_live_categories" id="blockedLiveCategories" value="">
                    <input type="hidden" name="blocked_movie_categories" id="blockedMovieCategories" value="">
                    <input type="hidden" name="blocked_series_categories" id="blockedSeriesCategories" value="">
                    <div class="mb-1">
                        <div class="card-soft-title access-section-title">Abas do App</div>
                        <div class="access-tab-grid">
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabMoviesSwitch" checked><label class="form-check-label fw-bold" for="allowTabMoviesSwitch">Filmes</label></div><small id="moviesStatusText">Liberado</small></div>
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabSeriesSwitch" checked><label class="form-check-label fw-bold" for="allowTabSeriesSwitch">Séries</label></div><small id="seriesStatusText">Liberado</small></div>
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabKidsSwitch" checked><label class="form-check-label fw-bold" for="allowTabKidsSwitch">Kids</label></div><small id="kidsStatusText">Liberado</small></div>
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabAnimeSwitch" checked><label class="form-check-label fw-bold" for="allowTabAnimeSwitch">Animes</label></div><small id="animeStatusText">Liberado</small></div>
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabNovelasSwitch" checked><label class="form-check-label fw-bold" for="allowTabNovelasSwitch">Novelas</label></div><small id="novelasStatusText">Liberado</small></div>
                            <div class="access-switch-card"><div class="form-check form-switch m-0"><input class="form-check-input" type="checkbox" id="allowTabEsportesSwitch" checked><label class="form-check-label fw-bold" for="allowTabEsportesSwitch">Esportes</label></div><small id="esportesStatusText">Liberado</small></div>
                        </div>
                    </div>

                    <!-- Nota informativa -->
                    <div class="card-soft mt-3 access-realtime-card">
                        <div class="d-flex align-items-start gap-2">
                            <i class="bi bi-lightning-charge-fill access-realtime-icon"></i>
                            <div>
                                <div class="access-realtime-title">Bloqueio em tempo real</div>
                                <div class="text-muted access-realtime-copy">As alterações refletem nos apps TV e Celular em até 30 segundos, sem necessidade de reiniciar.</div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="modal-footer access-modal-footer">
                    <button type="button" class="btn btn-light btn-premium" data-bs-dismiss="modal">Cancelar</button>
                    <button type="submit" class="btn btn-primary-gradient btn-premium">
                        <i class="bi bi-shield-check"></i> Salvar bloqueios
                    </button>
                </div>
            </form>
        </div>
    </div>
</div>
<?php if ($isAdmin || $isRev): ?>
<div class="modal fade" id="claimPairingModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
            <form method="post" autocomplete="off" id="claimPairingForm">
                <div class="modal-header">
                    <h5 class="modal-title"><i class="bi bi-key-fill"></i> Ativar cliente por código</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <input type="hidden" name="claim_pairing_code" value="1">
                    <label class="modal-label">Código exibido no aplicativo</label>
                    <input type="text" name="pairing_code" id="pairingCodeInput" class="form-control text-center" inputmode="numeric" pattern="[0-9]{4}" maxlength="4" placeholder="0000" required>
                    <div class="alert alert-info mt-3 mb-0">O MAC será vinculado a <?= $isAdmin ? 'conta do administrador' : 'sua revenda' ?>. Depois, a tela do link M3U será aberta automaticamente.</div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-light btn-premium" data-bs-dismiss="modal">Cancelar</button>
                    <button type="submit" class="btn btn-primary-gradient btn-premium"><i class="bi bi-person-check-fill"></i> Salvar e vincular</button>
                </div>
            </form>
        </div>
    </div>
</div>
<?php endif; ?>

<div class="modal fade" id="activateUserModal" tabindex="-1">
    <div class="modal-dialog modal-lg modal-dialog-centered">
        <div class="modal-content">
            <form id="activateUserForm">
                <div class="modal-header">
                    <h5 class="modal-title"><i class="bi bi-lightning-charge-fill"></i> Ativar cliente aguardando</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <input type="hidden" name="activate_pending_user" value="1">
                    <div class="row g-3">
                        <div class="col-md-6">
                            <label class="modal-label">MAC</label>
                            <input type="text" name="mac" id="activateUserMac" class="form-control" readonly>
                        </div>
                        <div class="col-md-6">
                            <label class="modal-label">Validade da conta</label>
                            <input type="datetime-local" name="vencimento" id="activateUserVencimento" class="form-control">
                        </div>
                        <div class="col-12">
                            <label class="modal-label">Link M3U</label>
                            <input type="url" name="m3u_url" id="activateUserM3u" class="form-control" placeholder="http://servidor.com/get.php?username=usuario&password=senha&type=m3u&output=ts" required>
                        </div>
                        <div class="col-12">
                            <div class="alert alert-info mb-0">Ao ativar, o painel separa automaticamente DNS, usuário e senha do link M3U e libera o MAC no app.</div>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-light btn-premium" data-bs-dismiss="modal">Cancelar</button>
                    <button type="submit" class="btn btn-primary-gradient btn-premium"><i class="bi bi-check2-circle"></i> Ativar</button>
                </div>
            </form>
        </div>
    </div>
</div>

<div class="modal fade" id="confirmDeleteModal" tabindex="-1">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content text-center">
            <div class="modal-body confirm-modal-body">
                <div class="confirm-modal-icon">
                    <i class="bi bi-trash3-fill"></i>
                </div>
                <h5 class="text-danger fw-1000">Confirmar exclusão</h5>
                <p class="confirm-modal-copy">Deseja apagar o(s) usuário(s) selecionado(s)?</p>
                <div class="d-flex justify-content-center gap-2 mt-3">
                    <button type="button" class="btn btn-light btn-premium" data-bs-dismiss="modal">Cancelar</button>
                    <button type="button" class="btn btn-danger btn-premium" id="confirmDelete"><i class="bi bi-trash-fill"></i> Apagar</button>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="successModal" tabindex="-1">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content text-center">
            <div class="modal-body confirm-modal-body">
                <div class="success-hero"><i class="bi bi-check2-circle"></i></div>
                <h5 class="success-title">Operação realizada</h5>
                <p class="success-sub">Alterações aplicadas com sucesso.</p>
                <button type="button" class="btn btn-success btn-premium mt-3" data-bs-dismiss="modal"><i class="bi bi-check2"></i> Ok</button>
            </div>
        </div>
    </div>
</div>
<div id="usersRuntime" hidden data-auto-mac="<?= htmlspecialchars((string)$pairingAutoActivateMac, ENT_QUOTES, 'UTF-8') ?>"></div>
<?php require_once __DIR__ . '/../includes/footer.php'; ?>
