<?php

declare(strict_types=1);

namespace App\Filament\Resources\PaymentResource\Pages;

use App\Filament\Resources\PaymentResource;
use App\Models\Booking;
use App\Models\Job;
use App\Models\Payment;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;

class CreatePayment extends CreateRecord
{
    protected static string $resource = PaymentResource::class;

    /**
     * Mutate the validated form data before the model is created.
     * - Resolve booking_id from reference or job_id when missing.
     * - Normalize fields.
     * - Allow marking as "deposit" for this booking.
     */
    protected function mutateFormDataBeforeCreate(array $data): array
    {
        // 1) Resolve booking_id
        if (empty($data['booking_id'])) {
            if (! empty($data['reference'])) {
                $data['booking_id'] = Booking::where('reference', $data['reference'])->value('id');
            }
            if (empty($data['booking_id']) && ! empty($data['job_id'])) {
                $data['booking_id'] = Job::whereKey($data['job_id'])->value('booking_id');
            }
        }
        unset($data['reference']); // transient

        // 2) Normalize & deposit flag
        $logAsPaid = (bool) ($data['log_as_paid'] ?? false);
        $isDeposit = (bool) ($data['is_booking_deposit'] ?? false);

        // Default type unless deposit explicitly flagged
        $data['type'] = $isDeposit ? 'deposit' : ($data['type'] ?? 'one_off');

        if ($logAsPaid) {
            $data['status']      = 'succeeded';
            $data['psp']         = $data['psp'] ?? 'manual';
            $data['charge_mode'] = $data['charge_mode'] ?? 'record_only';
        } else {
            $data['status'] = $data['status'] ?? 'pending';
        }

        // Clean up transient flags
        unset($data['log_as_paid'], $data['is_booking_deposit']);

        return $data;
    }

    /**
     * After create:
     * - If request link: ensure portal token & show the pay URL.
     * - If succeeded/manual: recalc job totals.
     * - Otherwise: info message.
     */
    protected function afterCreate(): void
    {
        /** @var Payment $payment */
        $payment = $this->record;

        // If we expect a checkout/pay page, make sure we have a token.
        if ($payment->charge_mode === 'request_link' && $payment->status !== 'succeeded') {
            $this->ensurePortalToken($payment);

            if ($url = $this->payPageUrl($payment)) {
                Notification::make()
                    ->title($payment->type === 'deposit' ? 'Deposit request created' : 'Payment request created')
                    ->body('Send this link to the customer: ' . $url)
                    ->success()
                    ->send();
            } else {
                Notification::make()
                    ->title('Payment request created')
                    ->body('Could not generate the public pay link (missing route or token).')
                    ->warning()
                    ->send();
            }
        }

        // If already paid, update Job totals and confirm.
        if ($payment->status === 'succeeded' && $payment->job) {
            $this->recalcJobTotals($payment->job);

            Notification::make()
                ->title($payment->type === 'deposit' ? 'Deposit recorded' : 'Payment recorded')
                ->body('The payment was logged as paid and the Job totals were updated.')
                ->success()
                ->send();

            return;
        }

        // Generic info (off-session or other)
        if ($payment->charge_mode !== 'request_link' && $payment->status !== 'succeeded') {
            $msg = $payment->charge_mode === 'off_session'
                ? 'Off-session charge created. It will be attempted shortly.'
                : 'Payment created.';
            Notification::make()->title('Payment created')->body($msg)->info()->send();
        }
    }

    /**
     * Recalculate and persist Job paid/remaining preview columns (if present).
     */
    protected function recalcJobTotals(Job $job): void
    {
        $paid      = (int) $job->payments()->where('status', 'succeeded')->sum('amount_cents');
        $total     = (int) ($job->charge_amount_cents ?? 0);
        $remaining = max(0, $total - $paid);

        try {
            if (Schema::hasColumn('jobs', 'paid_amount_cents')) {
                $job->paid_amount_cents = $paid;
            }
            if (Schema::hasColumn('jobs', 'remaining_amount_cents')) {
                $job->remaining_amount_cents = $remaining;
            }
            $job->saveQuietly();
        } catch (\Throwable $e) {
            Log::warning('Job totals recalc failed', [
                'job_id' => $job->id,
                'error'  => $e->getMessage(),
            ]);
        }
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    /**
     * Ensure a portal token exists on the Payment for request-link mode.
     */
    protected function ensurePortalToken(Payment $payment): void
    {
        // Only act if the column exists
        if (! Schema::hasColumn($payment->getTable(), 'portal_token')) {
            return;
        }

        if (empty($payment->portal_token)) {
            $payment->portal_token = Str::random(64);
            try {
                $payment->saveQuietly();
            } catch (\Throwable $e) {
                Log::warning('payment.portal_token.save_failed', [
                    'payment_id' => $payment->id,
                    'error'      => $e->getMessage(),
                ]);
            }
        }
    }

    /**
     * Build a public pay URL for a Payment using its portal token.
     * Expects a route like: route('portal.pay.payment', ['token' => $token])
     */
    protected function payPageUrl(Payment $payment): ?string
    {
        if (! Schema::hasColumn($payment->getTable(), 'portal_token')) {
            return null;
        }

        $token = $payment->portal_token;
        if (blank($token)) {
            return null;
        }

        if (! Route::has('portal.pay.payment')) {
            return null;
        }

        try {
            return route('portal.pay.payment', ['token' => $token]);
        } catch (\Throwable $e) {
            Log::warning('payment.pay_url.failed', [
                'payment_id' => $payment->id,
                'error'      => $e->getMessage(),
            ]);
            return null;
        }
    }
}
