<?php

declare(strict_types=1);

namespace ImovelZapAi\Properties\Infrastructure\Http\Controllers\Web;

use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use ImovelZapAi\Properties\Application\Actions\CreatePropertyAction;
use ImovelZapAi\Properties\Application\Actions\DeletePropertyAction;
use ImovelZapAi\Properties\Application\Actions\UpdatePropertyAction;
use ImovelZapAi\Properties\Application\Actions\UpdatePropertyStatusAction;
use ImovelZapAi\Properties\Application\Data\PropertyData;
use ImovelZapAi\Properties\Application\Data\PropertyImagesInput;
use ImovelZapAi\Properties\Application\Data\PropertySearchFilters;
use ImovelZapAi\Properties\Application\Queries\SearchPropertiesQuery;
use ImovelZapAi\Properties\Application\Services\PropertyMapPresenter;
use ImovelZapAi\Properties\Domain\Enums\PropertyPurpose;
use ImovelZapAi\Properties\Domain\Enums\PropertyStatus;
use ImovelZapAi\Properties\Domain\Enums\PropertyType;
use ImovelZapAi\Properties\Infrastructure\Http\Requests\StorePropertyRequest;
use ImovelZapAi\Properties\Infrastructure\Http\Requests\UpdatePropertyRequest;
use ImovelZapAi\Properties\Infrastructure\Http\Requests\UpdatePropertyStatusRequest;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\CityModel;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\PropertyModel;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\StateModel;
use ImovelZapAi\Tenancy\Support\TenantContext;

final class PropertyController
{
    public function index(Request $request, SearchPropertiesQuery $search): View
    {
        $filters = PropertySearchFilters::fromRequest($request->query());

        $properties = $search->paginate($filters);

        return view('properties.index', [
            'properties' => $properties,
            'purposes' => PropertyPurpose::cases(),
            'statuses' => PropertyStatus::cases(),
            'filters' => $filters->toQueryArray(),
        ]);
    }

    public function create(): View
    {
        return view('properties.create', $this->formData());
    }

    public function store(StorePropertyRequest $request, CreatePropertyAction $action): RedirectResponse
    {
        $property = $action->execute(
            tenantId: (string) TenantContext::id(),
            creator: $request->user(),
            data: PropertyData::fromValidated($request->validated()),
            images: PropertyImagesInput::fromRequest($request->validated(), $request->file('images', [])),
        );

        return redirect()
            ->route('properties.show', $property)
            ->with('status', 'Imóvel cadastrado com sucesso.');
    }

    public function show(PropertyModel $property, PropertyMapPresenter $mapPresenter): View
    {
        $property->load(['images', 'neighborhood', 'city', 'state', 'creator']);

        return view('properties.show', [
            'property' => $property,
            'map' => $mapPresenter->present($property),
        ]);
    }

    public function edit(PropertyModel $property): View
    {
        $property->load(['images', 'neighborhood', 'city', 'state']);

        return view('properties.edit', array_merge(
            $this->formData($property),
            ['property' => $property],
        ));
    }

    public function update(UpdatePropertyRequest $request, PropertyModel $property, UpdatePropertyAction $action): RedirectResponse
    {
        $validated = $request->validated();

        $action->execute(
            property: $property,
            data: PropertyData::fromValidated($validated),
            images: PropertyImagesInput::fromRequest($validated, $request->file('images', [])),
        );

        return redirect()
            ->route('properties.show', $property)
            ->with('status', 'Imóvel atualizado com sucesso.');
    }

    public function updateStatus(
        UpdatePropertyStatusRequest $request,
        PropertyModel $property,
        UpdatePropertyStatusAction $action,
    ): JsonResponse {
        $property = $action->execute(
            $property,
            PropertyStatus::from((string) $request->validated('status')),
        );

        return response()->json([
            'status' => $property->status->value,
            'status_label' => $property->status->label(),
        ]);
    }

    public function destroy(PropertyModel $property, DeletePropertyAction $action): RedirectResponse|JsonResponse
    {
        $action->execute($property);

        if (request()->wantsJson()) {
            return response()->json(['message' => 'Imóvel removido com sucesso.']);
        }

        return redirect()
            ->route('properties.index')
            ->with('status', 'Imóvel removido com sucesso.');
    }

    /**
     * @return array<string, mixed>
     */
    private function formData(?PropertyModel $property = null): array
    {
        $states = StateModel::query()->orderBy('name')->get();
        /** @var \Illuminate\Support\Collection<string, \Illuminate\Support\Collection<int, CityModel>> $citiesByState */
        $citiesByState = CityModel::query()->orderBy('name')->get()->groupBy('state_id');

        return [
            'states' => $states,
            'citiesByState' => $citiesByState,
            'statesByCode' => $states->mapWithKeys(fn (StateModel $state) => [$state->code => $state->id]),
            'types' => PropertyType::cases(),
            'purposes' => PropertyPurpose::cases(),
            'statuses' => PropertyStatus::cases(),
            'selectedStateId' => old('state_id', $property?->state_id),
            'selectedCityId' => old('city_id', $property?->city_id),
            'selectedCityName' => old('city_id')
                ? CityModel::query()->find(old('city_id'))?->name
                : $property?->city?->name,
            'neighborhoodName' => old('neighborhood_name', $property?->neighborhood?->name),
        ];
    }
}
