<?php

namespace App\Campaigns;

class PerformanceVerdictCombiner
{
    /**
     * @param  array<string, array{verdict: string, reason: string}>  $windows
     * @return array{verdict: PerformanceVerdict, reason: string}
     */
    public function final(array $windows): array
    {
        $verdicts = $this->verdicts($windows);

        if ($verdicts === []) {
            return [
                'verdict' => PerformanceVerdict::NoAction,
                'reason' => 'No analysis windows were evaluated.',
            ];
        }

        if ($this->contains($verdicts, PerformanceVerdict::ActionRequired)) {
            return $this->pick($windows, PerformanceVerdict::ActionRequired, 'Fix delivery or sync before changing the campaign.');
        }

        $pauseCount = $this->count($verdicts, PerformanceVerdict::Pause);
        $has3hPause = ($windows['3h']['verdict'] ?? null) === PerformanceVerdict::Pause->value;
        $has7dPause = ($windows['7d']['verdict'] ?? null) === PerformanceVerdict::Pause->value;

        if ($pauseCount >= 2 || ($has3hPause && $has7dPause)) {
            return [
                'verdict' => PerformanceVerdict::Stop,
                'reason' => 'Multiple windows agree: pause campaign now.',
            ];
        }

        if ($this->contains($verdicts, PerformanceVerdict::Pause)) {
            return $this->pick($windows, PerformanceVerdict::Pause, 'Performance looks weak in at least one window.');
        }

        if ($this->contains($verdicts, PerformanceVerdict::Start)) {
            return $this->pick($windows, PerformanceVerdict::Start, 'Campaign is paused but recent windows look healthy enough to enable.');
        }

        if ($this->contains($verdicts, PerformanceVerdict::Continue)) {
            return $this->pick($windows, PerformanceVerdict::Continue, 'Recent windows look acceptable. Keep running.');
        }

        return $this->pick($windows, PerformanceVerdict::NoAction, 'Still in learning or waiting for more signal.');
    }

    /**
     * @param  array<string, array{verdict: string, reason: string}>  $windows
     * @return array{verdict: PerformanceVerdict, reason: string}|null
     */
    public function hourly(array $windows): ?array
    {
        $subset = array_intersect_key($windows, array_flip(PerformanceWindows::hourWindowKeys()));

        if ($subset === []) {
            return null;
        }

        $result = $this->final($subset);
        $result['reason'] = sprintf('Hourly check: %s', $result['reason']);

        return $result;
    }

    /**
     * @param  array<string, array{verdict: string, reason: string}>  $windows
     * @return list<PerformanceVerdict>
     */
    private function verdicts(array $windows): array
    {
        return array_values(array_filter(array_map(
            fn (array $window): ?PerformanceVerdict => PerformanceVerdict::tryFrom((string) ($window['verdict'] ?? '')),
            $windows,
        )));
    }

    /**
     * @param  list<PerformanceVerdict>  $verdicts
     */
    private function contains(array $verdicts, PerformanceVerdict $needle): bool
    {
        foreach ($verdicts as $verdict) {
            if ($verdict === $needle) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  list<PerformanceVerdict>  $verdicts
     */
    private function count(array $verdicts, PerformanceVerdict $needle): int
    {
        return count(array_filter(
            $verdicts,
            fn (PerformanceVerdict $verdict): bool => $verdict === $needle,
        ));
    }

    /**
     * @param  array<string, array{verdict: string, reason: string}>  $windows
     * @return array{verdict: PerformanceVerdict, reason: string}
     */
    private function pick(array $windows, PerformanceVerdict $verdict, string $fallbackReason): array
    {
        foreach ($windows as $window) {
            if (($window['verdict'] ?? null) === $verdict->value) {
                return [
                    'verdict' => $verdict,
                    'reason' => (string) ($window['reason'] ?? $fallbackReason),
                ];
            }
        }

        return [
            'verdict' => $verdict,
            'reason' => $fallbackReason,
        ];
    }
}
