<?php

declare(strict_types=1);

namespace Autogedal\FeedMonitor\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;

class Config
{
    private const PATH = 'autogedal_feed_monitor/';

    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig
    ) {
    }

    public function isEmailEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(self::PATH . 'email/enabled');
    }

    /**
     * @return string[]
     */
    public function getRecipients(): array
    {
        $raw = (string)$this->scopeConfig->getValue(self::PATH . 'email/recipients');
        $emails = preg_split('/[\s,;]+/', $raw) ?: [];

        return array_values(array_unique(array_filter($emails, static fn (string $email): bool =>
            filter_var($email, FILTER_VALIDATE_EMAIL) !== false)));
    }

    public function getSenderIdentity(): string
    {
        return (string)($this->scopeConfig->getValue(self::PATH . 'email/identity') ?: 'general');
    }

    public function getEmailTemplate(): string
    {
        return (string)($this->scopeConfig->getValue(self::PATH . 'email/template') ?: 'autogedal_feed_monitor_email_template');
    }

    public function getReminderHours(): float
    {
        return max(0.25, (float)($this->scopeConfig->getValue(self::PATH . 'email/reminder_hours') ?: 6));
    }

    /**
     * Minimum share of the last successful run (percent) a feed must have before an update may start; 0 = no check.
     */
    public function getIncompleteThreshold(string $jobCode, string $feedCode = ''): float
    {
        $overrides = $this->getThresholdOverrides();
        $key = MonitorKey::build($jobCode, $feedCode);
        $value = $overrides[$key] ?? $overrides[$jobCode]
            ?? (float)$this->scopeConfig->getValue(self::PATH . 'guard/incomplete_threshold');

        return min(100.0, max(0.0, (float)$value));
    }

    /**
     * Interval after which a job that has not started is reported: the admin value ("job=minutes" lines), else the
     * default declared with the job; null: not checked (no regular schedule).
     */
    public function getMaxAgeMinutes(string $jobCode, ?int $default): ?int
    {
        $overrides = $this->parseLines((string)$this->scopeConfig->getValue(self::PATH . 'schedule/max_age'));
        if (isset($overrides[$jobCode])) {
            return max(1, (int)$overrides[$jobCode]);
        }

        return $default === null ? null : max(1, $default);
    }

    public function getConnectTimeout(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'fetch/connect_timeout') ?: 20));
    }

    public function getTimeout(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'fetch/timeout') ?: 120));
    }

    /**
     * Upper limit for reading one feed, all attempts and pauses included.
     */
    public function getMaxTotalSeconds(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'fetch/max_total') ?: 300));
    }

    public function getAttempts(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'fetch/attempts') ?: 3));
    }

    public function getRetryDelay(): int
    {
        return max(0, (int)$this->scopeConfig->getValue(self::PATH . 'fetch/retry_delay'));
    }

    public function getHeartbeatMaxIdleMinutes(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'heartbeat/max_idle_minutes') ?: 30));
    }

    /**
     * After how long a run still "running" is considered dead: the job's own value ("job=minutes" lines) or the
     * default.
     */
    public function getStuckAfterMinutes(string $jobCode = ''): int
    {
        $default = max(1, (int)($this->scopeConfig->getValue(self::PATH . 'history/stuck_after_minutes') ?: 120));
        $overrides = $this->parseLines((string)$this->scopeConfig->getValue(self::PATH . 'history/stuck_after_overrides'));

        return max(1, (int)($overrides[$jobCode] ?? $default));
    }

    public function getShortestStuckAfterMinutes(): int
    {
        $overrides = $this->parseLines((string)$this->scopeConfig->getValue(self::PATH . 'history/stuck_after_overrides'));

        return (int)min(array_merge([$this->getStuckAfterMinutes()], array_map(static fn (float $minutes): int => max(1, (int)$minutes), $overrides)));
    }

    public function getRetentionDays(): int
    {
        return max(1, (int)($this->scopeConfig->getValue(self::PATH . 'history/retention_days') ?: 30));
    }

    /**
     * "job=70" / "job:feed=30" lines.
     *
     * @return array<string, float>
     */
    private function getThresholdOverrides(): array
    {
        return $this->parseLines((string)$this->scopeConfig->getValue(self::PATH . 'guard/threshold_overrides'));
    }

    /**
     * "key=number" lines (an optional % is ignored), invalid lines are skipped.
     *
     * @return array<string, float>
     */
    private function parseLines(string $raw): array
    {
        $values = [];
        foreach (preg_split('/\R/', $raw) ?: [] as $line) {
            if (preg_match('/^\s*([\w:.-]+)\s*=\s*(\d+(?:[.,]\d+)?)\s*%?\s*$/', $line, $match)) {
                $values[$match[1]] = (float)str_replace(',', '.', $match[2]);
            }
        }

        return $values;
    }
}
