<?php
// admin/install_link_lib.php
// Main/child installation linking helpers.

require_once __DIR__ . '/app_runtime.php';
require_once __DIR__ . '/app_paths.php';

if (!function_exists('ua_link_normalize_url')) {
  function ua_link_normalize_url(string $url): string {
    $url = trim($url);
    if ($url === '') return '';
    if (!preg_match('~^https?://~i', $url)) $url = 'http://' . $url;
    $parts = @parse_url($url);
    if (!is_array($parts) || empty($parts['host'])) return '';
    $scheme = strtolower((string)($parts['scheme'] ?? 'http'));
    if (!in_array($scheme, ['http', 'https'], true)) return '';
    $host = strtolower((string)$parts['host']);
    $port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
    $path = trim((string)($parts['path'] ?? ''), '/');
    return rtrim($scheme . '://' . $host . $port . ($path !== '' ? '/' . $path : ''), '/');
  }
}

if (!function_exists('ua_link_random_token')) {
  function ua_link_random_token(int $bytes = 24): string {
    try {
      return bin2hex(random_bytes($bytes));
    } catch (Throwable $e) {
      return sha1(uniqid('ua-link-', true) . microtime(true));
    }
  }
}

if (!function_exists('ua_link_connection_mode')) {
  function ua_link_connection_mode(string $mode): string {
    return strtolower(trim($mode)) === 'internet' ? 'internet' : 'local';
  }
}

if (!function_exists('ua_link_canonical_endpoint_url')) {
  function ua_link_canonical_endpoint_url(string $url): string {
    $url = ua_link_normalize_url($url);
    if ($url === '') return '';
    return preg_replace('~/admin$~i', '', $url) ?: $url;
  }
}

if (!function_exists('ua_link_admin_url')) {
  function ua_link_admin_url(string $url): string {
    $url = ua_link_normalize_url($url);
    if ($url === '') return '';
    if (preg_match('~/admin$~i', $url)) return $url;
    if (preg_match('~/mikrotik$~i', $url)) return $url . '/admin';
    return $url . '/mikrotik/admin';
  }
}

if (!function_exists('ua_link_install_type_label')) {
  function ua_link_install_type_label(string $type): string {
    if ($type === 'branch') return 'نسخة فرعية';
    if ($type === 'agent') return 'نسخة معتمدين';
    return 'نسخة كاملة';
  }
}

if (!function_exists('ua_link_current_public_url')) {
  function ua_link_current_public_url(): string {
    $isHttps = (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off')
      || strtolower(trim((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''))) === 'https';
    $scheme = $isHttps ? 'https' : 'http';
    $host = trim((string)($_SERVER['HTTP_HOST'] ?? '127.0.0.1'));
    $script = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? '/admin/index.php'));
    $dir = rtrim(dirname($script), '/');
    return rtrim($scheme . '://' . $host . ($dir !== '' ? $dir : ''), '/');
  }
}

if (!function_exists('ua_link_decode_json_response')) {
  function ua_link_decode_json_response(string $raw): ?array {
    if (substr($raw, 0, 3) === "\xEF\xBB\xBF") {
      $raw = substr($raw, 3);
    }
    $data = json_decode($raw, true);
    return is_array($data) ? $data : null;
  }
}

if (!function_exists('ua_link_http_json')) {
  function ua_link_http_json(string $baseUrl, string $path, array $payload, string &$error = '', int $timeout = 10): ?array {
    $baseUrl = ua_link_admin_url($baseUrl);
    if ($baseUrl === '') {
      $error = 'رابط النسخة الرئيسية غير صحيح.';
      return null;
    }
    $url = $baseUrl . '/' . ltrim($path, '/');
    $body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if (!is_string($body)) $body = '{}';

    if (function_exists('curl_init')) {
      $ch = curl_init($url);
      curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
        CURLOPT_CONNECTTIMEOUT => $timeout,
        CURLOPT_TIMEOUT => $timeout,
      ]);
      $raw = curl_exec($ch);
      $code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
      $curlErr = curl_error($ch);
      curl_close($ch);
      if ($raw === false || $raw === '') {
        $error = $curlErr !== '' ? $curlErr : 'تعذر الاتصال بالنسخة الرئيسية.';
        return null;
      }
      $data = ua_link_decode_json_response((string)$raw);
      if (!is_array($data)) {
        $error = 'استجابة النسخة الرئيسية غير مفهومة.';
        return null;
      }
      if ($code >= 400 && empty($data['message'])) $data['message'] = 'رفضت النسخة الرئيسية الطلب.';
      return $data;
    }

    $ctx = stream_context_create([
      'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\nAccept: application/json\r\n",
        'content' => $body,
        'timeout' => $timeout,
        'ignore_errors' => true,
      ],
    ]);
    $raw = @file_get_contents($url, false, $ctx);
    if ($raw === false || $raw === '') {
      $error = 'تعذر الاتصال بالنسخة الرئيسية.';
      return null;
    }
    $data = ua_link_decode_json_response((string)$raw);
    if (!is_array($data)) {
      $error = 'استجابة النسخة الرئيسية غير مفهومة.';
      return null;
    }
    return $data;
  }
}

if (!function_exists('ua_link_ensure_schema')) {
  function ua_link_ensure_schema(mysqli $conn): void {
    @$conn->query("CREATE TABLE IF NOT EXISTS `ua_link_endpoints` (
      `id` INT AUTO_INCREMENT PRIMARY KEY,
      `label` VARCHAR(160) NOT NULL,
      `base_url` VARCHAR(255) NOT NULL,
      `address_type` VARCHAR(20) NOT NULL DEFAULT 'internal',
      `allow_branch` TINYINT(1) NOT NULL DEFAULT 1,
      `allow_agent` TINYINT(1) NOT NULL DEFAULT 1,
      `is_enabled` TINYINT(1) NOT NULL DEFAULT 1,
      `notes` TEXT NULL,
      `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `updated_at` DATETIME NULL,
      UNIQUE KEY `uniq_base_url` (`base_url`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

    @$conn->query("CREATE TABLE IF NOT EXISTS `ua_link_requests` (
      `id` INT AUTO_INCREMENT PRIMARY KEY,
      `instance_id` VARCHAR(80) NOT NULL,
      `instance_name` VARCHAR(180) NULL,
      `install_type` VARCHAR(20) NOT NULL,
      `connection_mode` VARCHAR(20) NOT NULL DEFAULT 'local',
      `server_url` VARCHAR(255) NULL,
      `reported_url` VARCHAR(255) NULL,
      `endpoint_id` INT NULL,
      `request_secret_hash` CHAR(64) NOT NULL,
      `device_id_hash` CHAR(64) NULL,
      `device_secret_hash` CHAR(64) NULL,
      `status` VARCHAR(20) NOT NULL DEFAULT 'pending',
      `remote_addr` VARCHAR(64) NULL,
      `metadata_json` LONGTEXT NULL,
      `admin_note` TEXT NULL,
      `approved_by` INT NULL,
      `approved_at` DATETIME NULL,
      `last_seen_at` DATETIME NULL,
      `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `updated_at` DATETIME NULL,
      UNIQUE KEY `uniq_instance_id` (`instance_id`),
      KEY `idx_status` (`status`),
      KEY `idx_install_type` (`install_type`),
      KEY `idx_device_identity` (`device_id_hash`, `install_type`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

    $modeColumn = @$conn->query("SHOW COLUMNS FROM `ua_link_requests` LIKE 'connection_mode'");
    if (!$modeColumn || !$modeColumn->num_rows) {
      @$conn->query("ALTER TABLE `ua_link_requests` ADD `connection_mode` VARCHAR(20) NOT NULL DEFAULT 'local' AFTER `install_type`");
    }

    $deviceIdColumn = @$conn->query("SHOW COLUMNS FROM `ua_link_requests` LIKE 'device_id_hash'");
    if (!$deviceIdColumn || !$deviceIdColumn->num_rows) {
      @$conn->query("ALTER TABLE `ua_link_requests` ADD `device_id_hash` CHAR(64) NULL AFTER `request_secret_hash`");
    }
    $deviceSecretColumn = @$conn->query("SHOW COLUMNS FROM `ua_link_requests` LIKE 'device_secret_hash'");
    if (!$deviceSecretColumn || !$deviceSecretColumn->num_rows) {
      @$conn->query("ALTER TABLE `ua_link_requests` ADD `device_secret_hash` CHAR(64) NULL AFTER `device_id_hash`");
    }
    $deviceIndex = @$conn->query("SHOW INDEX FROM `ua_link_requests` WHERE Key_name='idx_device_identity'");
    if (!$deviceIndex || !$deviceIndex->num_rows) {
      @$conn->query("ALTER TABLE `ua_link_requests` ADD KEY `idx_device_identity` (`device_id_hash`, `install_type`)");
    }

    @$conn->query("CREATE TABLE IF NOT EXISTS `ua_link_rate_limits` (
      `bucket_key` CHAR(64) PRIMARY KEY,
      `window_started_at` DATETIME NOT NULL,
      `attempt_count` INT NOT NULL DEFAULT 1,
      `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
  }
}

if (!function_exists('ua_link_match_endpoint')) {
  function ua_link_match_endpoint(mysqli $conn, string $serverUrl, string $installType, string $connectionMode = ''): ?array {
    ua_link_ensure_schema($conn);
    $normalized = ua_link_canonical_endpoint_url($serverUrl);
    $connectionMode = $connectionMode !== '' ? ua_link_connection_mode($connectionMode) : '';
    $roleCol = $installType === 'agent' ? 'allow_agent' : 'allow_branch';
    $rs = @$conn->query("SELECT * FROM ua_link_endpoints WHERE is_enabled=1 AND `$roleCol`=1 ORDER BY id ASC");
    if (!$rs) return null;
    while ($row = $rs->fetch_assoc()) {
      $base = ua_link_canonical_endpoint_url((string)($row['base_url'] ?? ''));
      $addressType = strtolower(trim((string)($row['address_type'] ?? 'internal')));
      if ($connectionMode === 'internet' && $addressType !== 'external') continue;
      if ($connectionMode === 'local' && $addressType !== 'internal') continue;
      if ($base !== '' && hash_equals($base, $normalized)) return $row;
    }
    return null;
  }
}

if (!function_exists('ua_link_rate_limit')) {
  function ua_link_rate_limit(mysqli $conn, string $scope, string $identity, int $limit, int $windowSeconds): bool {
    ua_link_ensure_schema($conn);
    $bucket = hash('sha256', $scope . '|' . trim($identity));
    $limit = max(1, $limit);
    $windowSeconds = max(10, $windowSeconds);
    $stmt = @$conn->prepare("INSERT INTO ua_link_rate_limits (bucket_key,window_started_at,attempt_count,updated_at)
      VALUES (?,NOW(),1,NOW())
      ON DUPLICATE KEY UPDATE
        attempt_count=IF(window_started_at < DATE_SUB(NOW(), INTERVAL ? SECOND),1,attempt_count+1),
        window_started_at=IF(window_started_at < DATE_SUB(NOW(), INTERVAL ? SECOND),NOW(),window_started_at),
        updated_at=NOW()");
    if (!$stmt) return true;
    $stmt->bind_param('sii', $bucket, $windowSeconds, $windowSeconds);
    $stmt->execute();
    $stmt->close();
    $bucketEsc = $conn->real_escape_string($bucket);
    $rs = @$conn->query("SELECT attempt_count FROM ua_link_rate_limits WHERE bucket_key='{$bucketEsc}' LIMIT 1");
    $count = ($rs && $rs->num_rows) ? (int)($rs->fetch_assoc()['attempt_count'] ?? 0) : 0;
    if (mt_rand(1, 100) === 1) {
      @$conn->query("DELETE FROM ua_link_rate_limits WHERE updated_at < DATE_SUB(NOW(), INTERVAL 2 DAY)");
    }
    return $count <= $limit;
  }
}

if (!function_exists('ua_link_parent_db_profile')) {
  function ua_link_parent_db_profile(mysqli $conn): array {
    if (!function_exists('ua_ensure_settings_table')) {
      $ui = __DIR__ . '/ui_settings.php';
      if (is_file($ui)) require_once $ui;
    }
    if (function_exists('ua_ensure_settings_table')) ua_ensure_settings_table($conn);
    $current = ua_install_database_config();
    $envPass = getenv('DB_PASS');
    $defaults = [
      'host' => getenv('DB_HOST') ?: (string)($current['host'] ?? '127.0.0.1'),
      'port' => getenv('DB_PORT') ?: (string)($current['port'] ?? 3306),
      'name' => getenv('DB_NAME') ?: (string)($current['name'] ?? 'isp_db'),
      'user' => getenv('DB_USER') ?: (string)($current['user'] ?? 'root'),
      'pass' => $envPass !== false ? (string)$envPass : (string)($current['pass'] ?? ''),
      'charset' => getenv('DB_CHARSET') ?: (string)($current['charset'] ?? 'utf8mb4'),
    ];
    $get = function(string $key, string $default = '') use ($conn): string {
      if (function_exists('ua_get_setting')) return trim((string)ua_get_setting($conn, $key, $default));
      return $default;
    };
    return [
      'host' => $get('link_db_host', $defaults['host']),
      'port' => (int)$get('link_db_port', $defaults['port']),
      'name' => $get('link_db_name', $defaults['name']),
      'user' => $get('link_db_user', $defaults['user']),
      'pass' => $get('link_db_pass', $defaults['pass']),
      'charset' => $get('link_db_charset', $defaults['charset']),
    ];
  }
}

if (!function_exists('ua_link_secret_hash')) {
  function ua_link_secret_hash(string $secret): string {
    return hash('sha256', $secret);
  }
}

if (!function_exists('ua_link_request_payload')) {
  function ua_link_request_payload(array $row, bool $includeDb, ?mysqli $conn = null): array {
    $status = (string)($row['status'] ?? 'pending');
    $payload = [
      'ok' => true,
      'status' => $status,
      'request_id' => (int)($row['id'] ?? 0),
      'connection_mode' => ua_link_connection_mode((string)($row['connection_mode'] ?? 'local')),
      'portal_url' => ua_link_admin_url((string)($row['server_url'] ?? '')),
      'message' => $status === 'approved'
        ? 'تمت الموافقة على الربط.'
        : ($status === 'pending' ? 'طلب الربط بانتظار موافقة المدير في النسخة الرئيسية.' : 'طلب الربط غير مفعل.'),
    ];
    if ($includeDb
        && $status === 'approved'
        && ua_link_connection_mode((string)($row['connection_mode'] ?? 'local')) === 'local'
        && $conn instanceof mysqli) {
      $payload['database'] = ua_link_parent_db_profile($conn);
    }
    return $payload;
  }
}

if (!function_exists('ua_link_mobile_request_payload')) {
  /**
   * Mobile clients must never receive database credentials. They communicate
   * exclusively through the HTTP API exposed by the main installation.
   */
  function ua_link_mobile_request_payload(array $row): array {
    $payload = ua_link_request_payload($row, false, null);
    $installType = strtolower(trim((string)($row['install_type'] ?? '')));
    $payload['api_version'] = 1;
    $payload['install_type'] = $installType;
    $payload['instance_id'] = (string)($row['instance_id'] ?? '');
    $payload['capabilities'] = $installType === 'agent'
      ? ['agent_profile', 'subscribers', 'payments', 'ledger', 'sync']
      : ['full_admin_web', 'main_database', 'all_main_features'];
    $payload['poll_after_seconds'] = 10;
    return $payload;
  }
}
