<?php

namespace App\Services\Prediction\Signals;

use Illuminate\Support\Facades\DB;

final class DayOfWeekSignal extends AbstractSignal
{
    private const int MIN_DAYS = 21;

    public function compute(SignalContext $context): array
    {
        $isSqlite = DB::connection()->getDriverName() === 'sqlite';
        $dowExpr = $isSqlite
            ? "(CAST(strftime('%w', price_effective_at) AS INTEGER) + 1)"
            : 'DAYOFWEEK(price_effective_at)';

        $rows = DB::table('station_prices')
            ->where('fuel_type', $context->fuelType->value)
            ->where('price_effective_at', '>=', now()->subDays(90))
            ->selectRaw("{$dowExpr} as dow, DATE(price_effective_at) as day, AVG(price_pence) as avg_price")
            ->groupBy('dow', 'day')
            ->get();

        $uniqueDays = $rows->pluck('day')->unique()->count();

        if ($uniqueDays < self::MIN_DAYS) {
            return $this->disabledSignal("Insufficient history for day-of-week pattern ({$uniqueDays} days, need ".self::MIN_DAYS.')');
        }

        $dowAverages = $rows->groupBy('dow')->map(fn ($g) => $g->avg('avg_price'));
        $weekAvg = $dowAverages->avg();
        $todayDow = (int) now()->format('w') + 1; // PHP 0=Sun → MySQL 1=Sun
        $todayAvg = $dowAverages->get($todayDow, $weekAvg);
        $cheapestDow = $dowAverages->keys()->sortBy(fn ($k) => $dowAverages[$k])->first();
        $dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
        $todayName = $dayNames[($todayDow - 1) % 7] ?? 'Today';
        $tomorrowName = $dayNames[$todayDow % 7] ?? 'Tomorrow';

        $todayDeltaPence = round(($todayAvg - $weekAvg) / 100, 1);
        $tomorrowDeltaPence = round(($dowAverages->get(($todayDow % 7) + 1, $weekAvg) - $todayAvg) / 100, 1);

        $direction = match (true) {
            ($todayAvg - $weekAvg) / 100 >= 1.5 => 'up',
            ($weekAvg - $todayAvg) / 100 >= 1.5 => 'down',
            default => 'stable',
        };

        $score = $direction === 'stable' ? 0.0 : ($direction === 'up' ? 1.0 : -1.0);

        $parts = [];
        $parts[] = abs($todayDeltaPence) < 0.1
            ? "Today ({$todayName}) is typically in line with the weekly average."
            : sprintf(
                'Today (%s) is typically %sp %s the weekly average.',
                $todayName,
                number_format(abs($todayDeltaPence), 1),
                $todayDeltaPence > 0 ? 'above' : 'below',
            );

        $parts[] = abs($tomorrowDeltaPence) < 0.1
            ? "Tomorrow ({$tomorrowName}) is typically the same."
            : sprintf(
                'Tomorrow (%s) is typically %sp %s.',
                $tomorrowName,
                number_format(abs($tomorrowDeltaPence), 1),
                $tomorrowDeltaPence < 0 ? 'cheaper' : 'pricier',
            );

        if ($cheapestDow === $todayDow) {
            $parts[] = 'Today is historically the cheapest day of the week.';
        }

        return [
            'score' => $score,
            'confidence' => min(1.0, $uniqueDays / 90),
            'direction' => $direction,
            'detail' => implode(' ', $parts),
            'data_points' => $uniqueDays,
            'enabled' => true,
        ];
    }
}
