<?php

use App\Models\BrentPrice;
use App\Models\LlmOverlay;
use App\Models\VolatilityRegime;
use App\Models\WatchedEvent;
use App\Services\ApiLogger;
use App\Services\Forecasting\LlmOverlayService;
use App\Services\Forecasting\VolatilityRegimeService;
use App\Services\Forecasting\WeeklyForecastService;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;

uses(RefreshDatabase::class);

function makeVolatilityService(): VolatilityRegimeService
{
    Http::preventStrayRequests();
    Config::set('services.anthropic.api_key', null); // makes LLM run a no-op

    return new VolatilityRegimeService(
        new LlmOverlayService(new ApiLogger, app(WeeklyForecastService::class)),
    );
}

it('does nothing when there are no triggers', function (): void {
    $service = makeVolatilityService();

    $result = $service->evaluate();

    expect($result)->toBeNull()
        ->and(VolatilityRegime::query()->count())->toBe(0);
});

it('flips ON when Brent moves more than 3% close-to-close', function (): void {
    BrentPrice::query()->create(['date' => '2026-04-26', 'price_usd' => 80.00]);
    BrentPrice::query()->create(['date' => '2026-04-27', 'price_usd' => 84.00]); // +5%

    $service = makeVolatilityService();

    $row = $service->evaluate();

    expect($row)->not->toBeNull()
        ->and($row->trigger)->toBe('brent_move')
        ->and($row->active)->toBeTrue();
});

it('does NOT flip on a 2% Brent move (below threshold)', function (): void {
    BrentPrice::query()->create(['date' => '2026-04-26', 'price_usd' => 80.00]);
    BrentPrice::query()->create(['date' => '2026-04-27', 'price_usd' => 81.50]); // +1.875%

    $service = makeVolatilityService();

    expect($service->evaluate())->toBeNull();
});

it('flips ON when the most recent llm_overlay flags a major impact event', function (): void {
    LlmOverlay::query()->create([
        'ran_at' => now(),
        'forecast_for_week' => Carbon::now()->next(Carbon::MONDAY)->toDateString(),
        'direction' => 'rising',
        'confidence' => 60,
        'reasoning' => 'OPEC unexpected cut.',
        'events_json' => [['headline' => 'OPEC cut', 'source' => 'Reuters', 'url' => 'https://reuters.com/x', 'impact' => 'rising']],
        'agrees_with_ridge' => true,
        'major_impact_event' => true,
        'volatility_flag_on' => false,
        'search_used' => true,
    ]);

    $service = makeVolatilityService();

    $row = $service->evaluate();

    expect($row)->not->toBeNull()
        ->and($row->trigger)->toBe('llm_event');
});

it('does NOT flip on llm_overlay when no URL is verified', function (): void {
    LlmOverlay::query()->create([
        'ran_at' => now(),
        'forecast_for_week' => Carbon::now()->next(Carbon::MONDAY)->toDateString(),
        'direction' => 'rising',
        'confidence' => 60,
        'reasoning' => '...',
        'events_json' => [['headline' => 'OPEC cut', 'source' => 'Reuters', 'url' => '', 'impact' => 'rising']],
        'agrees_with_ridge' => true,
        'major_impact_event' => true,
        'volatility_flag_on' => false,
        'search_used' => true,
    ]);

    $service = makeVolatilityService();

    expect($service->evaluate())->toBeNull();
});

it('flips ON when a watched_event covers today', function (): void {
    WatchedEvent::query()->create([
        'label' => 'Iran tensions',
        'starts_at' => Carbon::now()->subDay(),
        'ends_at' => Carbon::now()->addWeek(),
        'notes' => 'manually flagged',
    ]);

    $service = makeVolatilityService();

    $row = $service->evaluate();

    expect($row)->not->toBeNull()
        ->and($row->trigger)->toBe('manual')
        ->and($row->trigger_detail)->toContain('Iran tensions');
});

it('flips OFF when no triggers fire while a regime is active', function (): void {
    $existing = VolatilityRegime::query()->create([
        'flipped_on_at' => now()->subDay(),
        'flipped_off_at' => null,
        'trigger' => 'brent_move',
        'trigger_detail' => 'Brent +4.2%',
        'active' => true,
    ]);

    $service = makeVolatilityService();

    $result = $service->evaluate();

    expect($result)->toBeNull();
    $existing->refresh();
    expect($existing->active)->toBeFalse()
        ->and($existing->flipped_off_at)->not->toBeNull();
});

it('keeps the existing regime when a trigger still fires', function (): void {
    BrentPrice::query()->create(['date' => '2026-04-26', 'price_usd' => 80.00]);
    BrentPrice::query()->create(['date' => '2026-04-27', 'price_usd' => 84.00]);

    $existing = VolatilityRegime::query()->create([
        'flipped_on_at' => now()->subHour(),
        'flipped_off_at' => null,
        'trigger' => 'brent_move',
        'trigger_detail' => 'Brent +5%',
        'active' => true,
    ]);

    $service = makeVolatilityService();

    $result = $service->evaluate();

    expect($result?->id)->toBe($existing->id)
        ->and(VolatilityRegime::query()->count())->toBe(1);
});

it('skips station_churn trigger when feature flag is off (default)', function (): void {
    Config::set('services.forecasting.station_churn_enabled', false);
    $service = makeVolatilityService();

    expect($service->evaluate())->toBeNull();
});
