<?php

declare(strict_types=1);

namespace ImovelZapAi\Analytics\Application\Queries;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use ImovelZapAi\Analytics\Domain\Enums\MetricKey;
use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsDailyMetricModel;

final class GetRankingQuery
{
    /**
     * @param  array{from?: string, to?: string, days?: int, limit?: int}  $filters
     * @return array<string, mixed>
     */
    public function execute(string $tenantId, array $filters = []): array
    {
        [$from, $to] = $this->period($filters);
        $limit = (int) ($filters['limit'] ?? 5);

        return [
            'period' => ['from' => $from->toDateString(), 'to' => $to->toDateString()],
            'users_deals_won' => $this->topByDimension($tenantId, $from, $to, MetricKey::DealsWon, 'user:', $limit),
            'users_visits_completed' => $this->topByDimension($tenantId, $from, $to, MetricKey::VisitsCompleted, 'user:', $limit),
            'channels' => $this->topByDimension($tenantId, $from, $to, MetricKey::PeopleOpportunitiesCreated, 'channel:', $limit),
        ];
    }

    /**
     * @return list<array{key: string, label: string, value: float}>
     */
    private function topByDimension(
        string $tenantId,
        Carbon $from,
        Carbon $to,
        MetricKey $metricKey,
        string $prefix,
        int $limit,
    ): array {
        $rows = AnalyticsDailyMetricModel::query()
            ->select('dimension_key', DB::raw('SUM(value) as total'))
            ->where('tenant_id', $tenantId)
            ->where('metric_key', $metricKey->value)
            ->whereNotNull('dimension_key')
            ->where('dimension_key', 'like', $prefix.'%')
            ->whereBetween('metric_date', [$from->toDateString(), $to->toDateString()])
            ->groupBy('dimension_key')
            ->orderByDesc('total')
            ->limit($limit)
            ->get();

        return $rows->map(static function ($row) use ($prefix): array {
            $key = (string) $row->dimension_key;
            $label = str_starts_with($key, 'user:')
                ? 'Corretor #'.substr($key, 5)
                : ucfirst(substr($key, 8));

            return [
                'key' => $key,
                'label' => $label,
                'value' => (float) $row->total,
            ];
        })->all();
    }

    /** @param array{from?: string, to?: string, days?: int} $filters */
    /** @return array{0: Carbon, 1: Carbon} */
    private function period(array $filters): array
    {
        $to = isset($filters['to']) ? Carbon::parse($filters['to'])->endOfDay() : now();
        $from = isset($filters['from'])
            ? Carbon::parse($filters['from'])->startOfDay()
            : $to->copy()->subDays((int) ($filters['days'] ?? 30))->startOfDay();

        return [$from, $to];
    }
}
