<?php
// app/services/LayoutService.php

declare(strict_types=1);

namespace ScMedia\Services;

use ScMedia\Db\Db;

final class LayoutService {
  private Db $db;
  private array $config;

  public function __construct(Db $db, array $config) {
    $this->db = $db;
    $this->config = $config;
  }

  public function preview(array $settingsAll, string $kind, string $mode, int $limit = 10, ?array $sampleItems = null): array {
    $kind = ($kind === 'series') ? 'series' : 'movies';
    $limit = max(1, min($limit, 50));

    $paths = $settingsAll['paths'] ?? [];
    $layout = $settingsAll['layout'] ?? [];
    $root = ($kind === 'series') ? (string)($paths['series_root'] ?? '') : (string)($paths['movies_root'] ?? '');

    $cfg = ($kind === 'series') ? ($layout['series'] ?? []) : ($layout['movies'] ?? []);
    $norm = $layout['normalization'] ?? [];
    $collision = (string)($layout['collision_policy'] ?? 'stop');

    $items = [];
    if ($mode === 'samples') {
      $items = $this->defaultSamples($kind);
    } else if (is_array($sampleItems)) {
      $items = $sampleItems;
    } else {
      // Pull some items from DB (best effort)
      $rows = $this->db->fetchAll("
        SELECT display_name, year
        FROM items
        WHERE status = 'active'
        ORDER BY id DESC
        LIMIT :lim
      ", [':lim' => $limit]);
      foreach ($rows as $r) {
        $items[] = [
          'title' => (string)$r['display_name'],
          'year' => isset($r['year']) ? (int)$r['year'] : null,
        ];
      }
      if (count($items) === 0) {
        $items = $this->defaultSamples($kind);
      }
    }

    $examples = [];
    foreach (array_slice($items, 0, $limit) as $it) {
      $title = (string)($it['title'] ?? '');
      $year = $it['year'] ?? null;

      $folderName = $this->buildDisplayFolder($title, $year);
      $rel = $this->computeShardPath($cfg, $norm, $folderName, $year);
      $abs = rtrim($root, '/') . '/' . ltrim($rel, '/');

      $examples[] = [
        'input' => ['title' => $title, 'year' => $year],
        'output_rel' => $rel,
        'output_abs' => $abs,
        'notes' => [
          'collision_policy=' . $collision,
        ],
      ];
    }

    return [
      'kind' => $kind,
      'examples' => $examples,
    ];
  }

  private function buildDisplayFolder(string $title, $year): string {
    $t = trim($title);
    if ($year !== null && is_int($year) && $year > 1800 && $year < 2100) {
      return $t . " (" . $year . ")";
    }
    return $t;
  }

  private function computeShardPath(array $cfg, array $norm, string $folderName, $year): string {
    $strategy = (string)($cfg['strategy'] ?? 'prefix');
    $params = is_array($cfg['params'] ?? null) ? $cfg['params'] : [];
    $template = $cfg['template'] ?? null;

    $name = $this->normalizeName($folderName, $norm);
    $base = $name;

    if ($strategy === 'flat') {
      return $base;
    }

    if ($strategy === 'first_letter') {
      $sh = $this->firstLetters($name, 1, (bool)($norm['uppercase_shards'] ?? true));
      return $sh . '/' . $base;
    }

    if ($strategy === 'prefix') {
      $n = (int)($params['n'] ?? 2);
      $n = max(1, min($n, 4));
      $sh = $this->firstLetters($name, $n, (bool)($norm['uppercase_shards'] ?? true));
      return $sh . '/' . $base;
    }

    if ($strategy === 'hash_buckets') {
      $b = (int)($params['buckets'] ?? 100);
      if (!in_array($b, [10, 50, 100, 200], true)) {
        $b = 100;
      }
      $idx = crc32(mb_strtolower($name, 'UTF-8')) % $b;
      $sh = str_pad((string)$idx, 2, '0', STR_PAD_LEFT);
      return $sh . '/' . $base;
    }

    if ($strategy === 'by_year') {
      $y = (is_int($year) && $year > 1800 && $year < 2100) ? (string)$year : '_unknown';
      return $y . '/' . $base;
    }

    if ($strategy === 'letter_year') {
      $sh = $this->firstLetters($name, 1, (bool)($norm['uppercase_shards'] ?? true));
      $y = (is_int($year) && $year > 1800 && $year < 2100) ? (string)$year : '_unknown';
      return $sh . '/' . $y . '/' . $base;
    }

    if ($strategy === 'decade_year') {
      $y = (is_int($year) && $year > 1800 && $year < 2100) ? $year : null;
      $dec = ($y !== null) ? ((int)floor($y / 10) * 10) . "s" : '_unknown';
      $yy = ($y !== null) ? (string)$y : '_unknown';
      return $dec . '/' . $yy . '/' . $base;
    }

    if ($strategy === 'custom' && is_string($template) && trim($template) !== '') {
      return $this->applyTemplate($template, $name, $year);
    }

    // Default fallback
    $sh = $this->firstLetters($name, 2, (bool)($norm['uppercase_shards'] ?? true));
    return $sh . '/' . $base;
  }

  private function normalizeName(string $name, array $norm): string {
    $s = $name;

    if (!empty($norm['trim_dots_spaces'])) {
      $s = trim($s, " \t\n\r\0\x0B.");
    } else {
      $s = trim($s);
    }

    if (!empty($norm['ignore_articles'])) {
      $lower = mb_strtolower($s, 'UTF-8');
      foreach (['the ', 'a ', 'an '] as $a) {
        if (str_starts_with($lower, $a)) {
          $s = trim(mb_substr($s, mb_strlen($a, 'UTF-8'), null, 'UTF-8'));
          break;
        }
      }
    }

    if (!empty($norm['replace_unsafe_chars'])) {
      $s = preg_replace('/[\/\\\\:\*\?"<>\|]+/u', ' ', $s);
      $s = preg_replace('/\s+/u', ' ', $s);
      $s = trim((string)$s);
    }

    return $s;
  }

  private function firstLetters(string $name, int $n, bool $upper): string {
    $t = mb_strtolower(trim($name), 'UTF-8');
    if ($t === '') {
      return '_';
    }
    $letters = mb_substr($t, 0, $n, 'UTF-8');
    if ($upper) {
      $letters = mb_strtoupper($letters, 'UTF-8');
    }
    // sanitize shard
    $letters = preg_replace('/[^0-9A-Za-zА-Яа-я]+/u', '_', (string)$letters);
    return $letters !== '' ? $letters : '_';
  }

  private function applyTemplate(string $tpl, string $name, $year): string {
    $y = (is_int($year) && $year > 1800 && $year < 2100) ? (string)$year : '_unknown';
    $dec = (is_int($year) && $year > 1800 && $year < 2100) ? ((int)floor($year / 10) * 10) . "s" : '_unknown';

    $rep = [
      '{title}' => $name,
      '{year}' => $y,
      '{decade}' => $dec,
      '{first}' => $this->firstLetters($name, 1, true),
      '{first:2}' => $this->firstLetters($name, 2, true),
      '{first:3}' => $this->firstLetters($name, 3, true),
      '{hash:2}' => str_pad((string)(crc32(mb_strtolower($name, 'UTF-8')) % 100), 2, '0', STR_PAD_LEFT),
    ];

    $out = strtr($tpl, $rep);
    $out = trim($out, "/ \t\n\r\0\x0B");

    return $out;
  }

  private function defaultSamples(string $kind): array {
    if ($kind === 'series') {
      return [
        ['title' => 'Breaking Bad', 'year' => 2008],
        ['title' => 'The Office', 'year' => 2005],
        ['title' => 'Чернобыль', 'year' => 2019],
      ];
    }

    return [
      ['title' => 'Avatar', 'year' => 2009],
      ['title' => 'The Matrix', 'year' => 1999],
      ['title' => 'Сталкер', 'year' => 1979],
    ];
  }
}
