<?php

namespace App\Campaigns;

use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

class PerformanceWindowRollup
{
    /**
     * @param  Collection<int, PerformanceHour>  $hours
     * @return array{
     *     window: string,
     *     from: ?string,
     *     to: ?string,
     *     spend: float,
     *     impressions: int,
     *     clicks: int,
     *     cpc: ?float,
     *     ctr: ?float,
     *     conversions: int,
     *     conversion_value: float,
     *     buckets: int,
     *     expected_buckets: int,
     *     insufficient_data: bool
     * }
     */
    public static function fromHours(string $windowKey, int $length, Collection $hours): array
    {
        $to = now()->copy()->startOfHour();
        $from = $to->copy()->subHours($length - 1);
        $slice = $hours->filter(
            fn (PerformanceHour $row): bool => $row->stat_hour->gte($from)
                && $row->stat_hour->lte($to),
        );

        return self::totals($windowKey, $from, $to, $slice->count(), $length, $slice);
    }

    /**
     * @param  Collection<int, PerformanceDay>  $days
     * @return array{
     *     window: string,
     *     from: ?string,
     *     to: ?string,
     *     spend: float,
     *     impressions: int,
     *     clicks: int,
     *     cpc: ?float,
     *     ctr: ?float,
     *     conversions: int,
     *     conversion_value: float,
     *     buckets: int,
     *     expected_buckets: int,
     *     insufficient_data: bool
     * }
     */
    public static function fromDays(string $windowKey, int $length, Collection $days): array
    {
        $to = now()->copy()->startOfDay();
        $from = $to->copy()->subDays($length - 1);
        $slice = $days->filter(
            fn (PerformanceDay $row): bool => $row->stat_date->gte($from)
                && $row->stat_date->lte($to),
        );

        return self::totals($windowKey, $from, $to, $slice->count(), $length, $slice);
    }

    /**
     * @param  Collection<int, PerformanceHour|PerformanceDay>  $slice
     * @return array{
     *     window: string,
     *     from: ?string,
     *     to: ?string,
     *     spend: float,
     *     impressions: int,
     *     clicks: int,
     *     cpc: ?float,
     *     ctr: ?float,
     *     conversions: int,
     *     conversion_value: float,
     *     buckets: int,
     *     expected_buckets: int,
     *     insufficient_data: bool
     * }
     */
    private static function totals(
        string $windowKey,
        Carbon $from,
        Carbon $to,
        int $buckets,
        int $expectedBuckets,
        Collection $slice,
    ): array {
        $spend = round((float) $slice->sum('spend'), 2);
        $impressions = (int) $slice->sum('impressions');
        $clicks = (int) $slice->sum('clicks');
        $conversions = (int) $slice->sum(fn (PerformanceHour|PerformanceDay $row): int => (int) ($row->conversions ?? 0));
        $conversionValue = round((float) $slice->sum(fn (PerformanceHour|PerformanceDay $row): float => (float) ($row->conversion_value ?? 0)), 2);

        return [
            'window' => $windowKey,
            'from' => $from->toDateTimeString(),
            'to' => $to->toDateTimeString(),
            'spend' => $spend,
            'impressions' => $impressions,
            'clicks' => $clicks,
            'cpc' => $clicks > 0 ? round($spend / $clicks, 4) : null,
            'ctr' => $impressions > 0 ? round($clicks / $impressions, 4) : null,
            'conversions' => $conversions,
            'conversion_value' => $conversionValue,
            'buckets' => $buckets,
            'expected_buckets' => $expectedBuckets,
            'insufficient_data' => $buckets === 0,
        ];
    }
}
