<?php
/** /api/admin/* (admin-only) */

const SETTINGS_ID = 'global';
const DEFAULT_SETTINGS = [
    'whatsapp_number'         => '+1234567890',
    'shared_xtream_server'    => '',
    'shared_xtream_username'  => '',
    'shared_xtream_password'  => '',
    'shared_profiles'         => [],
    // Master Stream Proxy toggle — when OFF the app connects directly to
    // the stream source (no server-side relay). Default ON.
    'proxy_enabled'           => true,
    'external_player_enabled' => false,
    // Whether to show the "Install a video player" popup on first launch.
    // When false, the popup never appears (per-device dismiss key still respected
    // for users who already saw it).
    'player_notice_enabled'   => true,
    // Inline Preview Player on Live TV (TV / browser only). When true, tapping
    // a channel renders the small in-app preview player with gold controls
    // instead of launching fullscreen / external. Default true.
    'inline_preview_enabled'  => true,
    // ── In-app update / about / support fields (publicly readable via /api/public/app-info) ──
    'update_enabled'          => false,
    'app_version'             => '1.0.0',
    'min_supported_version'   => '1.0.0',
    'update_apk_url'          => '',
    'update_play_url'         => '',
    'update_changelog'        => '',
    // Delay (in seconds) BEFORE the update popup is shown to the user. This
    // gives admins time to log in if a forced update would otherwise lock
    // them out. Range: 10..14 seconds (default 10). Stored as int.
    'popup_delay_seconds'     => 10,
    'whatsapp_message'        => 'Hi, I need help with Nadiplayer.',
    'privacy_policy_text'     => 'Nadiplayer respects your privacy. This app is a media player that plays streams from playlists you provide. We do not collect, store, or share any personal data. Playlists are stored locally on your device only.',
    // FEATURE (2026-01) — Terms & Conditions, admin-editable, surfaced by
    // the About menu modal on the Account page.
    'terms_policy_text'       => 'Nadiplayer Terms & Conditions:

1. NadiPlayer is an IPTV media player only — no streams, channels, or playlists are provided with the app.
2. You are responsible for ensuring that any playlist or stream you load complies with the laws of your country and the rights of the content owner.
3. The developer accepts no liability for content delivered through user-provided playlists.
4. Continued use of the app indicates acceptance of these terms.

For full terms, contact support.',
    'support_email'           => '',
    'creator_name'            => '',
    'creator_role'            => '',
    'creator_avatar_url'      => '',
    'creator_bio'             => '',
    // ── AdMob / Ads configuration (publicly readable via /api/public/ad-config) ──
    // When ads_enabled is false, the app shows no banner/interstitial even if
    // IDs are present. When testing_mode is true, the Capacitor plugin uses
    // Google's universal TEST ad units and won't generate revenue (or get the
    // account flagged). Default = testing_mode ON until admin flips it off.
    //
    // Pre-populated with the default NADIPLAY AdMob account. To use your own,
    // replace in admin_panel → Ads — changes take effect on next app launch.
    'ads_enabled'             => false,
    // BUG-2 FIX (2026-05): Users-vs-Guests ads toggles. The master
    // `ads_enabled` flag still acts as a kill-switch — when false, no
    // ads anywhere. These two sub-toggles let the admin further
    // restrict ads to one audience without affecting the other.
    // Defaults preserve the pre-fix behaviour (guests on, users off).
    'ads_enabled_for_users'   => false,
    'ads_enabled_for_guests'  => true,
    'ads_testing_mode'        => true,
    'ads_app_id'              => 'ca-app-pub-6960839907465160~7767874649',
    'ads_banner_id'           => 'ca-app-pub-6960839907465160/6715858872',
    'ads_interstitial_id'     => 'ca-app-pub-6960839907465160/2856504914',
    // Native Advanced Ad Unit ID — used by the in-grid "Sponsored" cards
    // that mirror the movie/series poster aspect ratio (2:3 portrait).
    // Live-swappable from admin panel; APK does NOT need to be rebuilt.
    'ads_native_id'           => 'ca-app-pub-6960839907465160/3222873405',
    // Rewarded ad unit — used either as a dedicated rewarded flow
    // ("watch ad to unlock 1 hour of premium") OR as an interstitial
    // fallback when the regular slot is empty. Live-editable.
    'ads_rewarded_id'         => 'ca-app-pub-6960839907465160/7452490933',
    // Show interstitial every N channel switches (3, 5, 7, 10, …). 0 disables.
    'ads_interstitial_every'  => 5,
    // ── Push notifications kill-switch (v2.1) ──
    // Defaults to FALSE so APKs built BEFORE google-services.json was
    // bundled will NOT attempt to call PushNotifications.register()
    // (which crashes natively when Firebase isn't initialised). Admin
    // flips this ON only after distributing a Firebase-aware APK.
    'push_enabled'            => false,
    // ── Smart-TV custom "House Ad" banner (FEATURE 2026-01) ──
    // AdMob is not supported on Samsung Tizen / LG webOS, so the admin
    // can paste either a free-form HTML snippet OR an image URL + click
    // URL pair. Rendered fixed at the bottom of the TV screen only when
    // `tv_banner_enabled` is true AND the device is detected as Tizen /
    // webOS. `tv_banner_type` chooses between the two payload variants:
    //     "image" → render <img src=image_url> wrapped in <a href=click_url>
    //     "html"  → render the raw HTML snippet (admin's responsibility
    //               to sanitise — same trust model as AdMob native ads).
    'tv_banner_enabled'       => false,
    'tv_banner_type'          => 'image',
    'tv_banner_html'          => '',
    'tv_banner_image_url'     => '',
    'tv_banner_click_url'     => '',
    // ── World Cup Hub (Sports API) ──
    // Live-fixture proxy for the in-app "World Cup" tab. The mobile
    // client never sees the API key — it hits /api/sports/fixtures on
    // our backend, which forwards to api-football and caches the
    // response for `sports_cache_ttl` seconds. Tournament defaults to
    // the FIFA World Cup (league id 1).
    //   - sports_provider      : "api-football" (only one supported)
    //   - sports_api_key       : RapidAPI / api-football key (SECRET)
    //   - sports_league_id     : numeric league id (1 = World Cup,
    //                            34 = WC qualifiers, etc.)
    //   - sports_season        : year (e.g. 2026)
    //   - sports_cache_ttl     : seconds to keep the cached payload
    //                            before re-querying the upstream API
    //   - world_cup_enabled    : master kill-switch; when false the
    //                            client-side tab is hidden (see
    //                            /public/app-info)
    'sports_provider'         => 'api-football',
    'sports_api_key'          => '',
    'sports_league_id'        => 1,
    'sports_season'           => 2026,
    'sports_cache_ttl'        => 300,
    'world_cup_enabled'       => false,
];

function get_settings_doc(): array {
    ensure_schema();
    $stmt = db()->prepare('SELECT data FROM settings WHERE id = ?');
    $stmt->execute([SETTINGS_ID]);
    $row = $stmt->fetch();
    if (!$row) {
        $data = DEFAULT_SETTINGS;
        $ins = db()->prepare('INSERT INTO settings (id, data) VALUES (?, ?)');
        $ins->execute([SETTINGS_ID, json_encode($data)]);
    } else {
        $data = json_decode($row['data'], true) ?: [];
        $changed = false;
        foreach (DEFAULT_SETTINGS as $k => $v) {
            if (!array_key_exists($k, $data)) { $data[$k] = $v; $changed = true; }
        }
        if ($changed) {
            $up = db()->prepare('UPDATE settings SET data = ? WHERE id = ?');
            $up->execute([json_encode($data), SETTINGS_ID]);
        }
    }
    $data['id'] = SETTINGS_ID;
    return $data;
}

function set_settings_data(array $data): void {
    unset($data['id']);
    $up = db()->prepare(
        'INSERT INTO settings (id, data) VALUES (?, ?)
         ON DUPLICATE KEY UPDATE data = VALUES(data)'
    );
    $up->execute([SETTINGS_ID, json_encode($data)]);
}

// ── Users ──────────────────────────────────────────────────────────

function route_admin_list_users(): void {
    require_admin();
    $rows = db()->query("SELECT * FROM users WHERE role = 'user' ORDER BY created_at DESC")->fetchAll();
    send_json(array_map('shape_user', $rows));
}

function route_admin_create_user(): void {
    require_admin();
    $b = read_json_body();
    $username = strtolower(trim(require_field($b, 'username')));
    $password = require_field($b, 'password');
    $days     = isset($b['days']) ? max(1, min(3650, (int)$b['days'])) : 30;
    $mode     = $b['xtream_mode']     ?? 'shared';
    if (!in_array($mode, ['shared', 'own'], true)) json_error(400, 'Invalid xtream_mode');

    if (strlen($username) < 2 || strlen($username) > 40) json_error(400, 'Bad username length');
    if (strlen($password) < 3) json_error(400, 'Password too short');

    $exists = db()->prepare('SELECT 1 FROM users WHERE username = ?');
    $exists->execute([$username]);
    if ($exists->fetch()) json_error(409, 'Username already exists');

    $now    = new DateTime('now', new DateTimeZone('UTC'));
    $expiry = (clone $now)->modify("+{$days} days");

    $stmt = db()->prepare(
        'INSERT INTO users (id, username, password_hash, role, created_at, expiry_at,
                            xtream_mode, xtream_server, xtream_username, xtream_password,
                            live_server, live_username, live_password,
                            vod_server, vod_username, vod_password, note)
         VALUES (?, ?, ?, "user", ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
    );
    $id = uuid_v4();
    $stmt->execute([
        $id, $username, password_hash($password, PASSWORD_BCRYPT),
        $now->format('Y-m-d\TH:i:s.uP'),
        $expiry->format('Y-m-d\TH:i:s.uP'),
        $mode,
        $b['xtream_server']   ?? '',
        $b['xtream_username'] ?? '',
        $b['xtream_password'] ?? '',
        $b['live_server']     ?? '',
        $b['live_username']   ?? '',
        $b['live_password']   ?? '',
        $b['vod_server']      ?? '',
        $b['vod_username']    ?? '',
        $b['vod_password']    ?? '',
        $b['note'] ?? '',
    ]);

    $sel = db()->prepare('SELECT * FROM users WHERE id = ?');
    $sel->execute([$id]);
    send_json(shape_user($sel->fetch()));
}

function route_admin_update_user(string $user_id): void {
    require_admin();
    $b = read_json_body();

    $sel = db()->prepare("SELECT * FROM users WHERE id = ? AND role = 'user'");
    $sel->execute([$user_id]);
    $u = $sel->fetch();
    if (!$u) json_error(404, 'User not found');

    $patch = [];
    if (isset($b['password']) && $b['password'] !== '') {
        if (strlen($b['password']) < 3) json_error(400, 'Password too short');
        $patch['password_hash'] = password_hash($b['password'], PASSWORD_BCRYPT);
    }
    if (isset($b['set_expiry_at']) && $b['set_expiry_at'] !== null) {
        try { new DateTime($b['set_expiry_at']); } catch (Throwable $e) { json_error(400, 'Invalid set_expiry_at'); }
        $patch['expiry_at'] = $b['set_expiry_at'];
    }
    if (isset($b['extend_days']) && $b['extend_days'] !== null) {
        $base_iso = $patch['expiry_at'] ?? $u['expiry_at'];
        try {
            $base = $base_iso ? new DateTime($base_iso) : new DateTime('now', new DateTimeZone('UTC'));
        } catch (Throwable $e) { $base = new DateTime('now', new DateTimeZone('UTC')); }
        $now = new DateTime('now', new DateTimeZone('UTC'));
        if ($base < $now) $base = $now;
        $patch['expiry_at'] = $base->modify(((int)$b['extend_days']) . ' days')->format('Y-m-d\TH:i:s.uP');
    }
    foreach (['xtream_mode', 'xtream_server', 'xtream_username', 'xtream_password',
              'live_server', 'live_username', 'live_password',
              'vod_server', 'vod_username', 'vod_password', 'note'] as $f) {
        if (isset($b[$f])) $patch[$f] = $b[$f];
    }

    if ($patch) {
        $sets = [];
        $vals = [];
        foreach ($patch as $k => $v) { $sets[] = "$k = ?"; $vals[] = $v; }
        $vals[] = $user_id;
        $up = db()->prepare('UPDATE users SET ' . implode(', ', $sets) . ' WHERE id = ?');
        $up->execute($vals);
    }
    $sel->execute([$user_id]);
    send_json(shape_user($sel->fetch()));
}

function route_admin_delete_user(string $user_id): void {
    require_admin();
    $del = db()->prepare("DELETE FROM users WHERE id = ? AND role = 'user'");
    $del->execute([$user_id]);
    if ($del->rowCount() === 0) json_error(404, 'User not found');
    send_json(['ok' => true]);
}

// ── Settings ───────────────────────────────────────────────────────

function route_admin_get_settings(): void {
    require_admin();
    send_json(get_settings_doc());
}

function route_admin_put_settings(): void {
    require_admin();
    $b = read_json_body();
    $data = get_settings_doc();
    unset($data['id']);

    foreach (['whatsapp_number', 'shared_xtream_server', 'shared_xtream_username',
              'shared_xtream_password'] as $f) {
        if (isset($b[$f])) $data[$f] = (string)$b[$f];
    }
    if (isset($b['proxy_enabled'])) {
        $data['proxy_enabled'] = (bool)$b['proxy_enabled'];
    }
    if (isset($b['external_player_enabled'])) {
        $data['external_player_enabled'] = (bool)$b['external_player_enabled'];
    }
    if (isset($b['player_notice_enabled'])) {
        $data['player_notice_enabled'] = (bool)$b['player_notice_enabled'];
    }
    if (isset($b['inline_preview_enabled'])) {
        $data['inline_preview_enabled'] = (bool)$b['inline_preview_enabled'];
    }
    if (isset($b['push_enabled'])) {
        // v2.1: server-side kill-switch for FCM registration. See
        // usePushNotifications.ts for the rationale (ROG 8 Pro / A14-15
        // silent post-login crash).
        $data['push_enabled'] = (bool)$b['push_enabled'];
    }
    // ── In-app update / about / support — accept all string + bool fields ──
    if (isset($b['update_enabled'])) {
        $data['update_enabled'] = (bool)$b['update_enabled'];
    }
    if (isset($b['popup_delay_seconds'])) {
        $delay = (int)$b['popup_delay_seconds'];
        if ($delay < 0)   { $delay = 0; }
        if ($delay > 600) { $delay = 600; }
        $data['popup_delay_seconds'] = $delay;
    }
    foreach ([
        'app_version', 'min_supported_version', 'update_apk_url',
        'update_play_url', 'update_changelog', 'whatsapp_message',
        'privacy_policy_text', 'terms_policy_text', 'support_email', 'creator_name',
        'creator_role', 'creator_avatar_url', 'creator_bio',
        'ads_app_id', 'ads_banner_id', 'ads_interstitial_id', 'ads_native_id',
        'ads_rewarded_id',
    ] as $f) {
        if (isset($b[$f])) $data[$f] = (string)$b[$f];
    }
    // Ads bool toggles + numeric frequency
    if (isset($b['ads_enabled']))      $data['ads_enabled']      = (bool)$b['ads_enabled'];
    // BUG-2 FIX (2026-05) — Users-vs-Guests sub-toggles
    if (isset($b['ads_enabled_for_users']))  $data['ads_enabled_for_users']  = (bool)$b['ads_enabled_for_users'];
    if (isset($b['ads_enabled_for_guests'])) $data['ads_enabled_for_guests'] = (bool)$b['ads_enabled_for_guests'];
    if (isset($b['ads_testing_mode'])) $data['ads_testing_mode'] = (bool)$b['ads_testing_mode'];
    if (isset($b['ads_interstitial_every'])) {
        $n = (int)$b['ads_interstitial_every'];
        if ($n < 0)   $n = 0;
        if ($n > 100) $n = 100;
        $data['ads_interstitial_every'] = $n;
    }
    // ── Smart-TV custom banner (FEATURE 2026-01) ──
    if (isset($b['tv_banner_enabled'])) {
        $data['tv_banner_enabled'] = (bool)$b['tv_banner_enabled'];
    }
    if (isset($b['tv_banner_type'])) {
        $t = (string)$b['tv_banner_type'];
        $data['tv_banner_type'] = in_array($t, ['image', 'html'], true) ? $t : 'image';
    }
    foreach (['tv_banner_html', 'tv_banner_image_url', 'tv_banner_click_url'] as $f) {
        if (isset($b[$f])) $data[$f] = (string)$b[$f];
    }
    if (isset($b['shared_profiles']) && is_array($b['shared_profiles'])) {
        $cleaned = [];
        foreach ($b['shared_profiles'] as $p) {
            $cleaned[] = [
                'id'          => $p['id']   ?? uuid_v4(),
                'name'        => trim((string)($p['name'] ?? '')) ?: 'Profile',
                'kind'        => in_array($p['kind'] ?? 'xtream', ['xtream', 'm3u', 'stalker'], true) ? $p['kind'] : 'xtream',
                'server'      => trim((string)($p['server']   ?? '')),
                'username'    => trim((string)($p['username'] ?? '')),
                'password'    =>      (string)($p['password'] ?? ''),
                'm3u_url'     => trim((string)($p['m3u_url']  ?? '')),
                'm3u_content' =>      (string)($p['m3u_content'] ?? ''),
                'portal_url'  => trim((string)($p['portal_url'] ?? '')),
                'mac_address' => strtoupper(trim((string)($p['mac_address'] ?? ''))),
                // Hybrid Source A (Live TV)
                'source_a_server'   => trim((string)($p['source_a_server']   ?? '')),
                'source_a_username' => trim((string)($p['source_a_username'] ?? '')),
                'source_a_password' =>      (string)($p['source_a_password'] ?? ''),
                'source_a_kind'     => in_array($p['source_a_kind'] ?? '', ['xtream', 'm3u', 'stalker', ''], true) ? ($p['source_a_kind'] ?? '') : '',
                // Hybrid Source B (Movies & Series)
                'source_b_server'   => trim((string)($p['source_b_server']   ?? '')),
                'source_b_username' => trim((string)($p['source_b_username'] ?? '')),
                'source_b_password' =>      (string)($p['source_b_password'] ?? ''),
                'source_b_kind'     => in_array($p['source_b_kind'] ?? '', ['xtream', 'm3u', 'stalker', ''], true) ? ($p['source_b_kind'] ?? '') : '',
            ];
        }
        $data['shared_profiles'] = $cleaned;
    }

    set_settings_data($data);
    $data['id'] = SETTINGS_ID;
    send_json($data);
}
