<?php
declare(strict_types=1);

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/_trial_service.php';
require_once dirname(__DIR__) . '/api/_app_guard.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) {}

/* ============================================================
 * 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 (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'));
    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;
    }

    $appSessionCheck = ((string)($_REQUEST['session_check'] ?? $_REQUEST['logged_session'] ?? '') === '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 () 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) {}
        $qrPath = dirname(__DIR__) . '/api/img/qrcode.png';
        $qrUrl = is_file($qrPath) ? rtrim($blockPublicBase(), '/') . '/api/img/qrcode.png?v=' . filemtime($qrPath) : '';
        return ['block_title'=>trim((string)($cfg['title'] ?? 'Acesso bloqueado')) ?: 'Acesso bloqueado','block_message'=>trim((string)($cfg['message'] ?? 'Seu acesso está bloqueado ou vencido. Escaneie o QR Code e fale com seu revendedor para regularizar.')),'block_show_qrcode'=>((int)($cfg['show_qrcode'] ?? 1) === 1),'block_qrcode_url'=>$qrUrl,'block_updated_at'=>(int)($cfg['updated_at'] ?? 0)];
    };
    $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(); };

    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);
        $autoMacEnabled = $activeMode === 'mac' && (int)($testCfg['auto_mac_enabled'] ?? 0) === 1 && (int)($testCfg['enabled'] ?? 0) === 1;

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

        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) {
            if ($appSessionCheck) {
                echo json_encode([
                    'status' => 'not_registered',
                    'mac_registered' => false,
                    'mac_pending' => false,
                    'mac_has_credentials' => false,
                    'logout' => true,
                    'reason' => 'user_deleted',
                    'message' => 'Conta removida do painel.'
                ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                exit;
            }
            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' => '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) {
            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,
                'can_auto_trial' => $autoMacEnabled,
                'logout' => false,
                'message' => $autoMacEnabled ? 'Gerando teste automático ou aguardando DNS, usuário e senha.' : 'Aguardando DNS, usuário e senha.'
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        $statusUser = strtolower(trim((string)($row['status'] ?? '')));
        $panelVencimentoExpired = $panelVencExpired($row['vencimento'] ?? '');
        if (in_array($statusUser, ['disabled', 'banned', 'bloqueado', 'inativo'], true) || $panelVencimentoExpired) {
            $blockCfg = $getBlockConfig();
            echo json_encode(array_merge([
                'status' => 'ok', 'mac_registered' => true, 'mac_pending' => false, 'mac_has_credentials' => true, 'logout' => false,
                'blocked' => true, 'block_reason' => $panelVencimentoExpired ? 'expired' : 'inactive',
                'message' => $panelVencimentoExpired ? 'Conta vencida no painel.' : 'Acesso bloqueado pelo 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()
            ], $blockCfg), 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()], $getBlockConfig()), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            } else {
                echo json_encode(['status'=>'invalid','mac_registered'=>true,'mac_pending'=>false,'mac_has_credentials'=>true,'logout'=>false,'message'=>'Usuário, senha ou DNS inválidos.'], 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(),
        ], $getBlockConfig()), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($apiAction === 'device_pulse') {
        $st = $db->prepare("SELECT * FROM usuarios WHERE mac = ? LIMIT 1");
        $st->execute([$appMac]);
        $row = $st->fetch(PDO::FETCH_ASSOC) ?: null;
        $serverTs = time();
        header('Content-Type: application/json; charset=utf-8');
        if (!$row) {
            echo json_encode([
                'status'   => 'ok',
                'logout'   => true,
                'reason'   => 'mac_not_registered',
                'server_ts'=> $serverTs,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        if (!auth_user_has_credentials($row)) {
            try { da_update_last_device($db, $appMac, $appDevice); } catch (Throwable $e) {}
            echo json_encode([
                'status' => 'waiting',
                'mac_registered' => true,
                'mac_pending' => true,
                'logout' => false,
                'reason' => 'mac_pending',
                'server_ts' => $serverTs,
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        $userKey = da_user_key((string)$row['username'], (string)$row['password']);
        $profile = da_get_profile_by_key($db, $userKey);
        $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 ($panelVencExpired($row['vencimento'] ?? '')) { $blocked = true; $reason = 'expired'; }
        elseif ($statusUser !== '' && !in_array($statusUser, ['ativo', 'active'], true)) { $blocked = true; $reason = 'inactive'; }
        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,
            '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,
        ], $getBlockConfig()), 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;
}

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,
        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',
    ] 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"])) {
    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);
        }

        $hasUpdatedAt = columnExists($db, 'usuarios', 'updated_at');
        $ownerSql = $isRev ? " AND usuario_id=?" : " AND (usuario_id IS NULL OR usuario_id=0)";
        $sql = "UPDATE usuarios SET dns=?, username=?, password=?, status=?, vencimento=?" . ($hasUpdatedAt ? ", updated_at=?" : "") . " WHERE mac=? $ownerSql";
        $params = [$parsed['dns'], $parsed['username'], $parsed['password'], 'ativo', (string)$vencTs];
        if ($hasUpdatedAt) {
            $params[] = time();
        }
        $params[] = $mac;
        if ($isRev) {
            $params[] = $revId;
        }

        $st = $db->prepare($sql);
        $st->execute($params);

        if ($st->rowCount() <= 0) {
            throw new RuntimeException('Cliente não encontrado ou sem permissão para ativar.');
        }

        jsonOut(['status' => 'success', 'message' => 'Cliente ativado com sucesso.']);
    } catch (Throwable $e) {
        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]);
            }
        }

        $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]);
            $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 {
                    $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["activate_pending_user"])) {
    try {
        $mac = normalizeMac((string)($_POST['mac'] ?? ''));
        if ($mac === '') {
            throw new RuntimeException('MAC inválido.');
        }
        $m3u = parseM3uActivationUrl((string)($_POST['m3u_url'] ?? ''));
        $vencTs = parseActivationExpiry((string)($_POST['vencimento'] ?? ''));
        $ownerWhere = $isRev ? 'mac=? AND usuario_id=?' : 'mac=? AND (usuario_id IS NULL OR usuario_id=0)';
        $ownerParams = $isRev ? [$mac, $revId] : [$mac];
        $fields = 'dns=?, username=?, password=?, status=?, vencimento=?';
        $params = [$m3u['dns'], $m3u['username'], $m3u['password'], 'ativo', $vencTs];
        if (hasColumn($db, 'usuarios', 'updated_at')) {
            $fields .= ', updated_at=?';
            $params[] = time();
        }
        $params = array_merge($params, $ownerParams);
        $st = $db->prepare("UPDATE usuarios SET {$fields} WHERE {$ownerWhere}");
        $ok = $st->execute($params);
        jsonOut(['status' => $ok ? 'success' : 'error']);
    } catch (Throwable $e) {
        jsonOut(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["edit_save"])) {
    $mac = trim((string)($_POST["mac"] ?? ""));
    $username = trim((string)($_POST["username"] ?? ""));
    $password = (string)($_POST["password"] ?? "");
    $dns = trim((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 = ''; }

    $venc_ts = !empty($vencimento) ? strtotime($vencimento) : null;
    $testeValue = ($tipoRegistro === "teste") ? 1 : null;

    if ($isRev) {
        $up = $db->prepare("
            UPDATE usuarios
            SET username=?, password=?, dns=?, status=?, vencimento=?, nome_cliente=?, whatsapp=?, teste=?, login_mode=?, activation_code=?
            WHERE mac=? AND usuario_id=?
        ");

        jsonOut([
            "status" => $up->execute([
                $username,
                $password,
                $dns,
                $status,
                $venc_ts,
                $nomeCliente,
                $whatsapp,
                $testeValue,
                $loginMode,
                $activationCode,
                $mac,
                $revId
            ]) ? "success" : "error"
        ]);
    }

    $up = $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)
    ");

    jsonOut([
        "status" => $up->execute([
            $username,
            $password,
            $dns,
            $status,
            $venc_ts,
            $nomeCliente,
            $whatsapp,
            $testeValue,
            $loginMode,
            $activationCode,
            $mac
        ]) ? "success" : "error"
    ]);
}

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;

    $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,
        '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=?,
            updated_at=?
            WHERE id=?");
        $ok = $up->execute([
            $allowAllLive,
            $allowAllMovies,
            $allowAllSeries,
            $blockedLiveIds,
            $blockedMovieIds,
            $blockedSeriesIds,
            $blockedLiveCategories,
            $blockedMovieCategories,
            $blockedSeriesCategories,
            $allowTabMovies,
            $allowTabSeries,
            $allowTabKids,
            $allowTabAnime,
            $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,
        usuario_id,
        updated_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    $ok = $ins->execute([
        $mac,
        $allowAllLive,
        $allowAllMovies,
        $allowAllSeries,
        $blockedLiveIds,
        $blockedMovieIds,
        $blockedSeriesIds,
        $blockedLiveCategories,
        $blockedMovieCategories,
        $blockedSeriesCategories,
        $allowTabMovies,
        $allowTabSeries,
        $allowTabKids,
        $allowTabAnime,
        $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')),
    ];
}

include "./includes/header.php";
?>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

<style>
.dns-wrap{
    width:100%;
    max-width:none;
    margin:0;
    padding:0 18px;
}
.dns-card{
    border-radius:18px;
    overflow:hidden;
    width:100%;
    margin-bottom:18px;
}
.dns-header{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:14px;
    padding:18px 22px;
}
.dns-title{
    display:flex;
    align-items:center;
    gap:14px;
}
.dns-title .ic{
    width:44px;
    height:44px;
    min-width:44px;
    border-radius:14px;
    background:rgba(25,135,84,.10);
    border:1px solid rgba(25,135,84,.20);
    display:flex;
    align-items:center;
    justify-content:center;
}
.dns-title .ic i{
    font-size:24px;
    color:#198754;
}
.dns-title h5{
    margin:0;
    font-weight:900;
}
.dns-title span{
    display:block;
    margin-top:3px;
    font-size:13px;
    color:rgba(255,255,255,.55);
}
.badge-soft{
    display:inline-flex;
    align-items:center;
    gap:7px;
    padding:8px 12px;
    border-radius:999px;
    background:rgba(25,135,84,.10);
    border:1px solid rgba(25,135,84,.20);
    color:#198754;
    font-weight:900;
    white-space:nowrap;
}
.users-page{
    width:100%;
    max-width:none;
    margin:0;
    padding:0;
}
.stats-grid{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:14px;
    margin-bottom:18px;
}
.stat-card{
    border-radius:18px;
    overflow:hidden;
    border:1px solid rgba(0,0,0,.06);
    background:rgba(255,255,255,.02);
    padding:16px;
    display:flex;
    gap:14px;
    align-items:center;
    transition:transform .12s ease, box-shadow .12s ease;
}
.stat-card:hover{
    transform:translateY(-2px);
    box-shadow:0 12px 30px rgba(0,0,0,.10);
}
.stat-icon{
    width:62px;
    height:62px;
    min-width:62px;
    border-radius:18px;
    display:flex;
    align-items:center;
    justify-content:center;
    background:rgba(0,0,0,.03);
    border:1px solid rgba(0,0,0,.05);
}
.stat-icon i{
    font-size:24px;
}
.stat-icon.primary{
    color:#0d6efd;
    background:rgba(13,110,253,.08);
    border-color:rgba(13,110,253,.18);
}
.stat-icon.success{
    color:#198754;
    background:rgba(25,135,84,.10);
    border-color:rgba(25,135,84,.20);
}
.stat-icon.warning{
    color:#f59e0b;
    background:rgba(245,158,11,.12);
    border-color:rgba(245,158,11,.18);
}
.stat-meta small{
    display:block;
    font-size:12px;
    text-transform:uppercase;
    letter-spacing:.7px;
    opacity:.75;
    font-weight:900;
}
.stat-meta strong{
    display:block;
    font-size:28px;
    font-weight:1000;
    line-height:1;
}
.stat-meta span{
    display:block;
    margin-top:4px;
    font-size:13px;
    opacity:.75;
}
.users-toolbar{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:14px;
    flex-wrap:wrap;
    margin-bottom:18px;
}
.toolbar-left,
.toolbar-right{
    display:flex;
    gap:10px;
    flex-wrap:wrap;
    align-items:center;
}
.premium-filter{
    display:flex;
    align-items:center;
    gap:10px;
    border:1px solid rgba(0,0,0,.08);
    border-radius:14px;
    padding:10px 12px;
    background:rgba(0,0,0,.03);
    min-height:48px;
}
.premium-filter i{
    color:#198754;
    font-size:18px;
}
.premium-filter select,
.premium-filter input{
    border:0 !important;
    outline:none !important;
    box-shadow:none !important;
    background:transparent !important;
    min-width:170px;
    font-weight:800;
}
.btn-premium{
    border-radius:14px;
    min-height:46px;
    padding:0 18px;
    font-weight:900;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:8px;
}
.btn-primary-gradient{
    color:#fff !important;
    background:#198754 !important;
    border-color:#198754 !important;
}
.btn-soft-danger{
    color:#dc3545 !important;
    background:rgba(220,53,69,.10) !important;
    border:1px solid rgba(220,53,69,.20) !important;
}
.btn-soft-secondary{
    color:#6c757d !important;
    background:rgba(108,117,125,.10) !important;
    border:1px solid rgba(108,117,125,.18) !important;
}
.users-card{
    border-radius:18px;
    overflow:hidden;
    width:100%;
    border:1px solid rgba(0,0,0,.06);
    background:rgba(255,255,255,.02);
}
.users-card-header{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:14px;
    flex-wrap:wrap;
    padding:18px 22px;
    border-bottom:1px solid rgba(0,0,0,.06);
    background:rgba(0,0,0,.02);
}
.users-card-title{
    margin:0;
    font-weight:900;
    font-size:20px;
}
.users-card-sub{
    margin:4px 0 0;
    font-size:13px;
    color:rgba(255,255,255,.55);
}
.total-pill{
    display:inline-flex;
    align-items:center;
    gap:8px;
    border-radius:999px;
    padding:8px 12px;
    background:rgba(25,135,84,.10);
    border:1px solid rgba(25,135,84,.20);
    color:#198754;
    font-weight:900;
    white-space:nowrap;
}
.table{
    margin:0;
}
.table thead th{
    font-size:12px;
    text-transform:uppercase;
    letter-spacing:.6px;
    opacity:.75;
    font-weight:1000;
    white-space:nowrap;
}
.table td{
    padding-top:14px;
    padding-bottom:14px;
    vertical-align:middle;
}
.mac{
    font-weight:1000;
    letter-spacing:.2px;
}
.user-main{
    display:flex;
    flex-direction:column;
    gap:4px;
}
.user-main strong{
    font-size:14px;
    font-weight:1000;
}
.user-main span,
.user-sub{
    font-size:12px;
    opacity:.75;
}
.type-badge,
.status-badge,
.exp-badge,
.mini-badge,
.badge{
    display:inline-flex;
    align-items:center;
    gap:7px;
    border-radius:999px;
    padding:7px 10px;
    font-size:12px;
    font-weight:900;
    white-space:nowrap;
}
.type-badge.teste,
.status-badge.aguardando,
.mini-badge.rule-off{
    background:rgba(245,158,11,.12);
    color:#b45309;
    border:1px solid rgba(245,158,11,.18);
}
.type-badge.cliente,
.mini-badge.msg-on{
    background:rgba(13,110,253,.10);
    color:#0d6efd;
    border:1px solid rgba(13,110,253,.18);
}
.status-badge.ativo,
.mini-badge.rule-on{
    background:rgba(25,135,84,.10);
    color:#198754;
    border:1px solid rgba(25,135,84,.20);
}
.status-badge.inativo,
.exp-badge.expired{
    background:rgba(220,53,69,.10);
    color:#dc3545;
    border:1px solid rgba(220,53,69,.20);
}
.exp-badge.ok,
.mini-badge.msg-off{
    background:rgba(108,117,125,.10);
    color:#6c757d;
    border:1px solid rgba(108,117,125,.18);
}
.actions{
    display:flex;
    gap:8px;
    justify-content:center;
    flex-wrap:wrap;
}
.action-btn{
    min-width:42px;
    height:40px;
    border-radius:14px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:7px;
    text-decoration:none;
    border:1px solid rgba(0,0,0,.08);
    background:rgba(0,0,0,.03);
    transition:transform .12s ease, box-shadow .12s ease;
    font-weight:900;
    padding:0 10px;
}
.action-btn:hover{
    transform:translateY(-1px);
    box-shadow:0 10px 20px rgba(0,0,0,.12);
}
.action-btn i{
    font-size:17px;
}
.form-control,
.form-select{
    border-radius:14px !important;
    min-height:48px;
    box-shadow:none !important;
    padding-left:14px;
    padding-right:14px;
}
.form-control:focus,
.form-select:focus{
    border-color:rgba(25,135,84,.45) !important;
    box-shadow:0 0 0 4px rgba(25,135,84,.08) !important;
}
.modal-content{
    border-radius:20px !important;
    overflow:hidden;
}
.modal-header{
    background:rgba(0,0,0,.02);
    border-bottom:1px solid rgba(0,0,0,.06);
}
.modal-label{
    font-size:12px;
    font-weight:900;
    text-transform:uppercase;
    letter-spacing:.08em;
    margin-bottom:8px;
}
.card-soft{
    background:rgba(0,0,0,.03);
    border:1px solid rgba(0,0,0,.08);
    border-radius:16px;
    padding:14px;
}
.card-soft-title{
    font-size:12px;
    font-weight:1000;
    text-transform:uppercase;
    letter-spacing:.08em;
    margin-bottom:10px;
}
.success-hero{
    display:flex;
    align-items:center;
    justify-content:center;
    width:76px;
    height:76px;
    border-radius:24px;
    margin:4px auto 14px auto;
    background:rgba(25,135,84,.12);
}
.success-hero i{
    font-size:36px;
    color:#198754;
}
.success-title{
    font-weight:1000;
    font-size:19px;
    margin:0;
    text-align:center;
}
.success-sub{
    opacity:.85;
    margin:8px 0 0 0;
    text-align:center;
    font-size:13px;
}
.empty-state{
    padding:40px 20px;
    text-align:center;
    opacity:.75;
}
.empty-state i{
    font-size:42px;
    margin-bottom:10px;
    display:block;
}
@media(max-width:1200px){
    .table-responsive{
        overflow-x:auto;
    }
}
@media(max-width:992px){
    .stats-grid{
        grid-template-columns:1fr;
    }
}
@media(max-width:768px){
    .users-toolbar{
        flex-direction:column;
        align-items:stretch;
    }
    .toolbar-left,
    .toolbar-right{
        width:100%;
    }
    .premium-filter{
        width:100%;
    }
    .premium-filter select,
    .premium-filter input{
        width:100%;
        min-width:0;
    }
}
@media(max-width:575px){
    .dns-wrap{
        padding:0 8px;
    }
    .dns-header{
        align-items:flex-start;
        padding:16px;
    }
    .dns-title .ic{
        width:40px;
        height:40px;
        min-width:40px;
    }
    .dns-title .ic i{
        font-size:22px;
    }
    .dns-title h5{
        font-size:18px;
    }
    .badge-soft{
        padding:7px 10px;
        font-size:12px;
    }
    .btn-premium{
        width:100%;
    }
}
</style>


<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">

    <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">
             <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 style="width:40px"><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 rowid, * FROM usuarios WHERE usuario_id = ? ORDER BY rowid DESC");
                    $st->execute([$revId]);
                    $res = $st;
                } else {
                    $res = $db->query("SELECT rowid, * FROM usuarios WHERE (usuario_id IS NULL OR usuario_id=0) ORDER BY 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"] ?? ""));
                    $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();
                    $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);
                    $statusLabelRow = $isPendingActivationRow ? 'Aguardando' : (string)($u['status'] ?? '');


                    $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-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-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-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) ?>"></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>
                            </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></td>
                        <td><?= htmlspecialchars((string)$u["nome_cliente"]) ?></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">
                                <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 if ($isPendingActivationRow) { ?>
                                <button type="button" class="action-btn btn-activate-user action-wide" title="Ativar" data-mac="<?= htmlspecialchars($macRow) ?>">
                                    <i class="bi bi-lightning-charge-fill text-success"></i><span>Ativar</span>
                                </button>
                                <?php } ?>
                                <button type="button" class="action-btn btn-delete" data-mac="<?= htmlspecialchars($macRow) ?>" title="Apagar">
                                    <i class="bi bi-trash text-danger"></i>
                                </button>
                            </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">
                <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">
                    <div class="row g-3">
                        <div class="col-md-6"><label class="modal-label">Usuário</label><input name="username" class="form-control" placeholder="Usuário"></div>
                        <div class="col-md-6"><label class="modal-label">Senha</label><input name="password" class="form-control" placeholder="Senha"></div>
                        <div class="col-md-6"><label class="modal-label">DNS</label><input name="dns" class="form-control" placeholder="DNS"></div>
                        <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>
                </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" style="border-bottom:1px solid rgba(148,163,184,.15);">
                    <div class="d-flex align-items-center gap-2">
                        <div style="width:38px;height:38px;border-radius:12px;background:rgba(37,99,235,.12);display:flex;align-items:center;justify-content:center;">
                            <i class="bi bi-shield-lock" style="font-size:18px;color:#2563eb;"></i>
                        </div>
                        <div>
                            <h5 class="modal-title mb-0" style="font-weight:900;">Controle de Acesso</h5>
                            <p class="mb-0" style="font-size:11px;color:#64748b;" id="accessMacLabel">—</p>
                        </div>
                    </div>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>

                <div class="modal-body" style="padding:20px 22px;">
                    <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="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" style="font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.08em;color:#64748b;margin-bottom:12px;">Abas do App</div>
                        <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
                            <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>
                    </div>

                    <!-- Nota informativa -->
                    <div class="card-soft mt-3" style="background:rgba(37,99,235,.04);border-color:rgba(37,99,235,.12);">
                        <div class="d-flex align-items-start gap-2">
                            <i class="bi bi-lightning-charge-fill" style="color:#2563eb;margin-top:2px;"></i>
                            <div>
                                <div style="font-weight:700;font-size:12px;color:#1e40af;">Bloqueio em tempo real</div>
                                <div class="text-muted" style="font-size:11px;">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" style="border-top:1px solid rgba(148,163,184,.15);">
                    <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>

<style>
.access-switch-card {
    background: #f8fafc;
    border: 1.5px solid rgba(148,163,184,.18);
    border-radius: 14px;
    padding: 14px 16px;
    transition: border-color .2s, background .2s;
}
.access-switch-card:has(.form-check-input:not(:checked)) {
    background: rgba(239,68,68,.04);
    border-color: rgba(239,68,68,.25);
}
.access-switch-card .form-check-input:checked { background-color: #22c55e; border-color: #22c55e; }
.access-switch-card .form-check-input:not(:checked) { background-color: #ef4444; border-color: #ef4444; }
</style>


<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" style="padding:26px 22px;">
                <div style="width:72px;height:72px;border-radius:22px;background:rgba(239,68,68,.10);display:flex;align-items:center;justify-content:center;margin:0 auto 14px;">
                    <i class="bi bi-trash3-fill" style="font-size:34px;color:#dc2626;"></i>
                </div>
                <h5 class="text-danger" style="font-weight:1000;">Confirmar exclusão</h5>
                <p style="opacity:.85;color:#64748b;">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" style="padding:26px 22px;">
                <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>

<script>
(function(window, document, $) {
    'use strict';

    if (typeof $ === 'undefined') {
        console.error('users.php: jQuery não encontrado.');
        return;
    }

    var macsDelete = [];

    function getModalEl(id) {
        return document.getElementById(id);
    }

    function getModalInstance(el) {
        if (!el || !window.bootstrap || !window.bootstrap.Modal) return null;
        return window.bootstrap.Modal.getOrCreateInstance(el);
    }

    function openModal(id) {
        var el = getModalEl(id);
        if (!el) {
            console.error('Modal não encontrado:', id);
            return;
        }

        var instance = getModalInstance(el);
        if (instance) {
            instance.show();
            return;
        }

        el.classList.add('show');
        el.style.display = 'block';
        el.removeAttribute('aria-hidden');
        el.setAttribute('aria-modal', 'true');
        document.body.classList.add('modal-open');

        if (!document.querySelector('.modal-backdrop.js-fallback-backdrop')) {
            var backdrop = document.createElement('div');
            backdrop.className = 'modal-backdrop fade show js-fallback-backdrop';
            document.body.appendChild(backdrop);
        }
    }

    function closeModal(id) {
        var el = getModalEl(id);
        if (!el) return;

        var instance = getModalInstance(el);
        if (instance) {
            instance.hide();
            return;
        }

        el.classList.remove('show');
        el.style.display = 'none';
        el.setAttribute('aria-hidden', 'true');
        el.removeAttribute('aria-modal');
        document.body.classList.remove('modal-open');

        document.querySelectorAll('.modal-backdrop.js-fallback-backdrop').forEach(function(node) {
            node.remove();
        });
    }

    function showSuccessAndReload() {
        openModal('successModal');
        setTimeout(function() {
            window.location.reload();
        }, 900);
    }

    function showRequestError(xhr, fallbackMessage) {
        var msg = fallbackMessage || 'Ocorreu um erro ao processar a ação.';
        try {
            var resp = xhr && xhr.responseJSON ? xhr.responseJSON : JSON.parse((xhr && xhr.responseText) || '{}');
            if (resp && resp.message) msg = resp.message;
        } catch (e) {}
        alert(msg);
    }

    function rowData($tr, key) {
        var attrName = 'data-' + key;
        var attrValue = $tr.attr(attrName);
        return typeof attrValue !== 'undefined' ? attrValue : '';
    }

    function safeVal(selector, value) {
        var $el = $(selector);
        if ($el.length) $el.val(value == null ? '' : value);
    }

    function safeText(selector, value) {
        var $el = $(selector);
        if ($el.length) $el.text(value == null ? '' : value);
    }

    function safeFormValue(form, name, value) {
        if (form && form.elements && form.elements[name]) {
            form.elements[name].value = value == null ? '' : value;
        }
    }

    function setSwitch(selector, checked) {
        var $el = $(selector);
        if ($el.length) $el.prop('checked', !!checked);
    }

    function syncAccessHidden() {
        var movies = $('#allowTabMoviesSwitch').is(':checked');
        var series = $('#allowTabSeriesSwitch').is(':checked');
        var kids = $('#allowTabKidsSwitch').is(':checked');
        var anime = $('#allowTabAnimeSwitch').is(':checked');

        safeVal('#allowTabMovies', movies ? '1' : '0');
        safeVal('#allowTabSeries', series ? '1' : '0');
        safeVal('#allowTabKids', kids ? '1' : '0');
        safeVal('#allowTabAnime', anime ? '1' : '0');

        safeVal('#allowContentMovies', movies ? '1' : '0');
        safeVal('#allowContentSeries', series ? '1' : '0');
        safeVal('#allowContentLive', '1');

        safeVal('#allowAllMovies', movies ? '1' : '0');
        safeVal('#allowAllSeries', series ? '1' : '0');
        safeVal('#allowAllLive', '1');

        safeVal('#allowTv', '1');
        safeVal('#allowMobile', '1');

        safeText('#moviesStatusText', movies ? 'Liberado' : '🔒 Bloqueado');
        safeText('#seriesStatusText', series ? 'Liberado' : '🔒 Bloqueado');
        safeText('#kidsStatusText', kids ? 'Liberado' : '🔒 Bloqueado');
        safeText('#animeStatusText', anime ? 'Liberado' : '🔒 Bloqueado');

        $('#moviesStatusText').css('color', movies ? '#22c55e' : '#ef4444');
        $('#seriesStatusText').css('color', series ? '#22c55e' : '#ef4444');
        $('#kidsStatusText').css('color', kids ? '#22c55e' : '#ef4444');
        $('#animeStatusText').css('color', anime ? '#22c55e' : '#ef4444');
    }

    function filterRows() {
        var tipo = String($('#filterTipo').val() || '').toLowerCase().trim();
        var search = String($('#searchTable').val() || '').toLowerCase().trim();

        $('#usersTable tbody tr').each(function() {
            var $tr = $(this);
            if ($tr.find('td').length === 1) return;

            var rowTipo = String($tr.data('tipo') || '').toLowerCase();
            var rowSearch = String($tr.data('search') || '').toLowerCase();
            var show = true;

            if (tipo && rowTipo !== tipo) show = false;
            if (search && rowSearch.indexOf(search) === -1) show = false;

            $tr.toggle(show);
        });
    }

    $(document).on('click', '[data-bs-dismiss="modal"], .btn-close', function(e) {
        var modal = this.closest('.modal');
        if (modal && modal.id) closeModal(modal.id);
    });

    $(document).on('click', '.modal', function(e) {
        if (e.target === this && this.id) closeModal(this.id);
    });

    $(document).on('change', '#checkAll', function() {
        $('.chk').prop('checked', $(this).prop('checked'));
    });

    $(document).on('click', '.btn-delete', function(e) {
        e.preventDefault();
        var mac = $(this).data('mac') || $(this).closest('tr').attr('data-mac') || '';
        macsDelete = mac ? [String(mac)] : [];
        if (!macsDelete.length) {
            alert('MAC não encontrado para apagar.');
            return;
        }
        openModal('confirmDeleteModal');
    });

    $(document).on('click', '#btnDeleteSelected', function(e) {
        e.preventDefault();
        macsDelete = $('.chk:checked').map(function() {
            return this.value;
        }).get();

        if (!macsDelete.length) {
            alert('Selecione pelo menos um usuário para apagar.');
            return;
        }

        openModal('confirmDeleteModal');
    });

    $(document).on('click', '#confirmDelete', function(e) {
        e.preventDefault();

        if (!macsDelete.length) {
            alert('Nenhum usuário selecionado.');
            return;
        }

        $.ajax({
            url: 'users.php',
            type: 'POST',
            dataType: 'json',
            data: { delete: macsDelete },
            success: function(resp) {
                if (resp && resp.status === 'success') {
                    closeModal('confirmDeleteModal');
                    showSuccessAndReload();
                    return;
                }
                alert(resp && resp.message ? resp.message : 'Não foi possível apagar o usuário.');
            },
            error: function(xhr) {
                showRequestError(xhr, 'Falha ao apagar o usuário.');
            }
        });
    });

    $(document).on('click', '.btn-edit', function(e) {
        e.preventDefault();

        var $tr = $(this).closest('tr');
        var form = document.getElementById('editForm');

        if (!form) {
            alert('Formulário de edição não encontrado.');
            return;
        }

        safeFormValue(form, 'mac', rowData($tr, 'mac'));
        safeFormValue(form, 'username', rowData($tr, 'username'));
        safeFormValue(form, 'password', rowData($tr, 'password'));
        safeFormValue(form, 'dns', rowData($tr, 'dns'));
        safeFormValue(form, 'status', rowData($tr, 'status') || 'ativo');
        safeFormValue(form, 'vencimento', rowData($tr, 'venc'));
        safeFormValue(form, 'nome_cliente', rowData($tr, 'nome'));
        safeFormValue(form, 'whatsapp', rowData($tr, 'whatsapp'));
        safeFormValue(form, 'tipo_registro', rowData($tr, 'tipo-registro') || 'cliente');
        safeFormValue(form, 'login_mode', rowData($tr, 'login-mode') || 'normal');
        safeFormValue(form, 'activation_code', rowData($tr, 'activation-code') || '');

        safeVal('#macReadonly', rowData($tr, 'mac'));

        openModal('editModal');
    });

    $(document).on('submit', '#editForm', function(e) {
        e.preventDefault();

        $.ajax({
            url: 'users.php',
            type: 'POST',
            dataType: 'json',
            data: $(this).serialize(),
            success: function(resp) {
                if (resp && resp.status === 'success') {
                    closeModal('editModal');
                    showSuccessAndReload();
                    return;
                }
                alert(resp && resp.message ? resp.message : 'Não foi possível salvar as alterações.');
            },
            error: function(xhr) {
                showRequestError(xhr, 'Falha ao salvar as alterações.');
            }
        });
    });

    $(document).on('click', '.btn-access', function(e) {
        e.preventDefault();

        var $tr = $(this).closest('tr');
        var mac = String(rowData($tr, 'mac') || '');

        safeVal('#accessMac', mac);
        safeText('#accessMacLabel', 'MAC: ' + mac);

        var allowMovies = String(rowData($tr, 'allow-tab-movies') || rowData($tr, 'allow-movies') || '1') === '1';
        var allowSeries = String(rowData($tr, 'allow-tab-series') || rowData($tr, 'allow-series') || '1') === '1';
        var allowKids = String(rowData($tr, 'allow-tab-kids') || '1') === '1';
        var allowAnime = String(rowData($tr, 'allow-tab-anime') || '1') === '1';

        setSwitch('#allowTabMoviesSwitch', allowMovies);
        setSwitch('#allowTabSeriesSwitch', allowSeries);
        setSwitch('#allowTabKidsSwitch', allowKids);
        setSwitch('#allowTabAnimeSwitch', allowAnime);

        safeVal('#blockedLiveIds', String(rowData($tr, 'blocked-live-ids') || ''));
        safeVal('#blockedMovieIds', String(rowData($tr, 'blocked-movie-ids') || ''));
        safeVal('#blockedSeriesIds', String(rowData($tr, 'blocked-series-ids') || ''));
        safeVal('#blockedLiveCategories', String(rowData($tr, 'blocked-live-categories') || ''));
        safeVal('#blockedMovieCategories', String(rowData($tr, 'blocked-movie-categories') || ''));
        safeVal('#blockedSeriesCategories', String(rowData($tr, 'blocked-series-categories') || ''));

        syncAccessHidden();
        openModal('accessModal');
    });

    $(document).on('change', '#allowTabMoviesSwitch, #allowTabSeriesSwitch, #allowTabKidsSwitch, #allowTabAnimeSwitch', function() {
        syncAccessHidden();
    });

    $(document).on('submit', '#accessForm', function(e) {
        e.preventDefault();
        syncAccessHidden();

        $.ajax({
            url: 'users.php',
            type: 'POST',
            dataType: 'json',
            data: $(this).serialize(),
            success: function(resp) {
                if (resp && resp.status === 'success') {
                    closeModal('accessModal');
                    showSuccessAndReload();
                    return;
                }
                alert(resp && resp.message ? resp.message : 'Não foi possível salvar o controle de acesso.');
            },
            error: function(xhr) {
                showRequestError(xhr, 'Falha ao salvar o controle de acesso.');
            }
        });
    });


    $(document).on('click', '.btn-activate-user', function(e) {
        e.preventDefault();
        var $tr = $(this).closest('tr');
        var mac = $(this).data('mac') || rowData($tr, 'mac') || '';
        safeVal('#activateUserMac', mac);
        var now = new Date();
        now.setDate(now.getDate() + 30);
        var pad = function(n){ return String(n).padStart(2, '0'); };
        var dt = now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate()) + 'T' + pad(now.getHours()) + ':' + pad(now.getMinutes());
        safeVal('#activateUserVencimento', dt);
        safeVal('#activateUserM3u', '');
        openModal('activateUserModal');
    });

    $(document).on('submit', '#activateUserForm', function(e) {
        e.preventDefault();
        $.ajax({
            url: 'users.php',
            type: 'POST',
            dataType: 'json',
            data: $(this).serialize(),
            success: function(resp) {
                if (resp && resp.status === 'success') {
                    closeModal('activateUserModal');
                    showSuccessAndReload();
                    return;
                }
                alert(resp && resp.message ? resp.message : 'Não foi possível ativar o cliente.');
            },
            error: function(xhr) {
                showRequestError(xhr, 'Falha ao ativar o cliente.');
            }
        });
    });

    $(document).on('click', '#btnImportBackupJson', function(e) {
        e.preventDefault();
        safeVal('#backupJsonInput', '');
        $('#backupJsonInput').trigger('click');
    });

    $(document).on('change', '#backupJsonInput', function() {
        var file = this.files && this.files[0] ? this.files[0] : null;
        if (!file) return;

        var formData = new FormData();
        formData.append('import_backup_json', '1');
        formData.append('backup_file', file);

        $.ajax({
            url: 'users.php',
            type: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            dataType: 'json',
            success: function(resp) {
                if (resp && resp.status === 'success') {
                    alert(
                        'Backup importado com sucesso!\n\n' +
                        'Novos: ' + (resp.importados || 0) + '\n' +
                        'Atualizados: ' + (resp.atualizados || 0) + '\n' +
                        'Ignorados: ' + (resp.ignorados || 0)
                    );
                    window.location.reload();
                    return;
                }
                alert(resp && resp.message ? resp.message : 'Falha ao importar backup.');
            },
            error: function(xhr) {
                showRequestError(xhr, 'Falha ao enviar o arquivo JSON.');
            }
        });
    });

    $(document).on('change', '#filterTipo', filterRows);
    $(document).on('input', '#searchTable', filterRows);

    $(function() {
        syncAccessHidden();
        filterRows();
    });

})(window, document, window.jQuery);
</script>

<?php include "./includes/footer.php"; ?>
