<?php
/**
 * Canonical read model for subscriber state.
 *
 * RouterOS observations decide online/disabled state. The database is only a
 * fallback before the first verified observation; losing the router transport
 * never turns every subscriber offline.
 */
require_once __DIR__ . '/ppp_active_cache_lib.php';
require_once __DIR__ . '/mikrotik_lib.php';

if (!function_exists('ua_subscriber_reality_user')) {
  function ua_subscriber_reality_user(array $row): string {
    $user = trim((string)($row['ppp_username'] ?? ''));
    if ($user === '') $user = trim((string)($row['username'] ?? ''));
    return ua_norm_ppp_user($user);
  }
}

if (!function_exists('ua_subscriber_reality_snapshot')) {
  function ua_subscriber_reality_snapshot(mysqli $conn, int $secretTtl = 3600): array {
    $ppp = function_exists('ua_ppp_cache_snapshot')
      ? ua_ppp_cache_snapshot(86400)
      : ['ok'=>false,'users'=>[],'rows'=>[],'source'=>'cache_unavailable'];
    $active = [];
    $activeRows = [];
    foreach ((array)($ppp['users'] ?? []) as $user) {
      $user = ua_norm_ppp_user((string)$user);
      if ($user !== '') $active[$user] = true;
    }
    foreach ((array)($ppp['rows'] ?? []) as $row) {
      if (!is_array($row)) continue;
      $user = ua_norm_ppp_user((string)($row['name'] ?? $row['user'] ?? ''));
      if ($user !== '') $activeRows[$user] = $row;
    }
    $secrets = function_exists('mt_ppp_secret_reality_map_cached')
      ? mt_ppp_secret_reality_map_cached($conn, max(30, $secretTtl), false)
      : [];
    return [
      'active'=>$active,
      'active_rows'=>$activeRows,
      'secrets'=>is_array($secrets) ? $secrets : [],
      'has_observation'=>!empty($ppp['data_available']) || (!empty($ppp['live_data_reliable']) && isset($ppp['users'])),
      'router_online'=>$ppp['router_online'] ?? null,
      'live_data_reliable'=>!empty($ppp['live_data_reliable']),
      'connection_state'=>(string)($ppp['connection_state'] ?? 'unknown'),
      'data_age'=>(int)($ppp['data_age'] ?? $ppp['age'] ?? 0),
      'source'=>(string)($ppp['source'] ?? 'ppp_active_cache'),
      'raw'=>$ppp,
    ];
  }
}

if (!function_exists('ua_subscriber_reality_state')) {
  function ua_subscriber_reality_state(array $row, array $reality): array {
    $user = ua_subscriber_reality_user($row);
    $online = !empty($reality['has_observation'])
      ? ($user !== '' && isset($reality['active'][$user]))
      : !empty($row['is_online']);
    $disabled = !empty($row['is_disabled'])
      || !empty($row['ppp_is_disabled'])
      || !empty($row['mt_disabled'])
      || !empty($row['is_cut'])
      || strtoupper(trim((string)($row['speed_state'] ?? ''))) === 'CUT'
      || in_array(strtoupper(trim((string)($row['status'] ?? ''))), ['CUT','DISABLED','STOPPED'], true);
    if ($user !== '' && array_key_exists($user, (array)($reality['secrets'] ?? []))) {
      $disabled = !empty($reality['secrets'][$user]['disabled']);
    }
    return [
      'username'=>$user,
      'is_online'=>$online ? 1 : 0,
      'is_disabled'=>$disabled ? 1 : 0,
      'active_row'=>$user !== '' ? ($reality['active_rows'][$user] ?? null) : null,
      'secret'=>$user !== '' ? ($reality['secrets'][$user] ?? null) : null,
      'source'=>(string)($reality['source'] ?? 'unknown'),
      'data_age'=>(int)($reality['data_age'] ?? 0),
      'live_data_reliable'=>!empty($reality['live_data_reliable']),
    ];
  }
}
