<?php

declare(strict_types=1);

namespace ImovelZapAi\Owners\Application\Actions;

use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use ImovelZapAi\Conversations\Infrastructure\Persistence\Eloquent\ContactModel;
use ImovelZapAi\Owners\Application\Services\ListingHistoryWriter;
use ImovelZapAi\Owners\Domain\Enums\ListingHistoryEvent;
use ImovelZapAi\Owners\Domain\Enums\ListingStageKey;
use ImovelZapAi\Owners\Domain\Exceptions\OwnerException;
use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\ListingOpportunityModel;
use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\ListingVisitModel;
use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\OwnerContactModel;
use ImovelZapAi\Visits\Application\Actions\CreateVisitRequestAction;
use ImovelZapAi\Visits\Application\Actions\ScheduleVisitAction;
use ImovelZapAi\Visits\Domain\Enums\VisitPurpose;

final class ScheduleListingVisitAction
{
    public function __construct(
        private readonly CreateVisitRequestAction $createVisit,
        private readonly ScheduleVisitAction $scheduleVisit,
        private readonly ChangeListingStageAction $changeStage,
        private readonly ListingHistoryWriter $history,
    ) {}

    /**
     * @param  array{
     *     scheduled_start: string,
     *     scheduled_end?: string|null,
     *     purpose?: string|null,
     *     location_notes?: string|null,
     * }  $data
     */
    public function execute(ListingOpportunityModel $opportunity, User $actor, array $data): ListingVisitModel
    {
        if ($opportunity->isClosed()) {
            throw OwnerException::invalidState('oportunidade encerrada.');
        }

        return DB::transaction(function () use ($opportunity, $actor, $data): ListingVisitModel {
            $purpose = VisitPurpose::tryFrom((string) ($data['purpose'] ?? VisitPurpose::ListingEvaluation->value))
                ?? VisitPurpose::ListingEvaluation;

            $contactId = $this->resolveContactId($opportunity);

            $visit = $this->createVisit->execute($opportunity->tenant_id, $actor, [
                'contact_id' => $contactId,
                'property_id' => $opportunity->property_id,
                'owner_id' => $opportunity->owner_id,
                'listing_opportunity_id' => $opportunity->id,
                'purpose' => $purpose->value,
                'location_notes' => $data['location_notes'] ?? null,
            ]);

            $start = Carbon::parse($data['scheduled_start']);
            $end = isset($data['scheduled_end']) ? Carbon::parse($data['scheduled_end']) : null;
            $visit = $this->scheduleVisit->execute($visit, $actor, $start, $end);

            $link = ListingVisitModel::query()->create([
                'tenant_id' => $opportunity->tenant_id,
                'listing_opportunity_id' => $opportunity->id,
                'visit_id' => $visit->id,
                'owner_id' => $opportunity->owner_id,
                'purpose' => $purpose,
            ]);

            $opportunity->forceFill(['last_activity_at' => now()])->save();
            $opportunity->owner?->forceFill(['last_contacted_at' => now()])->save();

            $this->changeStage->execute($opportunity->fresh(), $actor, ListingStageKey::EvaluationScheduled->value);

            $this->history->write(
                $opportunity->tenant_id,
                ListingHistoryEvent::VisitLinked,
                opportunityId: $opportunity->id,
                ownerId: $opportunity->owner_id,
                actorUserId: (int) $actor->id,
                after: ['visit_id' => $visit->id, 'purpose' => $purpose->value],
                message: 'Visita de captação agendada.',
            );

            return $link->fresh(['visit']);
        });
    }

    private function resolveContactId(ListingOpportunityModel $opportunity): string
    {
        $phone = OwnerContactModel::query()
            ->where('owner_id', $opportunity->owner_id)
            ->whereIn('channel', ['phone', 'whatsapp'])
            ->orderByDesc('is_primary')
            ->first();

        $waId = $phone?->value ?? ('owner-'.$opportunity->owner_id);

        $contact = ContactModel::query()->firstOrCreate(
            [
                'tenant_id' => $opportunity->tenant_id,
                'wa_id' => $waId,
            ],
            [
                'phone' => $waId,
                'display_name' => $opportunity->owner?->name ?? 'Proprietário',
            ],
        );

        return $contact->id;
    }
}
