<?php
// app/services/MetadataService.php

declare(strict_types=1);

namespace ScMedia\Services;

use ScMedia\Db\Db;
use ScMedia\Services\Metadata\MetadataProvider;

final class MetadataService {
  private Db $db;
  private SettingsService $settings;
  private ?LogService $logger;
  /** @var MetadataProvider[] */
  private array $providers;

  /**
   * @param MetadataProvider[] $providers
   */
  public function __construct(Db $db, SettingsService $settings, array $providers, ?LogService $logger = null) {
    $this->db = $db;
    $this->settings = $settings;
    $this->providers = $providers;
    $this->logger = $logger;
  }

  public function search(string $query, string $type, ?int $year): array {
    $query = trim($query);
    if ($query === '') return [];

    $cfg = $this->getMetadataSettings();
    $langs = $this->getLanguages($cfg);
    $order = $this->getProviderPriority($cfg);
    $enabled = $this->getEnabledProviders($cfg, $order);

    $results = [];
    foreach ($enabled as $pid => $provider) {
      $pCfg = $cfg['providers'][$pid] ?? [];
      foreach ($langs as $lang) {
        $this->logger?->log('debug', 'Metadata search request', [
          'provider' => $pid,
          'lang' => $lang,
          'query' => $query,
          'type' => $type,
          'year' => $year,
        ]);
        $found = $provider->search($query, $type, $year, $lang, $pCfg);
        $this->logger?->log('info', 'Metadata provider results', [
          'provider' => $pid,
          'lang' => $lang,
          'count' => count($found),
        ]);
        foreach ($found as $row) {
          $key = $pid . ':' . (string)($row['provider_id'] ?? '');
          if ($key === $pid . ':') continue;
          if (!isset($results[$key])) {
            $results[$key] = $row;
            $results[$key]['title_map'] = is_array($row['title_map'] ?? null) ? $row['title_map'] : [];
            $results[$key]['_priority'] = array_search($pid, $order, true);
          } else {
            $existing = $results[$key]['title_map'] ?? [];
            $add = is_array($row['title_map'] ?? null) ? $row['title_map'] : [];
            $results[$key]['title_map'] = array_merge($existing, $add);
            if (($results[$key]['original_title'] ?? '') === '' && !empty($row['original_title'])) {
              $results[$key]['original_title'] = $row['original_title'];
            }
            if (($results[$key]['year'] ?? null) === null && isset($row['year'])) {
              $results[$key]['year'] = $row['year'];
            }
            if (($results[$key]['poster'] ?? '') === '' && !empty($row['poster'])) {
              $results[$key]['poster'] = $row['poster'];
            }
          }
        }
      }
    }

    $out = array_values($results);
    usort($out, function($a, $b) {
      $pa = $a['_priority'] ?? 999;
      $pb = $b['_priority'] ?? 999;
      if ($pa === $pb) return 0;
      return $pa < $pb ? -1 : 1;
    });
    foreach ($out as &$r) {
      unset($r['_priority']);
    }
    return $out;
  }

  public function getSubjectMeta(string $kind, string $key): ?array {
    if ($key === '') return null;
    $row = $this->db->fetchOne("
      SELECT *
      FROM media_metadata
      WHERE subject_kind=:k AND subject_key=:s
      LIMIT 1
    ", [':k' => $kind, ':s' => $key]);
    return $row ? $this->decodeMetaRow($row) : null;
  }

  public function getSubjectMetaMap(string $kind, array $keys): array {
    $keys = array_values(array_filter($keys, fn($k) => $k !== ''));
    if (count($keys) === 0) return [];
    $placeholders = [];
    $params = [':k' => $kind];
    foreach ($keys as $i => $k) {
      $ph = ':p' . $i;
      $placeholders[] = $ph;
      $params[$ph] = $k;
    }
    $rows = $this->db->fetchAll("
      SELECT *
      FROM media_metadata
      WHERE subject_kind=:k
        AND subject_key IN (" . implode(',', $placeholders) . ")
    ", $params);
    $out = [];
    foreach ($rows as $r) {
      $row = $this->decodeMetaRow($r);
      $out[(string)$row['subject_key']] = $row;
    }
    return $out;
  }

  public function saveSelection(string $kind, string $key, array $payload): void {
    $provider = (string)($payload['provider'] ?? '');
    $providerId = (string)($payload['provider_id'] ?? '');
    $titleMap = is_array($payload['title_map'] ?? null) ? $payload['title_map'] : [];
    $originalTitle = (string)($payload['original_title'] ?? '');
    $year = isset($payload['year']) ? (int)$payload['year'] : null;

    $this->upsertMeta($kind, $key, [
      'provider' => $provider !== '' ? $provider : null,
      'provider_id' => $providerId !== '' ? $providerId : null,
      'title_map' => $titleMap,
      'original_title' => $originalTitle !== '' ? $originalTitle : null,
      'year' => $year,
      'manual_title' => null,
      'manual_year' => null,
      'source' => 'auto',
    ]);
  }

  public function saveManual(string $kind, string $key, string $title, ?int $year): void {
    $row = $this->getSubjectMeta($kind, $key);
    $payload = [
      'provider' => $row['provider'] ?? null,
      'provider_id' => $row['provider_id'] ?? null,
      'title_map' => $row['title_map'] ?? [],
      'original_title' => $row['original_title'] ?? null,
      'year' => $row['year'] ?? null,
      'manual_title' => $title !== '' ? $title : null,
      'manual_year' => $year,
      'source' => 'manual',
    ];
    $this->upsertMeta($kind, $key, $payload);
  }

  public function clearManual(string $kind, string $key): void {
    $row = $this->getSubjectMeta($kind, $key);
    if (!$row) return;
    $payload = [
      'provider' => $row['provider'] ?? null,
      'provider_id' => $row['provider_id'] ?? null,
      'title_map' => $row['title_map'] ?? [],
      'original_title' => $row['original_title'] ?? null,
      'year' => $row['year'] ?? null,
      'manual_title' => null,
      'manual_year' => null,
      'source' => 'auto',
    ];
    $this->upsertMeta($kind, $key, $payload);
  }

  public function getProviderLabel(string $providerId): string {
    foreach ($this->providers as $p) {
      if ($p->id() === $providerId) return $p->label();
    }
    return $providerId;
  }

  public function getUiLanguage(): string {
    $all = $this->settings->getAll();
    return (string)($all['general']['language'] ?? 'en');
  }

  public function formatDisplay(?array $meta, ?string $uiLang = null): array {
    $uiLang = $uiLang ?? $this->getUiLanguage();
    $titleMap = is_array($meta['title_map'] ?? null) ? $meta['title_map'] : [];
    $manualTitle = (string)($meta['manual_title'] ?? '');
    $manualYear = $meta['manual_year'] ?? null;

    $titleDisplay = '';
    $titleOriginal = (string)($meta['original_title'] ?? '');
    $year = $meta['year'] ?? null;

    if (!empty($meta) && ($meta['source'] ?? '') === 'manual' && $manualTitle !== '') {
      $titleDisplay = $manualTitle;
      $year = $manualYear !== null ? (int)$manualYear : $year;
    } else {
      if (isset($titleMap[$uiLang]) && $titleMap[$uiLang] !== '') {
        $titleDisplay = (string)$titleMap[$uiLang];
      } else if (count($titleMap) > 0) {
        $first = array_values($titleMap)[0];
        $titleDisplay = is_string($first) ? $first : '';
      }
    }

    if ($titleOriginal === '' && $titleDisplay !== '') {
      $titleOriginal = $titleDisplay;
    }

    $metaOut = null;
    if (is_array($meta)) {
      $metaOut = [
        'provider' => $meta['provider'] ?? null,
        'provider_id' => $meta['provider_id'] ?? null,
        'source' => $meta['source'] ?? 'auto',
        'title_map' => $titleMap,
        'original_title' => $meta['original_title'] ?? null,
        'manual_title' => $meta['manual_title'] ?? null,
        'manual_year' => $meta['manual_year'] ?? null,
        'year' => $meta['year'] ?? null,
      ];
    }

    return [
      'title_display' => $titleDisplay,
      'title_original' => $titleOriginal,
      'year' => $year !== null ? (int)$year : null,
      'meta' => $metaOut,
    ];
  }

  public function buildProviderUrl(string $providerId, string $externalId): string {
    foreach ($this->providers as $p) {
      if ($p->id() === $providerId) return $p->buildUrl($externalId);
    }
    return '';
  }

  private function decodeMetaRow(array $row): array {
    $map = [];
    if (isset($row['title_map_json'])) {
      $decoded = json_decode((string)$row['title_map_json'], true);
      if (is_array($decoded)) $map = $decoded;
    }
    $row['title_map'] = $map;
    return $row;
  }

  private function upsertMeta(string $kind, string $key, array $payload): void {
    $json = json_encode($payload['title_map'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if ($json === false) $json = '{}';

    $this->db->exec("
      INSERT INTO media_metadata
        (subject_kind, subject_key, provider, provider_id, title_map_json, original_title, year, manual_title, manual_year, source)
      VALUES
        (:kind, :key, :provider, :provider_id, :title_map, :original_title, :year, :manual_title, :manual_year, :source)
      ON DUPLICATE KEY UPDATE
        provider = VALUES(provider),
        provider_id = VALUES(provider_id),
        title_map_json = VALUES(title_map_json),
        original_title = VALUES(original_title),
        year = VALUES(year),
        manual_title = VALUES(manual_title),
        manual_year = VALUES(manual_year),
        source = VALUES(source),
        updated_at = NOW()
    ", [
      ':kind' => $kind,
      ':key' => $key,
      ':provider' => $payload['provider'],
      ':provider_id' => $payload['provider_id'],
      ':title_map' => $json,
      ':original_title' => $payload['original_title'],
      ':year' => $payload['year'],
      ':manual_title' => $payload['manual_title'],
      ':manual_year' => $payload['manual_year'],
      ':source' => $payload['source'],
    ]);
  }

  private function getMetadataSettings(): array {
    $all = $this->settings->getAll();
    $meta = $all['metadata'] ?? [];
    if (!is_array($meta)) $meta = [];
    if (!isset($meta['enabled'])) $meta['enabled'] = false;
    if (!isset($meta['languages'])) $meta['languages'] = ['de','ru','en'];
    if (!isset($meta['providers'])) $meta['providers'] = [];
    if (!isset($meta['provider_priority'])) $meta['provider_priority'] = ['tvdb','omdb'];
    $meta = $this->applyEnvOverrides($meta);
    return $meta;
  }

  private function getLanguages(array $cfg): array {
    $langs = is_array($cfg['languages'] ?? null) ? $cfg['languages'] : [];
    $clean = [];
    foreach ($langs as $l) {
      $l = trim((string)$l);
      if ($l !== '') $clean[] = $l;
    }
    if (count($clean) === 0) {
      $clean = ['de','ru','en'];
    }
    return array_values(array_unique($clean));
  }

  private function getProviderPriority(array $cfg): array {
    $list = is_array($cfg['provider_priority'] ?? null) ? $cfg['provider_priority'] : [];
    $clean = [];
    foreach ($list as $p) {
      $p = trim((string)$p);
      if ($p !== '') $clean[] = $p;
    }
    if (count($clean) === 0) {
      $clean = ['tvdb','omdb'];
    }
    return $clean;
  }

  /**
   * @return array<string, MetadataProvider>
   */
  private function getEnabledProviders(array $cfg, array $order): array {
    if (empty($cfg['enabled'])) {
      return [];
    }
    $out = [];
    foreach ($order as $pid) {
      foreach ($this->providers as $p) {
        if ($p->id() !== $pid) continue;
        $pCfg = $cfg['providers'][$pid] ?? [];
        if (!empty($pCfg['enabled'])) {
          $out[$pid] = $p;
        }
      }
    }
    return $out;
  }

  private function applyEnvOverrides(array $meta): array {
    $envOmdb = getenv('OMDB_API_KEY') ?: '';
    $envTvdb = getenv('TVDB_API_KEY') ?: '';
    $envOmdbEnabled = getenv('OMDB_ENABLED');
    $envTvdbEnabled = getenv('TVDB_ENABLED');

    if ($envOmdb !== '') {
      $meta['providers']['omdb']['api_key'] = $envOmdb;
      $meta['providers']['omdb']['enabled'] = true;
    } else if ($envOmdbEnabled !== false) {
      $meta['providers']['omdb']['enabled'] = filter_var($envOmdbEnabled, FILTER_VALIDATE_BOOLEAN);
    }

    if ($envTvdb !== '') {
      $meta['providers']['tvdb']['api_key'] = $envTvdb;
      $meta['providers']['tvdb']['enabled'] = true;
    } else if ($envTvdbEnabled !== false) {
      $meta['providers']['tvdb']['enabled'] = filter_var($envTvdbEnabled, FILTER_VALIDATE_BOOLEAN);
    }

    $envPin = getenv('TVDB_PIN') ?: '';
    if ($envPin !== '') {
      $meta['providers']['tvdb']['pin'] = $envPin;
    }

    return $meta;
  }
}
