<?php

declare(strict_types=1);

namespace ImovelZapAi\Billing\Application\Actions;

use ImovelZapAi\Billing\Application\Contracts\MercadoPagoClient;
use ImovelZapAi\Billing\Application\Services\CouponCalculator;
use ImovelZapAi\Billing\Domain\Enums\BillingCycle;
use ImovelZapAi\Billing\Domain\Exceptions\BillingException;
use ImovelZapAi\Billing\Infrastructure\Persistence\Eloquent\BillingCouponRedemptionModel;
use ImovelZapAi\Organizations\Application\Actions\EnsureOrganizationBootstrapAction;
use ImovelZapAi\Organizations\Domain\Enums\SubscriptionStatus;
use ImovelZapAi\Organizations\Infrastructure\Persistence\Eloquent\OrganizationSubscriptionModel;
use ImovelZapAi\Organizations\Infrastructure\Persistence\Eloquent\SubscriptionPlanModel;

final class StartCheckoutAction
{
    public function __construct(
        private readonly EnsureOrganizationBootstrapAction $bootstrap,
        private readonly SyncMercadoPagoPlanCatalogAction $syncCatalog,
        private readonly MercadoPagoClient $client,
        private readonly CouponCalculator $coupons,
    ) {}

    /**
     * @return array{subscription: OrganizationSubscriptionModel, init_point: ?string}
     */
    public function execute(
        string $tenantId,
        string $planCode,
        BillingCycle $cycle,
        string $payerEmail,
        ?string $couponCode = null,
    ): array {
        if (! (bool) config('billing.enabled', true)) {
            throw BillingException::disabled();
        }

        $this->bootstrap->execute($tenantId);
        $this->syncCatalog->execute();

        $plan = SubscriptionPlanModel::query()
            ->where('code', $planCode)
            ->where('is_active', true)
            ->first();

        if ($plan === null) {
            throw BillingException::planNotFound($planCode);
        }

        if ((int) $plan->price_monthly_cents <= 0 && (int) $plan->price_yearly_cents <= 0) {
            throw BillingException::checkoutFailed('Plano Enterprise requer contato comercial.');
        }

        $mpPlanId = $cycle === BillingCycle::Yearly
            ? $plan->mp_preapproval_plan_id_yearly
            : $plan->mp_preapproval_plan_id_monthly;

        if (blank($mpPlanId)) {
            throw BillingException::checkoutFailed('Plano sem vínculo Mercado Pago.');
        }

        $coupon = null;
        $discountCents = 0;
        if ($couponCode !== null && trim($couponCode) !== '') {
            $coupon = $this->coupons->findValid($couponCode);
            $this->coupons->assertApplicable($coupon, $plan);
            $discountCents = $this->coupons->discountCents($coupon, $plan, $cycle);
        }

        $amountCents = $this->coupons->amountAfterDiscount($plan, $cycle, $coupon);

        /** @var OrganizationSubscriptionModel $subscription */
        $subscription = OrganizationSubscriptionModel::query()->updateOrCreate(
            ['tenant_id' => $tenantId],
            [
                'subscription_plan_id' => $plan->id,
                'status' => SubscriptionStatus::Trialing,
                'billing_cycle' => $cycle->value,
                'mp_payer_email' => $payerEmail,
                'coupon_code' => $coupon?->code,
                'trial_ends_at' => now()->addDays((int) config('billing.trial_days', 14)),
                'current_period_ends_at' => now()->addDays((int) config('billing.trial_days', 14)),
            ],
        );

        $this->syncTenantType($tenantId, $plan);

        $preapproval = $this->client->createPreapproval(
            (string) $mpPlanId,
            $payerEmail,
            (string) $subscription->id,
            (string) config('billing.checkout_back_url'),
            $amountCents,
        );

        $subscription->forceFill([
            'mp_preapproval_id' => $preapproval['id'],
        ])->save();

        if ($coupon !== null) {
            BillingCouponRedemptionModel::query()->updateOrCreate(
                [
                    'billing_coupon_id' => $coupon->id,
                    'tenant_id' => $tenantId,
                ],
                [
                    'organization_subscription_id' => $subscription->id,
                    'discount_cents' => $discountCents,
                ],
            );
            $coupon->forceFill([
                'redemptions_count' => (int) $coupon->redemptions_count + 1,
            ])->save();
        }

        return [
            'subscription' => $subscription->fresh(['plan']) ?? $subscription,
            'init_point' => $preapproval['init_point'] ?? null,
        ];
    }

    private function syncTenantType(string $tenantId, SubscriptionPlanModel $plan): void
    {
        $type = (int) $plan->max_users === 1
            ? \ImovelZapAi\Tenancy\Domain\Enums\OrganizationType::Solo
            : \ImovelZapAi\Tenancy\Domain\Enums\OrganizationType::Agency;

        \ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel::query()
            ->whereKey($tenantId)
            ->update(['type' => $type]);
    }
}
