<?php

namespace App\Serp\Operations;

use App\Serp\Exceptions\ProviderAuthenticationException;
use App\Serp\Exceptions\ProviderConfigurationException;
use App\Serp\Exceptions\SerpProviderException;
use Illuminate\Support\Facades\Cache;

final class CircuitBreaker
{
    public function __construct(private KillSwitch $kill, private OperationAuditLogger $audit) {}

    public function assertClosed(): void
    {
        try {
            if (Cache::get('serp:circuit:open')) {
                throw new OperationBlockedException('circuit_open');
            }
        } catch (OperationBlockedException $e) {
            throw $e;
        } catch (\Throwable) {
            throw new OperationBlockedException('circuit_cache_unavailable');
        }
    }

    public function success(): void
    {
        Cache::forget('serp:circuit:failures');
        Cache::forget('serp:circuit:open');
    }

    public function failure(SerpProviderException $e): void
    {
        if ($e instanceof ProviderAuthenticationException || $e instanceof ProviderConfigurationException) {
            Cache::put('serp:circuit:open', true, now()->addMinutes((int) config('serp.circuit_cooldown_minutes')));
            $this->kill->activate('provider_auth_configuration');
            $this->audit->record('circuit_opened', ['error_category' => $e->safeCode()]);

            return;
        }$n = (int) Cache::increment('serp:circuit:failures');
        Cache::put('serp:circuit:failures', $n, now()->addMinutes((int) config('serp.circuit_window_minutes')));
        if ($n >= (int) config('serp.circuit_failure_threshold')) {
            Cache::put('serp:circuit:open', true, now()->addMinutes((int) config('serp.circuit_cooldown_minutes')));
            $this->audit->record('circuit_opened', ['error_category' => $e->safeCode()]);
        }
    }

    public function open(): bool
    {
        return (bool) Cache::get('serp:circuit:open');
    }
}
