<?php

namespace App\Campaigns;

use App\Models\CampaignPerformanceStat;
use Illuminate\Support\Collection;

class PerformanceTrend
{
    /**
     * Last 7 days are the decision window. Prior 7 days and lifetime are
     * context so we can see whether performance is rising or falling.
     *
     * @param  Collection<int, CampaignPerformanceStat>  $days
     * @return array{
     *     last_7_days: array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float},
     *     previous_7_days: array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float},
     *     lifetime: array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float, days: int},
     *     trend: string,
     *     trend_summary: string,
     *     trend_cpc_change_pct: ?float,
     *     trend_clicks_change_pct: ?float
     * }
     */
    public static function fromDays(Collection $days): array
    {
        $sorted = $days
            ->filter(fn (CampaignPerformanceStat $row): bool => $row->stat_date !== null)
            ->sortBy(fn (CampaignPerformanceStat $row): int => $row->stat_date?->timestamp ?? 0)
            ->values();

        $last7Since = now()->subDays(6)->startOfDay();
        $prev7Since = now()->subDays(13)->startOfDay();
        $prev7Until = now()->subDays(7)->endOfDay();

        $last7 = $sorted->filter(
            fn (CampaignPerformanceStat $row): bool => $row->stat_date->gte($last7Since),
        );
        $previous7 = $sorted->filter(
            fn (CampaignPerformanceStat $row): bool => $row->stat_date->gte($prev7Since)
                && $row->stat_date->lte($prev7Until),
        );

        $last = self::totals($last7);
        $previous = self::totals($previous7);
        $lifetime = self::totals($sorted);
        $lifetime['days'] = $sorted->count();

        [$trend, $summary, $cpcChange, $clicksChange] = self::compare($last, $previous);

        return [
            'last_7_days' => $last,
            'previous_7_days' => $previous,
            'lifetime' => $lifetime,
            'trend' => $trend,
            'trend_summary' => $summary,
            'trend_cpc_change_pct' => $cpcChange,
            'trend_clicks_change_pct' => $clicksChange,
        ];
    }

    /**
     * @param  Collection<int, CampaignPerformanceStat>  $days
     * @return array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float}
     */
    private static function totals(Collection $days): array
    {
        $spend = round((float) $days->sum('spend'), 2);
        $impressions = (int) $days->sum('impressions');
        $clicks = (int) $days->sum('clicks');

        return [
            'spend' => $spend,
            'impressions' => $impressions,
            'clicks' => $clicks,
            'cpc' => $clicks > 0 ? round($spend / $clicks, 4) : null,
            'ctr' => $impressions > 0 ? round($clicks / $impressions, 4) : null,
        ];
    }

    /**
     * @param  array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float}  $last
     * @param  array{spend: float, impressions: int, clicks: int, cpc: ?float, ctr: ?float}  $previous
     * @return array{0: string, 1: string, 2: ?float, 3: ?float}
     */
    private static function compare(array $last, array $previous): array
    {
        $cpcChange = self::percentChange($previous['cpc'], $last['cpc']);
        $clicksChange = $previous['clicks'] > 0
            ? self::percentChange((float) $previous['clicks'], (float) $last['clicks'])
            : null;

        if ($previous['spend'] < 0.01 && $previous['clicks'] === 0) {
            return ['unknown', 'Not enough prior-week data to compare.', $cpcChange, $clicksChange];
        }

        if ($cpcChange !== null && abs($cpcChange) >= 10) {
            if ($cpcChange <= -10) {
                return ['improving', 'CPC improved '.self::pctLabel(abs($cpcChange)).' vs prior 7 days.', $cpcChange, $clicksChange];
            }

            return ['declining', 'CPC worsened '.self::pctLabel($cpcChange).' vs prior 7 days.', $cpcChange, $clicksChange];
        }

        if ($clicksChange !== null && abs($clicksChange) >= 10) {
            if ($clicksChange >= 10) {
                return ['improving', 'Clicks up '.self::pctLabel($clicksChange).' vs prior 7 days.', $cpcChange, $clicksChange];
            }

            return ['declining', 'Clicks down '.self::pctLabel(abs($clicksChange)).' vs prior 7 days.', $cpcChange, $clicksChange];
        }

        return ['flat', 'Last 7 days are in line with the prior 7 days.', $cpcChange, $clicksChange];
    }

    private static function percentChange(?float $from, ?float $to): ?float
    {
        if ($from === null || $to === null || abs($from) < 0.0001) {
            return null;
        }

        return round((($to - $from) / $from) * 100, 1);
    }

    private static function pctLabel(float $value): string
    {
        return rtrim(rtrim(number_format($value, 1, '.', ''), '0'), '.').'%';
    }
}
