<?php
// app/services/ScanProfilesService.php

declare(strict_types=1);

namespace ScMedia\Services;

use ScMedia\Db\Db;
use Exception;

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

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

  public function list(): array {
    return $this->db->fetchAll("
      SELECT
        id, sort_order, profile_type, enabled, name, root_path, max_depth,
        exclude_patterns_json, include_ext_mode, include_ext_json,
        last_scan_at, last_result
      FROM scan_profiles
      ORDER BY sort_order ASC, id ASC
    ");
  }

  public function create(array $p): int {
    $this->validate($p);

    $exclude = json_encode($p['exclude_patterns'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $include = null;
    if (($p['include_ext_mode'] ?? 'default') === 'custom') {
      $include = json_encode($p['include_ext'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    $sortOrder = isset($p['sort_order']) ? (int)$p['sort_order'] : $this->nextSortOrder();

    $this->db->exec("
      INSERT INTO scan_profiles
        (sort_order, profile_type, enabled, name, root_path, max_depth, exclude_patterns_json, include_ext_mode, include_ext_json)
      VALUES
        (:sort_order, :ptype, :enabled, :name, :root, :depth, :exclude, :mode, :include)
    ", [
      ':sort_order' => $sortOrder,
      ':ptype' => (string)($p['profile_type'] ?? 'scan'),
      ':enabled' => !empty($p['enabled']) ? 1 : 0,
      ':name' => (string)$p['name'],
      ':root' => (string)$p['root_path'],
      ':depth' => (int)$p['max_depth'],
      ':exclude' => $exclude ?: '[]',
      ':mode' => (string)($p['include_ext_mode'] ?? 'default'),
      ':include' => $include,
    ]);

    $row = $this->db->fetchOne("SELECT LAST_INSERT_ID() AS id");
    return (int)($row['id'] ?? 0);
  }

  public function update(int $id, array $p): void {
    $this->validate($p);

    $exclude = json_encode($p['exclude_patterns'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $include = null;
    if (($p['include_ext_mode'] ?? 'default') === 'custom') {
      $include = json_encode($p['include_ext'] ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    $this->db->exec("
      UPDATE scan_profiles
      SET
        profile_type = :ptype,
        enabled = :enabled,
        name = :name,
        root_path = :root,
        max_depth = :depth,
        exclude_patterns_json = :exclude,
        include_ext_mode = :mode,
        include_ext_json = :include
      WHERE id = :id
    ", [
      ':ptype' => (string)($p['profile_type'] ?? 'scan'),
      ':enabled' => !empty($p['enabled']) ? 1 : 0,
      ':name' => (string)$p['name'],
      ':root' => (string)$p['root_path'],
      ':depth' => (int)$p['max_depth'],
      ':exclude' => $exclude ?: '[]',
      ':mode' => (string)($p['include_ext_mode'] ?? 'default'),
      ':include' => $include,
      ':id' => $id,
    ]);
  }

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

  public function reorder(array $ids): void {
    $ids = array_values(array_filter(array_map('intval', $ids), fn($v) => $v > 0));
    if (count($ids) === 0) return;

    $this->db->begin();
    try {
      $order = 10;
      foreach ($ids as $id) {
        $this->db->exec("
          UPDATE scan_profiles
          SET sort_order = :o
          WHERE id = :id
        ", [':o' => $order, ':id' => $id]);
        $order += 10;
      }
      $this->db->commit();
    } catch (\Throwable $e) {
      $this->db->rollBack();
      throw $e;
    }
  }

  private function validate(array $p): void {
    $name = trim((string)($p['name'] ?? ''));
    $root = trim((string)($p['root_path'] ?? ''));
    $depth = (int)($p['max_depth'] ?? 0);
    $ptype = (string)($p['profile_type'] ?? 'scan');

    if ($name === '') {
      throw new Exception("Profile name is required");
    }
    if ($root === '' || $root[0] !== '/') {
      throw new Exception("Root path must be an absolute path");
    }
    if (!in_array($ptype, ['scan', 'analyze'], true)) {
      throw new Exception("Invalid profile type");
    }

    $fromSettings = $this->settings->getAll()['safety'] ?? [];
    $hardMaxDepth = (int)($fromSettings['max_depth'] ?? ($this->config['safety']['hard_limits']['max_depth'] ?? 10));
    if ($depth < 1 || $depth > $hardMaxDepth) {
      throw new Exception("Max depth out of range");
    }

    $mode = (string)($p['include_ext_mode'] ?? 'default');
    if (!in_array($mode, ['default', 'custom'], true)) {
      throw new Exception("Invalid include_ext_mode");
    }
  }

  private function nextSortOrder(): int {
    $row = $this->db->fetchOne("SELECT MAX(sort_order) AS max_sort FROM scan_profiles");
    $max = isset($row['max_sort']) ? (int)$row['max_sort'] : 0;
    return $max + 10;
  }
}
