<?php

declare(strict_types=1);

namespace ImovelZapAi\Visits\Application\Actions;

use App\Models\User;
use Illuminate\Support\Facades\DB;
use ImovelZapAi\Visits\Application\Services\AgendaConflictChecker;
use ImovelZapAi\Visits\Application\Services\VisitLogWriter;
use ImovelZapAi\Visits\Application\Services\VisitSnapshotter;
use ImovelZapAi\Visits\Domain\Enums\VisitLogEvent;
use ImovelZapAi\Visits\Domain\Enums\VisitParticipantRole;
use ImovelZapAi\Visits\Domain\Exceptions\VisitException;
use ImovelZapAi\Visits\Infrastructure\Persistence\Eloquent\VisitModel;
use ImovelZapAi\Visits\Infrastructure\Persistence\Eloquent\VisitParticipantModel;

final class ReassignVisitBrokerAction
{
    public function __construct(
        private readonly AgendaConflictChecker $conflictChecker,
        private readonly VisitLogWriter $logWriter,
        private readonly VisitSnapshotter $snapshotter,
    ) {}

    public function execute(VisitModel $visit, User $actor, int $newBrokerId): VisitModel
    {
        if ($visit->status->isTerminal()) {
            throw VisitException::invalidState($visit->status->value, 'reassign');
        }

        if ($visit->scheduled_start !== null && $visit->scheduled_end !== null) {
            $this->conflictChecker->assertNoConflict(
                $visit->tenant_id,
                $newBrokerId,
                $visit->scheduled_start,
                $visit->scheduled_end,
                $visit->id,
            );
        }

        return DB::transaction(function () use ($visit, $actor, $newBrokerId): VisitModel {
            $before = $this->snapshotter->snapshot($visit);

            $visit->forceFill(['responsible_user_id' => $newBrokerId])->save();

            VisitParticipantModel::query()
                ->where('visit_id', $visit->id)
                ->where('role', VisitParticipantRole::Responsible)
                ->delete();

            VisitParticipantModel::query()->create([
                'tenant_id' => $visit->tenant_id,
                'visit_id' => $visit->id,
                'user_id' => $newBrokerId,
                'role' => VisitParticipantRole::Responsible,
            ]);

            $this->logWriter->write(
                $visit->tenant_id,
                $visit->id,
                VisitLogEvent::BrokerChanged,
                actorUserId: (int) $actor->id,
                before: $before,
                after: $this->snapshotter->snapshot($visit->fresh()),
                message: 'Corretor responsável alterado.',
            );

            return $visit->fresh(['participants', 'responsible']);
        });
    }
}
