<?php

declare(strict_types=1);

namespace ImovelZapAi\Proposals\Application\Actions;

use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use ImovelZapAi\Proposals\Application\Services\ProposalHistoryWriter;
use ImovelZapAi\Proposals\Application\Services\ProposalPdfGenerator;
use ImovelZapAi\Proposals\Application\Services\ProposalTemplateRenderer;
use ImovelZapAi\Proposals\Domain\Enums\ProposalHistoryEvent;
use ImovelZapAi\Proposals\Domain\Enums\ProposalStatus;
use ImovelZapAi\Proposals\Domain\Exceptions\ProposalException;
use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalModel;
use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalTemplateModel;
use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalVersionModel;

final class CreateProposalVersionAction
{
    public function __construct(
        private readonly ProposalTemplateRenderer $renderer,
        private readonly ProposalPdfGenerator $pdf,
        private readonly ProposalHistoryWriter $history,
    ) {}

    /**
     * @param  array{
     *     amount?: float|string|null,
     *     commission_percent?: float|string|null,
     *     intro_message?: string|null,
     *     title?: string|null,
     *     change_summary?: string|null,
     * }  $data
     */
    public function execute(ProposalModel $proposal, User $actor, array $data = []): ProposalVersionModel
    {
        if (in_array($proposal->status, [
            \ImovelZapAi\Proposals\Domain\Enums\ProposalStatus::Accepted,
            \ImovelZapAi\Proposals\Domain\Enums\ProposalStatus::Rejected,
            \ImovelZapAi\Proposals\Domain\Enums\ProposalStatus::Expired,
        ], true)) {
            throw ProposalException::invalidState('proposta encerrada.');
        }

        return DB::transaction(function () use ($proposal, $actor, $data): ProposalVersionModel {
            if (array_key_exists('amount', $data)) {
                $proposal->amount = $data['amount'];
            }
            if (array_key_exists('commission_percent', $data)) {
                $proposal->commission_percent = $data['commission_percent'];
            }
            if (array_key_exists('intro_message', $data)) {
                $proposal->intro_message = $data['intro_message'];
            }
            if (! empty($data['title'])) {
                $proposal->title = $data['title'];
            }
            if ($proposal->status === ProposalStatus::ChangesRequested) {
                $proposal->status = ProposalStatus::Draft;
            }
            $proposal->save();

            $proposal->load(['contact', 'property.neighborhood', 'property.city', 'responsible', 'template']);
            $next = ((int) $proposal->versions()->max('version')) + 1;
            $template = $proposal->template
                ?? ProposalTemplateModel::query()->where('tenant_id', $proposal->tenant_id)->where('is_default', true)->firstOrFail();

            $snapshot = $this->renderer->buildSnapshot($proposal, $next);
            $html = $this->renderer->render($template, $proposal, $snapshot);

            $version = ProposalVersionModel::query()->create([
                'tenant_id' => $proposal->tenant_id,
                'proposal_id' => $proposal->id,
                'version' => $next,
                'snapshot' => $snapshot,
                'html_body' => $html,
                'change_summary' => $data['change_summary'] ?? 'Nova revisão',
                'validation_code' => Str::upper(Str::random(10)),
                'created_by' => $actor->id,
            ]);

            $pdf = $this->pdf->generate($proposal, $version, '');
            $version->forceFill(['pdf_disk' => $pdf['disk'], 'pdf_path' => $pdf['path']])->save();
            $proposal->forceFill(['current_version_id' => $version->id])->save();

            $this->history->write(
                $proposal->tenant_id,
                $proposal->id,
                ProposalHistoryEvent::VersionCreated,
                actorType: 'broker',
                actorUserId: (int) $actor->id,
                payload: ['version' => $next],
                message: 'Versão v'.$next.' criada.',
            );

            return $version->fresh();
        });
    }
}
