<?php

namespace App\Http\Controllers;

use App\Http\Requests\Marketing\WithdrawalConfirmRequest;
use App\Models\EventPackage;
use App\Models\PackagePurchase;
use App\Models\Tenant;
use App\Notifications\Customer\WithdrawalConfirmed;
use App\Services\Paddle\PaddleTransactionService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use Inertia\Inertia;
use Inertia\Response;

class WithdrawalController extends Controller
{
    private const WITHDRAWAL_DAYS = 14;

    public function show(Request $request, string $locale): Response
    {
        $user = $request->user();
        $tenant = $user?->tenant;

        if (! $tenant) {
            abort(404);
        }

        return Inertia::render('marketing/WithdrawalConfirm', [
            'eligiblePurchases' => $this->eligiblePurchases($tenant, $locale),
            'windowDays' => self::WITHDRAWAL_DAYS,
        ]);
    }

    public function confirm(
        WithdrawalConfirmRequest $request,
        PaddleTransactionService $transactions,
        string $locale
    ): RedirectResponse {
        $user = $request->user();
        $tenant = $user?->tenant;

        if (! $tenant) {
            abort(404);
        }

        $purchaseId = $request->integer('purchase_id');
        $purchase = PackagePurchase::query()
            ->with('package')
            ->where('tenant_id', $tenant->id)
            ->findOrFail($purchaseId);

        $eligibility = $this->evaluateEligibility($purchase, $tenant);

        if (! $eligibility['eligible']) {
            return redirect()
                ->back()
                ->with('error', __('marketing.withdrawal.errors.not_eligible', [], $locale));
        }

        $transactionId = $this->resolveTransactionId($purchase);

        if (! $transactionId) {
            Log::warning('Withdrawal missing Paddle transaction reference.', [
                'purchase_id' => $purchase->id,
                'provider' => $purchase->provider,
            ]);

            return redirect()
                ->back()
                ->with('error', __('marketing.withdrawal.errors.missing_transaction', [], $locale));
        }

        try {
            $transactions->refund($transactionId, ['reason' => 'withdrawal']);
        } catch (\Throwable $exception) {
            Log::warning('Withdrawal refund failed', [
                'purchase_id' => $purchase->id,
                'transaction_id' => $transactionId,
                'error' => $exception->getMessage(),
            ]);

            return redirect()
                ->back()
                ->with('error', __('marketing.withdrawal.errors.refund_failed', [], $locale));
        }

        $confirmedAt = now();
        $metadata = $purchase->metadata ?? [];
        $withdrawalMeta = is_array($metadata['withdrawal'] ?? null) ? $metadata['withdrawal'] : [];

        $withdrawalMeta = array_merge($withdrawalMeta, [
            'confirmed_at' => $confirmedAt->toIso8601String(),
            'confirmed_by' => $user?->id,
            'transaction_id' => $transactionId,
        ]);

        $metadata['withdrawal'] = $withdrawalMeta;

        $purchase->forceFill([
            'provider_id' => $transactionId,
            'refunded' => true,
            'metadata' => $metadata,
        ])->save();

        $this->deactivateTenantPackage($tenant, $purchase);

        $recipient = $tenant->contact_email ?? $user?->email;
        if ($recipient) {
            Notification::route('mail', $recipient)
                ->notify(new WithdrawalConfirmed($purchase, $confirmedAt));
        }

        return redirect()
            ->back()
            ->with('success', __('marketing.withdrawal.success', [], $locale));
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function eligiblePurchases(Tenant $tenant, string $locale): array
    {
        $purchases = PackagePurchase::query()
            ->with('package')
            ->where('tenant_id', $tenant->id)
            ->where('type', 'endcustomer_event')
            ->where('provider', 'paddle')
            ->where('refunded', false)
            ->orderByDesc('purchased_at')
            ->orderByDesc('id')
            ->get();

        return $purchases
            ->filter(fn (PackagePurchase $purchase) => $this->evaluateEligibility($purchase, $tenant)['eligible'])
            ->map(fn (PackagePurchase $purchase) => $this->mapPurchaseForView($purchase, $locale))
            ->values()
            ->all();
    }

    /**
     * @return array{eligible: bool, reasons: array<int, string>}
     */
    private function evaluateEligibility(PackagePurchase $purchase, Tenant $tenant): array
    {
        $reasons = [];

        if ($purchase->type !== 'endcustomer_event') {
            $reasons[] = 'type';
        }

        if ($purchase->provider !== 'paddle') {
            $reasons[] = 'provider';
        }

        if ($purchase->refunded) {
            $reasons[] = 'refunded';
        }

        if (! $this->resolveTransactionId($purchase)) {
            $reasons[] = 'missing_reference';
        }

        if (! $this->isWithinWindow($purchase)) {
            $reasons[] = 'expired';
        }

        if ($this->hasAttachedEvent($purchase, $tenant)) {
            $reasons[] = 'event_used';
        }

        return [
            'eligible' => $reasons === [],
            'reasons' => $reasons,
        ];
    }

    private function isWithinWindow(PackagePurchase $purchase): bool
    {
        $purchasedAt = $purchase->purchased_at;

        if (! $purchasedAt) {
            return false;
        }

        return $purchasedAt->greaterThanOrEqualTo(now()->subDays(self::WITHDRAWAL_DAYS));
    }

    private function hasAttachedEvent(PackagePurchase $purchase, Tenant $tenant): bool
    {
        if (! $purchase->purchased_at) {
            return true;
        }

        return EventPackage::query()
            ->where('package_id', $purchase->package_id)
            ->where('purchased_at', '>=', $purchase->purchased_at)
            ->whereHas('event', fn ($query) => $query->where('tenant_id', $tenant->id))
            ->exists();
    }

    /**
     * @return array<string, mixed>
     */
    private function mapPurchaseForView(PackagePurchase $purchase, string $locale): array
    {
        $package = $purchase->package;
        $currency = data_get($purchase->metadata, 'currency', 'EUR');
        $purchasedAt = $purchase->purchased_at;
        $expiresAt = $purchasedAt?->copy()->addDays(self::WITHDRAWAL_DAYS);
        $packageName = $package?->getNameForLocale($locale)
            ?? $package?->name
            ?? __('emails.package_limits.package_fallback', [], $locale);

        return [
            'id' => $purchase->id,
            'package_name' => $packageName,
            'purchased_at' => $purchasedAt?->toIso8601String(),
            'expires_at' => $expiresAt?->toIso8601String(),
            'price' => $purchase->price,
            'currency' => $currency,
        ];
    }

    private function resolveTransactionId(PackagePurchase $purchase): ?string
    {
        if ($purchase->provider === 'paddle' && $purchase->provider_id) {
            return (string) $purchase->provider_id;
        }

        return data_get($purchase->metadata, 'paddle_transaction_id');
    }

    private function deactivateTenantPackage(Tenant $tenant, PackagePurchase $purchase): void
    {
        $tenant->tenantPackages()
            ->where('package_id', $purchase->package_id)
            ->where('active', true)
            ->update([
                'active' => false,
                'expires_at' => now(),
            ]);

        $hasActive = $tenant->tenantPackages()
            ->where('active', true)
            ->whereHas('package', fn ($query) => $query->where('type', 'endcustomer'))
            ->exists();

        if (! $hasActive) {
            $tenant->forceFill([
                'subscription_status' => 'free',
                'subscription_expires_at' => null,
            ])->save();
        }
    }
}
