<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\Signal;
use App\ValueObject\Point;

class SignalSnapshotBuilder
{
    /**
     * @param list<Signal> $signals
     *
     * @return array{
     *     points: list<array{
     *         id: string,
     *         signalLocation: Point,
     *         createdAt: string,
     *         userKey: string,
     *     }>,
     *     density: list<array{lat: float, lng: float, intensity: int}>,
     *     latestByUser: list<array{
     *         id: string,
     *         signalLocation: Point,
     *         createdAt: string,
     *         userKey: string,
     *     }>,
     *     totals: array{points: int, contributors: int}
     * }
     */
    public function build(array $signals): array
    {
        $points = [];
        $densityBuckets = [];
        $latestByUser = [];

        foreach ($signals as $signal) {
            $point = [
                'id' => $signal->id->toString(),
                'signalLocation' => [
                    'lat' => $signal->signalLocation->getLat(),
                    'lng' => $signal->signalLocation->getLng(),
                ],
                'createdAt' => $signal->createdAt->format(DATE_ATOM),
                'userKey' => $signal->userKey,
            ];

            $points[] = $point;

            $bucketLat = round($signal->signalLocation->getLat(), 3);
            $bucketLng = round($signal->signalLocation->getLng(), 3);
            $bucketKey = $bucketLat . ':' . $bucketLng;

            if (! isset($densityBuckets[$bucketKey])) {
                $densityBuckets[$bucketKey] = [
                    'lat' => $bucketLat,
                    'lng' => $bucketLng,
                    'intensity' => 0,
                    'latestPoint' => $point,
                ];
            }

            $densityBuckets[$bucketKey]['intensity']++;
            if ($point['createdAt'] > $densityBuckets[$bucketKey]['latestPoint']['createdAt']) {
                $densityBuckets[$bucketKey]['latestPoint'] = $point;
            }

            $existingLatest = $latestByUser[$point['userKey']] ?? null;
            if ($existingLatest === null || $point['createdAt'] > $existingLatest['createdAt']) {
                $latestByUser[$point['userKey']] = $point;
            }
        }

        usort($points, static fn (array $a, array $b): int => strcmp($b['createdAt'], $a['createdAt']));

        $density = array_values(array_map(
            static fn (array $bucket): array => [
                'lat' => $bucket['lat'],
                'lng' => $bucket['lng'],
                'intensity' => $bucket['intensity'],
            ],
            $densityBuckets,
        ));

        usort($density, static fn (array $a, array $b): int => $b['intensity'] <=> $a['intensity']);

        $latest = array_values($latestByUser);
        usort($latest, static fn (array $a, array $b): int => strcmp($b['createdAt'], $a['createdAt']));

        return [
            'points' => $points,
            'density' => $density,
            'latestByUser' => $latest,
            'totals' => [
                'points' => count($points),
                'contributors' => count($latestByUser),
            ],
        ];
    }
}
