<?php

namespace App\Repositories;

use App\Models\AccountPaymentCard;

class AccountPaymentCardRepository
{
    public function __construct(AccountPaymentCard $accountPaymentCard)
    {
        $this->model = $accountPaymentCard;
    }

    public function getAllByUserId($userId)
    {
        return $this->model
            ->where('user_id', '=', (int) $userId)
            ->where('is_deleted', '=', 0)
            ->orderBy('is_default', 'desc')
            ->orderBy('id', 'desc')
            ->get();
    }

    public function defaultCard($userId)
    {
        return $this->model
            ->where('user_id', '=', (int) $userId)
            ->where('is_deleted', '=', 0)
            ->where('is_default', '=', 1)
            ->first();
    }

    public function setDefault($userId, $cardId)
    {
        $userId = (int) $userId;
        $cardId = (int) $cardId;

        return \DB::transaction(function () use ($userId, $cardId) {
            $this->model
                ->where('user_id', '=', $userId)
                ->update(['is_default' => 0]);

            $this->model
                ->where('user_id', '=', $userId)
                ->where('id', '=', $cardId)
                ->update(['is_default' => 1]);

            return $this->defaultCard($userId);
        });
    }

    public function upsertStripeCard($userId, array $data)
    {
        $userId = (int) $userId;

        $stripeCustomerId = (string) ($data['stripe_customer_id'] ?? '');
        $stripeSourceId = (string) ($data['stripe_source_id'] ?? '');
        if (! $stripeSourceId) {
            throw new \InvalidArgumentException('Missing stripe_source_id');
        }

        $card = $this->model
            ->where('user_id', '=', $userId)
            ->where('stripe_source_id', '=', $stripeSourceId)
            ->first();

        // Stripe may create a new source id for the same physical card when a token
        // is re-submitted. Avoid creating duplicate saved cards by matching on
        // card details as a fallback.
        if (! $card) {
            $cardLast4 = trim((string) ($data['card_last4'] ?? ''));
            $cardExpMonth = trim((string) ($data['card_exp_month'] ?? ''));
            $cardExpYear = trim((string) ($data['card_exp_year'] ?? ''));
            $cardBrand = trim((string) ($data['card_brand'] ?? ''));
            $cardCountry = trim((string) ($data['card_country'] ?? ''));

            if ($cardLast4 !== '' && $cardExpMonth !== '' && $cardExpYear !== '') {
                $query = $this->model
                    ->where('user_id', '=', $userId)
                    ->where('is_deleted', '=', 0)
                    ->where('payment_gateway', '=', 'stripe')
                    ->where('card_last4', '=', $cardLast4)
                    ->where('card_exp_month', '=', $cardExpMonth)
                    ->where('card_exp_year', '=', $cardExpYear);

                if ($cardBrand !== '') {
                    $query->where('card_brand', '=', $cardBrand);
                }
                if ($cardCountry !== '') {
                    $query->where('card_country', '=', $cardCountry);
                }

                $card = $query->orderBy('id', 'desc')->first();
                if ($card) {
                    // Refresh the stripe_source_id to the latest attached source.
                    $card->stripe_source_id = $stripeSourceId;
                }
            }
        }

        if (! $card) {
            $card = $this->model->newInstance();
            $card->user_id = $userId;
            $card->payment_gateway = 'stripe';
            $card->stripe_source_id = $stripeSourceId;
            $card->is_deleted = 0;
            $card->is_default = 0;
        }

        if ($stripeCustomerId) {
            $card->stripe_customer_id = $stripeCustomerId;
        }

        $card->card_brand = $data['card_brand'] ?? $card->card_brand;
        $card->card_last4 = $data['card_last4'] ?? $card->card_last4;
        $card->card_exp_month = $data['card_exp_month'] ?? $card->card_exp_month;
        $card->card_exp_year = $data['card_exp_year'] ?? $card->card_exp_year;
        $card->card_country = $data['card_country'] ?? $card->card_country;
        $card->save();

        $wantsDefault = (int) ($data['set_as_default'] ?? 0) === 1;
        $hasDefault = (bool) $this->defaultCard($userId);
        if ($wantsDefault || ! $hasDefault) {
            $this->setDefault($userId, (int) $card->id);
            $card->refresh();
        }

        return $card;
    }
}
