<?php

namespace App\Reporting\StatsModels;

use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Enquiry;
use Mtc\MercuryDataModels\ReportingStat;

/**
 * Pre-aggregates enquiry UTM attribution into reporting_stats so reports/dashboard can read
 * cheap, indexed rows instead of parsing the enquiry `data` JSON on every request.
 *
 * One row per (dimension, bucket, day): key = "enquiry-utm:{dimension}:{bucket}".
 */
class EnquiryUtmBreakdown
{
    public const KEY_PREFIX = 'enquiry-utm';
    public const DIMENSIONS = ['source', 'medium', 'campaign', 'term', 'content'];
    public const UNCATEGORISED = 'uncategorised';

    private const MAX_KEY_LENGTH = 100;

    public function generate(Carbon $date): void
    {
        $enquiries = Enquiry::query()
            ->whereBetween('ingested_at', [$date->copy()->startOfDay(), $date->copy()->endOfDay()])
            ->get(['data']);

        foreach (self::DIMENSIONS as $dimension) {
            foreach ($this->tally($enquiries, $dimension) as $bucket => $count) {
                ReportingStat::query()->updateOrCreate(
                    [
                        'key' => $this->statKey($dimension, $bucket),
                        'date' => $date->format('Y-m-d'),
                    ],
                    ['value' => $count],
                );
            }
        }
    }

    public function statKey(string $dimension, string $bucket): string
    {
        $prefix = self::KEY_PREFIX . ':' . $dimension . ':';

        return $prefix . Str::limit($bucket, self::MAX_KEY_LENGTH - strlen($prefix), '');
    }

    private function tally(Collection $enquiries, string $dimension): array
    {
        $counts = [];

        foreach ($enquiries as $enquiry) {
            $bucket = $this->normaliseBucket(data_get($enquiry->data, "meta.utm_{$dimension}"));
            $counts[$bucket] = ($counts[$bucket] ?? 0) + 1;
        }

        return $counts;
    }

    private function normaliseBucket(mixed $value): string
    {
        $value = is_string($value) ? trim($value) : '';

        return $value === '' ? self::UNCATEGORISED : $value;
    }
}
