<?php

namespace App\Services\Payments;

use App\Models\PaymentProfile;
use App\Contracts\PaymentGatewayInterface;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Exception;

/**
 * PaymentMethodService
 *
 * Manages payment method updates and retrieval for a company through the configured payment gateway.
 */
class PaymentMethodService
{
    protected PaymentGatewayInterface $gateway;

    /**
     * Inject the active payment gateway implementation (Stripe, ACH, etc.).
     */
    public function __construct(PaymentGatewayInterface $gateway)
    {
        $this->gateway = $gateway;
    }

    /**
     * Set or update the default payment method for a company's billing profile.
     *
     * @param int $companyId
     * @param string $newPaymentMethodId
     * @return string
     *
     * @throws ValidationException|Exception
     */
    public function updateDefaultPaymentMethod(int $companyId, string $newPaymentMethodId): string
    {
        $profile = PaymentProfile::where('company_id', $companyId)
            ->where('provider', 'stripe') // Replace with dynamic provider logic if needed
            ->first();

        if (!$profile || !$profile->provider_customer_id) {
            throw ValidationException::withMessages([
                'payment_profile' => 'No payment profile or customer ID found for this company.'
            ]);
        }

        $customerId = $profile->provider_customer_id;

        try {

            // Set it as default if it's different from the current one
            $success = $this->gateway->changePaymentMethod($customerId, $newPaymentMethodId);

            return $success === true
                ? 'This payment method is already set as default.'
                : 'Payment method updated successfully.';
        } catch (Exception $e) {
            Log::error('Payment method update failed', [
                'company_id' => $companyId,
                'error' => $e->getMessage(),
            ]);

            throw new Exception('Failed to update payment method. Please try again later.');
        }
    }

    /**
     * Retrieve the currently set default payment method for a company.
     *
     * @param int $companyId
     * @return array|null
     *
     * @throws Exception
     */
    public function getCurrentPaymentMethod(int $companyId): ?array
    {
        $profile = PaymentProfile::where('company_id', $companyId)
            ->where('provider', 'stripe')
            ->first();

        if (!$profile || !$profile->provider_customer_id) {
            throw new Exception('No payment profile or customer ID found.');
        }

        $customerId = $profile->provider_customer_id;

        try {
            // Use gateway abstraction to fetch customer details
            $customer = $this->gateway->retrieveCustomer($customerId);
            $defaultId = $customer['invoice_settings']['default_payment_method'] ?? null;

            if (!$defaultId) {
                return null;
            }

            $method = $this->gateway->getRawPaymentMethod($defaultId);

            return [
                'brand'      => $method['card']['brand'] ?? 'Unknown',
                'last4'      => $method['card']['last4'] ?? '****',
                'exp_month'  => $method['card']['exp_month'] ?? null,
                'exp_year'   => $method['card']['exp_year'] ?? null,
            ];
        } catch (Exception $e) {
            Log::error('Failed to fetch current payment method.', [
                'company_id' => $companyId,
                'error' => $e->getMessage(),
            ]);

            throw new Exception('Unable to retrieve payment method.');
        }
    }

    /**
     * Charge a customer for a specific amount using the default payment method on file.
     *
     * This method handles payment intent creation through the configured payment gateway (e.g., Stripe).
     * It retrieves the company's payment profile, validates it, and initiates the charge.
     *
     * @param int    $companyId        The ID of the company being charged.
     * @param int    $amountInCents    The amount to charge in **cents** (e.g., 500 = $5.00).
     * @param string $description      A dynamic description for the charge (e.g., invoice info).
     * @param array  $metadata         Optional metadata to pass with the payment (for traceability).
     *
     * @return array                   The full payment intent response from the payment gateway.
     *
     * @throws \Exception              If no valid payment profile or customer ID is found.
     */
    public function chargeCustomer(int $companyId, int $amountInCents, string $description, array $metadata = []): array {
        // Look up the payment profile for the given company
        $profile = PaymentProfile::where('company_id', $companyId)
            ->where('provider', 'stripe') // NOTE: This can be made dynamic in future (based on settings)
            ->first();

        // If profile or customer ID is missing, we can't proceed
        if (!$profile || !$profile->provider_customer_id) {
            throw new \Exception('No customer profile found.');
        }

        // Call the gateway to create a PaymentIntent
        return $this->gateway->createPaymentIntent([
            'amount'      => $amountInCents,
            'currency'    => 'usd',
            'customer_id' => $profile->provider_customer_id,
            'description' => $description,
            'metadata'    => $metadata,
        ]);
    }

    public function createSetupIntent(int $companyId): array
    {
        $profile = PaymentProfile::where('company_id', $companyId)
            ->where('provider', 'stripe')
            ->first();

        if (!$profile || !$profile->provider_customer_id) {
            throw new \Exception('No Stripe customer ID found. for ');
        }

        return $this->gateway->createSetupIntent($profile->provider_customer_id);
    }

}
