<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\User;
use App\Support\DemoPropertyPlaceholderImageFactory;
use Database\Seeders\LocationCatalogSeeder;
use Database\Seeders\PlatformDemoSeeder;
use Illuminate\Console\Command;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use ImovelZapAi\Properties\Application\Actions\CreatePropertyAction;
use ImovelZapAi\Properties\Application\Actions\SyncPropertyImagesAction;
use ImovelZapAi\Properties\Application\Data\PropertyData;
use ImovelZapAi\Properties\Application\Data\PropertyImagesInput;
use ImovelZapAi\Properties\Domain\Enums\PropertyPurpose;
use ImovelZapAi\Properties\Domain\Enums\PropertyStatus;
use ImovelZapAi\Properties\Domain\Enums\PropertyType;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\CityModel;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\PropertyImageModel;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\PropertyModel;
use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\StateModel;
use ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel;
use ImovelZapAi\Tenancy\Support\TenantContext;
use Throwable;

final class SeedDemoPropertiesCommand extends Command
{
    protected $signature = 'demo:seed-properties
                            {--tenant=demo : Slug do tenant (padrão: demo)}
                            {--fresh : Remove imóveis DEMO-JP-* antes de recriar}
                            {--refresh-images : Regenera fotos dos imóveis DEMO-JP-* existentes}';

    protected $description = 'Cadastra 5 imóveis demo completos em João Pessoa/PB (venda e aluguel, 3 fotos cada)';

    public function handle(CreatePropertyAction $createPropertyAction): int
    {
        $tenantSlug = (string) $this->option('tenant');

        $this->components->info('ImóvelZapAi — cadastro de imóveis demo (João Pessoa/PB)');

        try {
            $this->ensureLocationCatalog();
            $this->ensureStorageLink();
            $tenant = $this->resolveTenant($tenantSlug);
            $creator = $this->resolveCreator($tenant);
            TenantContext::set((string) $tenant->id);

            if ((bool) $this->option('fresh')) {
                $removed = $this->removeExistingDemoProperties((string) $tenant->id);
                $this->components->twoColumnDetail('Imóveis demo removidos', (string) $removed);
            }

            [$state, $city] = $this->resolveJoaoPessoa();

            if ((bool) $this->option('refresh-images')) {
                $refreshed = $this->refreshDemoPropertyImages((string) $tenant->id, $state, $city);

                $this->components->info("Fotos regeneradas em {$refreshed} imóvel(is) demo.");

                if (! (bool) $this->option('fresh')) {
                    return self::SUCCESS;
                }
            }

            $created = 0;
            $skipped = 0;
            $rows = [];

            foreach ($this->propertyDefinitions($state, $city) as $definition) {
                $internalCode = $definition['internal_code'];

                if (PropertyModel::query()
                    ->where('tenant_id', $tenant->id)
                    ->where('internal_code', $internalCode)
                    ->exists()) {
                    $skipped++;
                    $rows[] = [$internalCode, $definition['title'], 'já existia'];

                    continue;
                }

                $property = $createPropertyAction->execute(
                    tenantId: (string) $tenant->id,
                    creator: $creator,
                    data: $definition['data'],
                    images: $this->buildImagesInput($definition['image_captions']),
                );

                $created++;
                $rows[] = [
                    $property->internal_code,
                    $property->title,
                    $property->purpose->label().' · '.$property->neighborhood?->name,
                ];
            }

            $this->newLine();
            $this->table(['Código', 'Título', 'Status'], $rows);
            $this->newLine();
            $this->components->info("Concluído: {$created} criado(s), {$skipped} ignorado(s).");
            $this->line('  Painel: '.config('app.url').'/properties');
            $this->line('  WhatsApp: teste com "quero alugar apartamento em Tambaú" ou "apartamento à venda no Cabo Branco".');

            return self::SUCCESS;
        } catch (Throwable $exception) {
            $this->components->error($exception->getMessage());

            return self::FAILURE;
        } finally {
            TenantContext::clear();
        }
    }

    private function ensureLocationCatalog(): void
    {
        if (StateModel::query()->where('code', 'PB')->exists()) {
            return;
        }

        $this->components->warn('Catálogo de localidades ausente. Executando LocationCatalogSeeder...');
        $this->call('db:seed', ['--class' => LocationCatalogSeeder::class, '--force' => true]);
    }

    private function ensureStorageLink(): void
    {
        $publicStorage = public_path('storage');

        if (is_link($publicStorage) || is_dir($publicStorage)) {
            return;
        }

        $this->components->warn('Link storage/public ausente. Executando storage:link...');
        Artisan::call('storage:link');
    }

    private function resolveTenant(string $slug): TenantModel
    {
        $tenant = TenantModel::query()->where('slug', $slug)->first();

        if ($tenant !== null) {
            return $tenant;
        }

        $this->components->warn("Tenant [{$slug}] não encontrado. Executando PlatformDemoSeeder...");
        $this->call('db:seed', ['--class' => PlatformDemoSeeder::class, '--force' => true]);

        return TenantModel::query()->where('slug', $slug)->firstOrFail();
    }

    private function resolveCreator(TenantModel $tenant): User
    {
        $creator = User::query()
            ->where('tenant_id', $tenant->id)
            ->where('email', 'corretor@demo.test')
            ->first();

        if ($creator !== null) {
            return $creator;
        }

        $creator = User::query()
            ->where('tenant_id', $tenant->id)
            ->orderBy('created_at')
            ->first();

        if ($creator === null) {
            throw new \RuntimeException(
                'Nenhum usuário encontrado no tenant. Rode: php artisan db:seed --class=PlatformDemoSeeder'
            );
        }

        return $creator;
    }

    /** @return array{0: StateModel, 1: CityModel} */
    private function resolveJoaoPessoa(): array
    {
        $state = StateModel::query()->where('code', 'PB')->firstOrFail();
        $city = CityModel::query()
            ->where('state_id', $state->id)
            ->where('name', 'João Pessoa')
            ->firstOrFail();

        return [$state, $city];
    }

    private function removeExistingDemoProperties(string $tenantId): int
    {
        $properties = PropertyModel::query()
            ->withTrashed()
            ->where('tenant_id', $tenantId)
            ->where('internal_code', 'like', 'DEMO-JP-%')
            ->get();

        foreach ($properties as $property) {
            /** @var PropertyModel $property */
            PropertyImageModel::query()
                ->withTrashed()
                ->where('property_id', $property->id)
                ->each(function (PropertyImageModel $image): void {
                    if ($image->path !== '') {
                        Storage::disk('public')->delete($image->path);
                    }
                    $image->forceDelete();
                });

            Storage::disk('public')->deleteDirectory('properties/'.$property->id);
            $property->forceDelete();
        }

        return $properties->count();
    }

    private function refreshDemoPropertyImages(string $tenantId, StateModel $state, CityModel $city): int
    {
        $definitions = collect($this->propertyDefinitions($state, $city))->keyBy('internal_code');
        $properties = PropertyModel::query()
            ->where('tenant_id', $tenantId)
            ->where('internal_code', 'like', 'DEMO-JP-%')
            ->get();

        /** @var SyncPropertyImagesAction $syncImages */
        $syncImages = app(SyncPropertyImagesAction::class);
        $refreshed = 0;

        foreach ($properties as $property) {
            /** @var PropertyModel $property */
            PropertyImageModel::query()
                ->where('property_id', $property->id)
                ->each(function (PropertyImageModel $image): void {
                    if ($image->path !== '') {
                        Storage::disk('public')->delete($image->path);
                    }
                    $image->forceDelete();
                });

            /** @var array{image_captions: list<string>}|null $definition */
            $definition = $definitions->get($property->internal_code);
            $captions = $definition['image_captions'] ?? ['Fachada', 'Sala de estar', 'Quarto principal'];

            $syncImages->sync($property, $this->buildImagesInput($captions));
            $refreshed++;
        }

        return $refreshed;
    }

    /**
     * @param  list<string>  $captions
     */
    private function buildImagesInput(array $captions): PropertyImagesInput
    {
        $files = [];

        foreach ($captions as $index => $caption) {
            $files[] = $this->makePlaceholderUpload($index + 1, $caption);
        }

        return new PropertyImagesInput(
            newFiles: $files,
            newCaptions: $captions,
            newOrders: [1, 2, 3],
            newCoverIndex: 0,
        );
    }

    private function makePlaceholderUpload(int $index, string $caption): UploadedFile
    {
        $path = DemoPropertyPlaceholderImageFactory::createTempFile($index, $caption);

        return new UploadedFile(
            $path,
            "demo-foto-{$index}.jpg",
            'image/jpeg',
            null,
            true,
        );
    }

    /**
     * @return list<array{internal_code: string, title: string, data: PropertyData, image_captions: list<string>}>
     */
    private function propertyDefinitions(StateModel $state, CityModel $city): array
    {
        $stateId = (string) $state->id;
        $cityId = (string) $city->id;

        return [
            $this->definition(
                internalCode: 'DEMO-JP-001',
                title: 'Apartamento 3 quartos com vista mar — Tambaú',
                type: PropertyType::Apartment,
                purpose: PropertyPurpose::Sale,
                price: '685000.00',
                neighborhood: 'Tambaú',
                street: 'Av. Almirante Tamandaré',
                number: '320',
                complement: 'Apto 801',
                cep: '58039-021',
                latitude: '-7.1156',
                longitude: '-34.8340',
                bedrooms: 3,
                suites: 1,
                bathrooms: 2,
                parkingSpaces: 2,
                usefulArea: '92.00',
                totalArea: '110.00',
                condominiumFee: '980.00',
                iptu: '420.00',
                shortDescription: 'Apartamento frente mar no Tambaú, 3 quartos e 2 vagas.',
                fullDescription: 'Excelente apartamento no bairro Tambaú, a poucos metros da orla. '
                    .'Sala ampla, cozinha planejada, área de serviço, 3 quartos sendo 1 suíte, '
                    .'2 banheiros sociais e 2 vagas de garagem. Condomínio com piscina e salão de festas.',
                stateId: $stateId,
                cityId: $cityId,
            ),
            $this->definition(
                internalCode: 'DEMO-JP-002',
                title: 'Casa ampla com quintal — Bessa',
                type: PropertyType::House,
                purpose: PropertyPurpose::Rent,
                price: '3500.00',
                neighborhood: 'Bessa',
                street: 'Rua Empresário João Rodrigues',
                number: '450',
                complement: null,
                cep: '58038-000',
                latitude: '-7.0661',
                longitude: '-34.8443',
                bedrooms: 4,
                suites: 1,
                bathrooms: 3,
                parkingSpaces: 3,
                usefulArea: '180.00',
                totalArea: '240.00',
                condominiumFee: null,
                iptu: '280.00',
                shortDescription: 'Casa para locação no Bessa, 4 quartos e quintal.',
                fullDescription: 'Casa térrea no Bessa com sala, copa, cozinha, 4 quartos (1 suíte), '
                    .'3 banheiros, área gourmet e quintal. Garagem para 3 carros. Ideal para família.',
                stateId: $stateId,
                cityId: $cityId,
            ),
            $this->definition(
                internalCode: 'DEMO-JP-003',
                title: 'Apartamento mobiliado — Manaíra',
                type: PropertyType::Apartment,
                purpose: PropertyPurpose::Rent,
                price: '4200.00',
                neighborhood: 'Manaíra',
                street: 'Rua Juvenal Morais',
                number: '180',
                complement: 'Apto 502',
                cep: '58038-100',
                latitude: '-7.1047',
                longitude: '-34.8455',
                bedrooms: 2,
                suites: 1,
                bathrooms: 2,
                parkingSpaces: 1,
                usefulArea: '68.00',
                totalArea: '78.00',
                condominiumFee: '720.00',
                iptu: '190.00',
                shortDescription: 'Apartamento mobiliado para aluguel na Manaíra.',
                fullDescription: 'Apartamento mobiliado próximo ao Shopping Manaíra. '
                    .'2 quartos sendo 1 suíte, sala integrada, varanda e 1 vaga. '
                    .'Pronto para morar — excelente para executivos.',
                stateId: $stateId,
                cityId: $cityId,
            ),
            $this->definition(
                internalCode: 'DEMO-JP-004',
                title: 'Cobertura duplex — Cabo Branco',
                type: PropertyType::Penthouse,
                purpose: PropertyPurpose::Sale,
                price: '1280000.00',
                neighborhood: 'Cabo Branco',
                street: 'Av. Cabo Branco',
                number: '900',
                complement: 'Cobertura 1201',
                cep: '58045-000',
                latitude: '-7.1319',
                longitude: '-34.7983',
                bedrooms: 4,
                suites: 2,
                bathrooms: 4,
                parkingSpaces: 3,
                usefulArea: '210.00',
                totalArea: '280.00',
                condominiumFee: '1850.00',
                iptu: '890.00',
                shortDescription: 'Cobertura duplex de alto padrão no Cabo Branco.',
                fullDescription: 'Cobertura duplex com vista panorâmica para o mar. '
                    .'4 quartos (2 suítes), sala dupla, lavabo, terraço com piscina privativa, '
                    .'churrasqueira e 3 vagas. Acabamento premium.',
                stateId: $stateId,
                cityId: $cityId,
            ),
            $this->definition(
                internalCode: 'DEMO-JP-005',
                title: 'Studio compacto — Varadouro',
                type: PropertyType::Studio,
                purpose: PropertyPurpose::Rent,
                price: '1950.00',
                neighborhood: 'Varadouro',
                street: 'Rua das Trincheiras',
                number: '75',
                complement: 'Studio 304',
                cep: '58010-000',
                latitude: '-7.1197',
                longitude: '-34.8820',
                bedrooms: 1,
                suites: 0,
                bathrooms: 1,
                parkingSpaces: 1,
                usefulArea: '32.00',
                totalArea: '38.00',
                condominiumFee: '380.00',
                iptu: '95.00',
                shortDescription: 'Studio econômico no Varadouro, próximo ao centro.',
                fullDescription: 'Studio funcional no Varadouro, ideal para solteiros ou estudantes. '
                    .'Ambiente integrado, banheiro, kitchenette e 1 vaga. '
                    .'A poucos minutos do Centro Histórico e da orla.',
                stateId: $stateId,
                cityId: $cityId,
            ),
        ];
    }

    /**
     * @return array{internal_code: string, title: string, data: PropertyData, image_captions: list<string>}
     */
    private function definition(
        string $internalCode,
        string $title,
        PropertyType $type,
        PropertyPurpose $purpose,
        string $price,
        string $neighborhood,
        string $street,
        string $number,
        ?string $complement,
        string $cep,
        string $latitude,
        string $longitude,
        int $bedrooms,
        int $suites,
        int $bathrooms,
        int $parkingSpaces,
        ?string $usefulArea,
        ?string $totalArea,
        ?string $condominiumFee,
        ?string $iptu,
        string $shortDescription,
        string $fullDescription,
        string $stateId,
        string $cityId,
    ): array {
        return [
            'internal_code' => $internalCode,
            'title' => $title,
            'data' => new PropertyData(
                internalCode: $internalCode,
                title: $title,
                type: $type,
                purpose: $purpose,
                status: PropertyStatus::Available,
                price: $price,
                condominiumFee: $condominiumFee,
                iptu: $iptu,
                cep: $cep,
                stateId: $stateId,
                cityId: $cityId,
                neighborhoodName: $neighborhood,
                street: $street,
                number: $number,
                complement: $complement,
                latitude: $latitude,
                longitude: $longitude,
                bedrooms: $bedrooms,
                suites: $suites,
                bathrooms: $bathrooms,
                parkingSpaces: $parkingSpaces,
                usefulArea: $usefulArea,
                totalArea: $totalArea,
                shortDescription: $shortDescription,
                fullDescription: $fullDescription,
            ),
            'image_captions' => ['Fachada', 'Sala de estar', 'Quarto principal'],
        ];
    }
}
