<?php
declare(strict_types=1);

function cors_headers(array $cfg): void {
  $origin = (string)($cfg['cors_allow_origin'] ?? '*');
  header('Access-Control-Allow-Origin: ' . ($origin === '' ? '*' : $origin));
  header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
  header('Access-Control-Allow-Headers: Content-Type');
  header('Access-Control-Max-Age: 86400');
}

function json_out(int $code, array $payload): void {
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($payload, JSON_UNESCAPED_SLASHES);
  exit;
}

function read_json_file(string $path): ?array {
  if (!is_file($path)) return null;
  $raw = file_get_contents($path);
  if ($raw === false) return null;
  $d = json_decode($raw, true);
  return is_array($d) ? $d : null;
}

function write_json_atomic(string $path, array $data, int $mode = 0640): bool {
  $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  if ($json === false) return false;
  $tmp = $path . '.tmp';
  if (file_put_contents($tmp, $json, LOCK_EX) === false) return false;
  @chmod($tmp, $mode);
  return @rename($tmp, $path);
}

function parse_env_file(string $envPath): array {
  if (!is_file($envPath)) return [];
  $raw = (string)(file_get_contents($envPath) ?: '');
  $lines = preg_split("/\r\n|\n|\r/", $raw) ?: [];
  $out = [];
  foreach ($lines as $line) {
    $line = trim($line);
    if ($line === '' || str_starts_with($line, '#')) continue;
    $pos = strpos($line, '=');
    if ($pos === false) continue;
    $k = trim(substr($line, 0, $pos));
    $v = trim(substr($line, $pos + 1));
    $out[$k] = $v;
  }
  return $out;
}

function load_wp_master_key(array $cfg): string {
  $value = trim((string)($cfg['wp_master_key_b64'] ?? ''));
  if ($value === '') {
    $value = trim((string)($_ENV['WP_MASTER_KEY_B64'] ?? getenv('WP_MASTER_KEY_B64') ?? ''));
  }
  if ($value === '') {
    return '';
  }

  if (is_file($value) && is_readable($value)) {
    $fileValue = file_get_contents($value);
    if ($fileValue !== false) {
      $value = trim($fileValue);
    }
  }

  $raw = base64_decode($value, true);
  if (!is_string($raw) || strlen($raw) !== 32) return '';
  return $raw;
}

function crypto_encrypt_aes256gcm(string $plaintext, string $key, string $aad = ''): array {
  $iv = random_bytes(12);
  $tag = '';
  $ct = openssl_encrypt($plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad, 16);
  if ($ct === false) throw new RuntimeException('encrypt_failed');
  return [
    'ct'  => base64_encode($ct),
    'iv'  => base64_encode($iv),
    'tag' => base64_encode($tag),
  ];
}

function crypto_decrypt_aes256gcm(array $blob, string $key, string $aad = ''): string {
  $ct  = base64_decode((string)($blob['ct'] ?? ''), true);
  $iv  = base64_decode((string)($blob['iv'] ?? ''), true);
  $tag = base64_decode((string)($blob['tag'] ?? ''), true);
  if (!is_string($ct) || !is_string($iv) || !is_string($tag)) throw new RuntimeException('decrypt_bad_blob');
  $pt = openssl_decrypt($ct, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad);
  if ($pt === false) throw new RuntimeException('decrypt_failed');
  return $pt;
}

function is_private_ip(string $ip): bool {
  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
    $long = ip2long($ip);
    if ($long === false) return true;
    // 10.0.0.0/8
    if (($long & 0xFF000000) === 0x0A000000) return true;
    // 172.16.0.0/12
    if (($long & 0xFFF00000) === 0xAC100000) return true;
    // 192.168.0.0/16
    if (($long & 0xFFFF0000) === 0xC0A80000) return true;
    // 127.0.0.0/8
    if (($long & 0xFF000000) === 0x7F000000) return true;
    return false;
  }
  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
    // block localhost + unique local
    if ($ip === '::1') return true;
    if (str_starts_with(strtolower($ip), 'fc') || str_starts_with(strtolower($ip), 'fd')) return true;
    return false;
  }
  return true;
}

function validate_wp_url(string $url): array {
  $url = trim($url);
  $p = parse_url($url);
  if (!is_array($p) || !isset($p['scheme'], $p['host'])) return [false, 'Invalid URL'];
  $scheme = strtolower((string)$p['scheme']);
  if (!in_array($scheme, ['http', 'https'], true)) return [false, 'URL must be http or https'];
  $host = (string)$p['host'];
  if ($host === 'localhost') return [false, 'localhost not allowed'];

  // If host is an IP, block private ranges
  if (filter_var($host, FILTER_VALIDATE_IP)) {
    if (is_private_ip($host)) return [false, 'Private IP not allowed'];
  } else {
    $resolved = gethostbyname($host);
    if ($resolved !== $host && is_private_ip($resolved)) return [false, 'Private host not allowed'];
  }

  // normalize to no trailing slash
  return [true, rtrim($url, '/')];
}

function tenant_paths(array $cfg, string $ref): array {
  $tenantsDir = rtrim((string)($cfg['tenants_dir'] ?? '/home/mytown/blog.mytown.ink/tenants'), '/');
  $tenantDir  = $tenantsDir . '/' . $ref;
  return [
    'tenantDir' => $tenantDir,
    'envPath'   => $tenantDir . '/.env',
    'active'    => $tenantDir . '/active.json',
    'wpJson'    => $tenantDir . '/data/wp.json',
  ];
}

function require_active_and_license(array $cfg, string $ref, string $license): array {
  if (!preg_match('/^t_[a-f0-9]{16}$/', $ref)) json_out(400, ['ok'=>false,'error'=>'Invalid ref']);
  if ($license === '' || strlen($license) < 8) json_out(400, ['ok'=>false,'error'=>'Invalid license']);

  $p = tenant_paths($cfg, $ref);
  if (!is_file($p['active']) || !is_file($p['envPath'])) json_out(404, ['ok'=>false,'error'=>'Tenant not found']);

  $active = read_json_file($p['active']);
  if (!is_array($active) || strtolower((string)($active['status'] ?? '')) !== 'active') {
    json_out(403, ['ok'=>false,'error'=>'Tenant not active']);
  }

  $env = parse_env_file($p['envPath']);
  $stored = (string)($env['LICENSE_KEY'] ?? '');
  if ($stored === '' || !hash_equals($stored, $license)) json_out(403, ['ok'=>false,'error'=>'License mismatch']);

  if (!is_dir(dirname($p['wpJson']))) @mkdir(dirname($p['wpJson']), 0750, true);

  return [$p, $active, $env];
}
