<?php

namespace App\Http\Controllers;

use App\Models\Development;
use App\Models\Lot;
use App\Models\ProjectMedium;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
use Inertia\Response;

/** The public page of a published project: gallery, plan and available lots. */
class PortalProjectController extends Controller
{
    public function __invoke(string $slug): Response
    {
        $development = Development::query()
            ->where('slug', $slug)
            ->where('published', true)
            ->with(['media', 'lots'])
            ->firstOrFail();

        $available = $development->lots->where('status', 'available');
        $prices = $available->where('price', '>', 0)->pluck('price');

        return Inertia::render('portal/project', [
            'project' => [
                'name' => $development->name,
                'slug' => $development->slug,
                'location' => $development->location,
                'address' => $development->address,
                'latitude' => $development->latitude,
                'longitude' => $development->longitude,
                'mapUrl' => $development->latitude !== null && $development->longitude !== null
                    ? sprintf(
                        'https://www.google.com/maps/search/?api=1&query=%s,%s',
                        $development->latitude,
                        $development->longitude,
                    )
                    : null,
                'headline' => $development->headline ?? 'Lotes con inventario verificado desde el plano.',
                'totalLots' => $development->lots->count(),
                'availableLots' => $available->count(),
                'soldLots' => $development->lots->where('status', 'sold')->count(),
                'heldLots' => $development->lots->where('status', 'held')->count(),
                'blockCount' => $development->lots->pluck('block')->filter()->unique()->count(),
                'startingPrice' => (float) ($prices->min() ?? 0),
                'smallestArea' => (float) ($available->min('area') ?? 0),
                'largestArea' => (float) ($available->max('area') ?? 0),
                'planUrl' => $development->source_image_path
                    ? Storage::disk('public')->url($development->source_image_path)
                    : null,
                'explorerUrl' => route('portal.explorer', ['project' => $development->slug]),
                'whatsapp' => $development->whatsappNumber(),
                'downPaymentPercent' => $development->down_payment_percent,
                'financingMonths' => $development->financing_months,
                'averagePricePerMeter' => $this->averagePricePerMeter($available),
                'soldShare' => $development->lots->count() > 0
                    ? (int) round($development->lots->where('status', 'sold')->count() / $development->lots->count() * 100)
                    : 0,
            ],
            'media' => $development->media
                ->sortByDesc('is_cover')
                ->map(fn (ProjectMedium $medium): array => $medium->toPayload())
                ->values(),
            'blocks' => $this->blocks($development),
            'lots' => $available
                ->sortBy([['block', 'asc'], ['lot_number', 'asc']])
                ->map(fn (Lot $lot): array => [
                    'id' => $lot->id,
                    'block' => $lot->block,
                    'lotNumber' => $lot->lot_number,
                    'area' => (float) $lot->area,
                    'price' => (float) $lot->price,
                    'dimensions' => array_map(
                        static fn (array $measurement): string => (string) ($measurement['text'] ?? ''),
                        $lot->measurements ?? [],
                    ),
                ])->values(),
        ]);
    }

    /** @param Collection<int, Lot> $lots */
    private function averagePricePerMeter(mixed $lots): ?float
    {
        $priced = $lots->filter(fn (Lot $lot): bool => $lot->price > 0 && $lot->area > 0);

        if ($priced->isEmpty()) {
            return null;
        }

        return round((float) $priced->sum('price') / (float) $priced->sum('area'), 2);
    }

    /** @return list<array<string, mixed>> */
    private function blocks(Development $development): array
    {
        $grouped = $development->lots
            ->groupBy(fn (Lot $lot): string => $lot->block ?? 'Sin manzana')
            ->sortKeys();
        $blocks = [];

        foreach ($grouped as $name => $lots) {
            $blocks[] = [
                'name' => (string) $name,
                'lots' => $lots->count(),
                'available' => $lots->where('status', 'available')->count(),
            ];
        }

        return $blocks;
    }
}
