<?php
// app/services/SettingsService.php

declare(strict_types=1);

namespace ScMedia\Services;

use ScMedia\Db\Db;
use Exception;

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

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

  public function getAll(): array {
    $rows = $this->db->fetchAll("SELECT `key`, value_json FROM settings");
    $map = [];
    foreach ($rows as $r) {
      $k = (string)$r['key'];
      $v = json_decode((string)$r['value_json'], true);
      $map[$k] = is_array($v) ? $v : [];
    }

    $system = $map['_system'] ?? ['settings_revision' => 1, 'first_run_completed' => false];

    return [
      'meta' => [
        'app_id' => (string)($this->config['app']['app_id'] ?? ''),
        'env' => (string)($this->config['app']['env'] ?? 'production'),
        'debug_tools_enabled' => (bool)($this->config['app']['debug_tools_enabled'] ?? false),
        'allow_db_reset' => (bool)($this->config['app']['allow_db_reset'] ?? false),
        'settings_revision' => (int)($system['settings_revision'] ?? 1),
        'backend_version' => (string)($this->config['app']['backend_version'] ?? ''),
        'db_version' => (string)($this->config['app']['db_version'] ?? ''),
        'plugins_version' => (string)($this->config['app']['plugins_version'] ?? ''),
      ],
      'general' => $map['general'] ?? [],
      'scanner_defaults' => $map['scanner_defaults'] ?? [],
      'paths' => $map['paths'] ?? [],
      'tools' => $map['tools'] ?? [],
      'logs' => $map['logs'] ?? [],
      'layout' => $map['layout'] ?? [],
      'media_rules' => $map['media_rules'] ?? [],
      'rules' => $map['rules'] ?? [],
      'templates' => $map['templates'] ?? [],
      'sources' => $map['sources'] ?? [],
      'metadata' => $map['metadata'] ?? [],
      'exports' => $map['exports'] ?? [],
      'ui' => $map['ui'] ?? [],
      'background' => $map['background'] ?? [],
      'safety' => $map['safety'] ?? [],
      'tasks' => $map['tasks'] ?? [],
      '_system' => $system,
    ];
  }

  public function saveBulk(array $payload): int {
    $all = $this->getAll();
    $rev = (int)($all['meta']['settings_revision'] ?? 1);

    $if = isset($payload['if_revision']) ? (int)$payload['if_revision'] : null;
    if ($if !== null && $if !== $rev) {
      throw new Exception("Settings revision mismatch");
    }

    $allowed = ['general', 'scanner_defaults', 'paths', 'tools', 'logs', 'layout', 'media_rules', 'rules', 'templates', 'sources', 'metadata', 'exports', 'ui', 'background', 'safety', 'tasks', 'pending_tasks'];
    $toSave = [];
    foreach ($allowed as $k) {
      if (array_key_exists($k, $payload)) {
        $toSave[$k] = $payload[$k];
      }
    }

    $newRev = $rev + 1;
    $system = $all['_system'] ?? [];
    $system['settings_revision'] = $newRev;

    $this->db->begin();
    try {
      foreach ($toSave as $k => $v) {
        $this->upsertJson($k, $v);
      }
      $this->upsertJson('_system', $system);
      $this->db->commit();
    } catch (Exception $e) {
      $this->db->rollBack();
      throw $e;
    }

    return $newRev;
  }

  public function getConfig(): array {
    return $this->config;
  }

  public function listSnapshots(int $limit = 50): array {
    $limit = max(1, min(200, $limit));
    return $this->db->fetchAll("
      SELECT id, label, created_at, created_by
      FROM settings_snapshots
      ORDER BY id DESC
      LIMIT {$limit}
    ");
  }

  public function createSnapshot(?string $label = null, ?int $createdBy = null): int {
    $all = $this->getAll();
    unset($all['meta']);
    $json = json_encode($all, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if ($json === false) {
      $json = '{}';
    }
    $this->db->exec("
      INSERT INTO settings_snapshots (label, data_json, created_by)
      VALUES (:label, :data_json, :created_by)
    ", [
      ':label' => ($label !== '' ? $label : null),
      ':data_json' => $json,
      ':created_by' => $createdBy,
    ]);
    $row = $this->db->fetchOne("SELECT LAST_INSERT_ID() AS id");
    return (int)($row['id'] ?? 0);
  }

  public function restoreSnapshot(int $id): void {
    $row = $this->db->fetchOne("SELECT data_json FROM settings_snapshots WHERE id = :id", [':id' => $id]);
    if (!$row || !isset($row['data_json'])) {
      throw new Exception('Snapshot not found');
    }
    $data = json_decode((string)$row['data_json'], true);
    if (!is_array($data)) {
      throw new Exception('Snapshot data invalid');
    }

    $system = $this->getAll()['_system'] ?? [];
    $system['settings_revision'] = (int)($system['settings_revision'] ?? 1) + 1;
    $data['_system'] = $system;

    $this->db->begin();
    try {
      $this->db->exec("DELETE FROM settings");
      foreach ($data as $key => $value) {
        $this->upsertJson((string)$key, $value);
      }
      $this->db->commit();
    } catch (Exception $e) {
      $this->db->rollBack();
      throw $e;
    }
  }

  public function deleteSnapshot(int $id): void {
    $this->db->exec("DELETE FROM settings_snapshots WHERE id = :id", [':id' => $id]);
  }


  private function upsertJson(string $key, $value): void {
    $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if ($json === false) {
      $json = '{}';
    }

    $this->db->exec("
      INSERT INTO settings (`key`, value_json)
      VALUES (:k, :v)
      ON DUPLICATE KEY UPDATE value_json = VALUES(value_json)
    ", [
      ':k' => $key,
      ':v' => $json,
    ]);
  }
}
