<?php
declare(strict_types=1);

const SAVE_KEEP_PER_GROUP = 12;
const BACKUP_KEEP_PER_GROUP = 30;
const GZIP_LEVEL = 5;

$root = dirname(__DIR__);
$saveDir = $root . '/.manager-saves';
$metaDir = $saveDir . '/.meta';
$backupDir = $root . '/.manager-save-backups';
$archiveDir = $backupDir . '/archive';
$liveDir = $backupDir . '/live';
$dryRun = in_array('--dry-run', $argv, true);

function clean_name_local(string $name): string {
  return strtolower(trim($name));
}

function division_level_local(array $meta): int {
  if (isset($meta['divisionLevel']) && is_numeric($meta['divisionLevel'])) return max(1, min(5, (int)$meta['divisionLevel']));
  $map = [
    'premier league' => 1,
    'championship' => 2,
    'league one' => 3,
    'league two' => 4,
    'national league' => 5,
  ];
  return $map[strtolower(trim((string)($meta['divisionName'] ?? '')))] ?? 5;
}

function save_meta_local(string $file): ?array {
  $save = json_decode((string)@file_get_contents($file), true);
  if (!is_array($save)) return null;
  $meta = is_array($save['meta'] ?? null) ? $save['meta'] : [];
  $users = [];
  foreach (($meta['users'] ?? []) as $name) {
    $name = clean_name_local((string)$name);
    if ($name !== '' && !in_array($name, $users, true)) $users[] = $name;
  }
  $mode = (string)($meta['mode'] ?? (count($users) > 1 ? 'online' : 'solo'));
  $gameId = (string)($meta['managerGameId'] ?? '');
  if ($gameId === '') $gameId = (string)($meta['seasonId'] ?? '');
  $slotKey = (string)($meta['slotKey'] ?? ($mode . ':' . implode('+', $users)));
  sort($users, SORT_STRING);
  return [
    'file' => basename($file),
    'savedAt' => (int)($meta['savedAt'] ?? @filemtime($file)),
    'seasonYear' => (int)($meta['seasonYear'] ?? 0),
    'divisionName' => (string)($meta['divisionName'] ?? ''),
    'divisionLevel' => division_level_local($meta),
    'users' => $users,
    'mode' => $mode,
    'slotKey' => $slotKey,
    'managerGameId' => $gameId,
  ];
}

function save_group_key_local(array $meta): string {
  $users = array_values(array_map('clean_name_local', array_map('strval', $meta['users'] ?? [])));
  sort($users, SORT_STRING);
  $slot = (string)($meta['slotKey'] ?? '');
  if ($slot === '') $slot = (string)($meta['mode'] ?? 'save') . ':' . implode('+', $users);
  $game = (string)($meta['managerGameId'] ?? '');
  return $game !== '' ? $slot . '|' . $game : $slot;
}

function prefer_meta_local(array $a, array $b): int {
  $year = ((int)($b['seasonYear'] ?? 0) <=> (int)($a['seasonYear'] ?? 0));
  if ($year !== 0) return $year;
  $level = ((int)($a['divisionLevel'] ?? 5) <=> (int)($b['divisionLevel'] ?? 5));
  if ($level !== 0) return $level;
  $saved = ((int)($b['savedAt'] ?? 0) <=> (int)($a['savedAt'] ?? 0));
  if ($saved !== 0) return $saved;
  return strcmp((string)($b['file'] ?? ''), (string)($a['file'] ?? ''));
}

function gzip_copy_local(string $source, string $target, bool $dryRun): bool {
  if ($dryRun) return true;
  $data = @file_get_contents($source);
  if (!is_string($data)) return false;
  $encoded = @gzencode($data, GZIP_LEVEL);
  if (!is_string($encoded)) return false;
  if (!is_dir(dirname($target))) @mkdir(dirname($target), 0775, true);
  if (@file_put_contents($target, $encoded, LOCK_EX) === false) return false;
  @chmod($target, 0666);
  return true;
}

function backup_active_save_local(string $file, string $archiveDir, bool $dryRun): bool {
  $base = date('Ymd-His') . '.cleanup.' . basename($file) . '.gz';
  $target = $archiveDir . '/' . $base;
  for ($i = 2; is_file($target); $i++) {
    $target = $archiveDir . '/' . preg_replace('/\.json\.gz$/', '.' . $i . '.json.gz', $base);
  }
  return gzip_copy_local($file, $target, $dryRun);
}

function sidecar_path_local(string $metaDir, string $id): string {
  return $metaDir . '/' . basename($id) . '.meta.json';
}

function prune_active_saves_local(string $saveDir, string $metaDir, string $archiveDir, bool $dryRun): array {
  $groups = [];
  foreach ((glob($saveDir . '/*.json') ?: []) as $file) {
    if (!is_file($file)) continue;
    $meta = save_meta_local($file);
    if (!is_array($meta)) continue;
    $key = save_group_key_local($meta);
    $groups[$key][] = ['file' => $file, 'meta' => $meta];
  }
  $deleted = 0;
  $backedUp = 0;
  foreach ($groups as $items) {
    usort($items, fn($a, $b) => prefer_meta_local($a['meta'], $b['meta']));
    foreach (array_slice($items, SAVE_KEEP_PER_GROUP) as $stale) {
      $file = (string)$stale['file'];
      if (backup_active_save_local($file, $archiveDir, $dryRun)) $backedUp++;
      if (!$dryRun && @unlink($file)) {
        @unlink(sidecar_path_local($metaDir, basename($file)));
        $deleted++;
      } elseif ($dryRun) {
        $deleted++;
      }
    }
  }
  return ['groups' => count($groups), 'activePruned' => $deleted, 'activeBackedUp' => $backedUp];
}

function compress_existing_json_backups_local(string $dir, bool $dryRun): array {
  $compressed = 0;
  foreach ((glob($dir . '/*.json') ?: []) as $file) {
    if (!is_file($file)) continue;
    $target = $file . '.gz';
    if (is_file($target)) {
      if (!$dryRun) @unlink($file);
      $compressed++;
      continue;
    }
    if (gzip_copy_local($file, $target, $dryRun)) {
      if (!$dryRun) @unlink($file);
      $compressed++;
    }
  }
  return ['compressed' => $compressed];
}

function backup_original_filename_local(string $file): string {
  $name = basename($file);
  if (str_ends_with($name, '.gz')) $name = substr($name, 0, -3);
  if (preg_match('/^\d{8}-\d{6}\.[a-z0-9_-]+\.([a-z0-9_.-]+\.json(?:\.\d+)?)$/i', $name, $m)) return $m[1];
  return $name;
}

function backup_group_key_local(string $file): string {
  $name = backup_original_filename_local($file);
  $name = preg_replace('/\.json$/i', '', $name);
  $key = preg_replace('/\.\d{8}-\d{6}(?:\.\d+)?$/', '', $name);
  return is_string($key) && $key !== '' ? $key : $name;
}

function prune_backup_dir_local(string $dir, bool $dryRun): array {
  $groups = [];
  foreach (array_merge(glob($dir . '/*.json') ?: [], glob($dir . '/*.json.gz') ?: []) as $file) {
    if (is_file($file)) $groups[backup_group_key_local($file)][] = $file;
  }
  $deleted = 0;
  foreach ($groups as $files) {
    usort($files, fn($a, $b) => ((int)@filemtime($b) <=> (int)@filemtime($a)) ?: strcmp(basename($b), basename($a)));
    foreach (array_slice($files, BACKUP_KEEP_PER_GROUP) as $stale) {
      if (!$dryRun) @unlink($stale);
      $deleted++;
    }
  }
  return ['backupGroups' => count($groups), 'backupPruned' => $deleted];
}

if (!is_dir($archiveDir) && !$dryRun) @mkdir($archiveDir, 0775, true);

$result = ['dryRun' => $dryRun];
$result += prune_active_saves_local($saveDir, $metaDir, $archiveDir, $dryRun);
foreach ([$liveDir, $archiveDir] as $dir) {
  if (!is_dir($dir)) continue;
  $label = basename($dir);
  $result[$label . 'Compressed'] = compress_existing_json_backups_local($dir, $dryRun)['compressed'];
  $pruned = prune_backup_dir_local($dir, $dryRun);
  $result[$label . 'Groups'] = $pruned['backupGroups'];
  $result[$label . 'Pruned'] = $pruned['backupPruned'];
}

echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL;
