<?php

namespace App\Http\Controllers;

use App\Models\Booking;
use App\Models\Deposit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Stripe\StripeClient;

class DepositController extends Controller
{
    protected function stripe(): StripeClient
    {
        return new StripeClient((string) config('services.stripe.secret'));
    }

    /**
     * Authorise a HOLD (manual capture PaymentIntent).
     * POST: amount_cents (required), payment_method (optional if customer default exists)
     *
     * NOTE: This authorises against a Booking; we store a Deposit with job_id null (ok),
     * and link customer_id where available.
     */
    public function authorise(Request $request, Booking $booking)
    {
        $customer = $booking->customer;
        abort_unless($customer, 422, 'Customer not found for booking.');

        $amount = (int) $request->integer('amount_cents', (int) ($booking->hold_amount ?? 0));
        abort_if($amount <= 0, 422, 'Invalid hold amount.');

        $paymentMethodId = $request->input('payment_method')
            ?: ($customer->default_payment_method_id ?? null);
        abort_unless($paymentMethodId, 422, 'No payment method available.');

        $stripe = $this->stripe();
        $idemKey = 'hold_'.$booking->id.'_'.$amount;

        try {
            $pi = $stripe->paymentIntents->create([
                'amount'                    => $amount,
                'currency'                  => strtolower($booking->currency ?? 'NZD'),
                'capture_method'            => 'manual',
                'customer'                  => $customer->stripe_customer_id,
                'payment_method'            => $paymentMethodId,
                'confirm'                   => true,
                'off_session'               => true,
                'automatic_payment_methods' => ['enabled' => true],
                'metadata'       => [
                    'booking_id' => (string) $booking->id,
                    'type'       => 'deposit_hold',
                ],
            ], ['idempotency_key' => $idemKey]);

            $deposit = Deposit::firstOrCreate(
                ['stripe_payment_intent' => $pi->id],
                [
                    'job_id'           => null, // booking-scoped hold (no job)
                    'customer_id'      => $customer->id,
                    'authorized_cents' => $amount,
                    'currency'         => strtoupper($booking->currency ?? 'NZD'),
                    'status'           => 'authorized',
                ]
            );

            Log::info('[holds] hold authorized', [
                'booking' => $booking->id,
                'deposit' => $deposit->id,
                'pi'      => $pi->id,
                'amount'  => $amount,
            ]);

            return response()->json([
                'status'   => 'authorized',
                'deposit'  => $deposit->id,
                'pi'       => $pi->id,
                'amount'   => $amount,
            ]);
        } catch (\Throwable $e) {
            Log::error('[holds] authorise error: '.$e->getMessage(), [
                'booking' => $booking->id,
                'amount'  => $amount,
                'key'     => $idemKey,
            ]);

            return response()->json([
                'error' => 'Could not authorise hold: '.$e->getMessage(),
            ], 500);
        }
    }

    /**
     * Capture a previously authorized Deposit (with optional reason).
     * POST: amount_cents (required), reason (optional)
     */
    public function capture(Request $request, Deposit $deposit)
    {
        $amount = (int) $request->integer('amount_cents');
        $reason = (string) $request->input('reason', '');

        try {
            $deposit->capture($this->stripe(), $amount, $reason);

            // Optionally also update Job paid/balance if this capture should count toward the job
            if ($deposit->job && class_exists(PaymentController::class)) {
                app(PaymentController::class)->recordJobPaid(new Request([
                    'amount_cents'   => $amount,
                    'currency'       => $deposit->currency ?? 'NZD',
                    'payment_intent' => $deposit->stripe_payment_intent,
                    'charge'         => $deposit->stripe_charge,
                ]), $deposit->job);
            }

            return back()->with('status', 'Captured successfully.');
        } catch (\Throwable $e) {
            Log::error('[deposit.capture] ' . $e->getMessage(), [
                'deposit_id' => $deposit->id,
                'job_id'     => $deposit->job_id,
                'amount'     => $amount,
                'reason'     => $reason,
            ]);

            return back()->withErrors('Could not capture: ' . $e->getMessage());
        }
    }

    /**
     * Void (cancel) a previously authorized HOLD.
     */
    public function void(Deposit $deposit)
    {
        $piId = $deposit->stripe_payment_intent;
        abort_unless($piId, 422, 'Deposit missing PaymentIntent.');

        $stripe = $this->stripe();
        $idemKey = 'void_'.$deposit->id;

        try {
            $stripe->paymentIntents->cancel($piId, [], ['idempotency_key' => $idemKey]);
            $deposit->update(['status' => 'canceled']);

            Log::info('[holds] voided', [
                'deposit' => $deposit->id,
                'pi'      => $piId,
            ]);

            return response()->json([
                'status'  => 'canceled',
                'deposit' => $deposit->id,
                'pi'      => $piId,
            ]);
        } catch (\Throwable $e) {
            Log::error('[holds] void error: '.$e->getMessage(), [
                'deposit' => $deposit->id,
                'pi'      => $piId,
                'key'     => $idemKey,
            ]);

            return response()->json([
                'error' => 'Could not void hold: '.$e->getMessage(),
            ], 500);
        }
    }

    /**
     * Capture a previously authorised BOND (security hold) PaymentIntent
     * stored directly on the Booking (stripe_bond_pi_id).
     *
     * POST: amount_cents (optional, defaults to booking->hold_amount)
     */
    public function captureBond(Booking $booking, Request $request)
    {
        $amount = (int) $request->integer('amount_cents', (int) ($booking->hold_amount ?? 0));
        abort_unless($booking->stripe_bond_pi_id && $amount > 0, 400, 'Missing bond intent or invalid amount.');

        $stripe = $this->stripe();

        try {
            $stripe->paymentIntents->capture($booking->stripe_bond_pi_id, [
                'amount_to_capture' => $amount,
            ]);

            $booking->forceFill(['bond_captured_at' => now()])->save();

            Log::info('[holds] bond captured', [
                'booking' => $booking->id,
                'pi'      => $booking->stripe_bond_pi_id,
                'amount'  => $amount,
            ]);

            return back()->with('status', 'Bond captured');
        } catch (\Throwable $e) {
            Log::error('[holds] bond capture error: '.$e->getMessage(), [
                'booking' => $booking->id,
                'pi'      => $booking->stripe_bond_pi_id,
            ]);

            return back()->withErrors('Could not capture bond: '.$e->getMessage());
        }
    }
}
