<?php

namespace App\Helpers\FirstParty\Credits;

use App\Helpers\FirstParty\Purchase\PurchaseHelper;
use App\Models\UserCredit;
use App\Models\UserCreditTransaction;
use Illuminate\Support\Facades\DB;

class CreditsHelper
{
    public static function getCreditPackByStripePriceId($stripe_price_id)
    {
        $credit_packs = self::getCreditPacks(null, true);

        foreach ($credit_packs as $credit_pack) {

            $stripe_price_ids = PurchaseHelper::getPlanSystemProperty($credit_pack, 'stripe.stripe_price_ids');

            if (in_array($stripe_price_id, $stripe_price_ids)) {
                return $credit_pack;
            }
        }

        return null;
    }

    public static function getCreditsPackById($id, $enabled = true, $system = false)
    {
        $credits_packs = self::getCreditPacks($enabled, $system);
        foreach ($credits_packs as $credits_pack) {
            if ($credits_pack['id'] == $id) {
                return $credits_pack;
            }
        }

        return null;
    }

    public static function getCreditPacks($enabled = null, $system = true)
    {

        $credit_packs = [];

        $one_times = PurchaseHelper::getOneTimePurchases($enabled, $system);

        foreach ($one_times as $one_time) {

            if ($one_time['type'] == 'alacarte_credits') {
                $credit_packs[] = $one_time;
            }
        }

        return $credit_packs;
    }

    // ////

    public static function getUserCredits(int $user_id): UserCredit
    {
        $user_credit = UserCredit::where('user_id', $user_id)->first();

        if (! $user_credit) {
            $user_credit = UserCredit::create([
                'user_id' => $user_id,
                'subscription_credits' => 0,
                'alacarte_credits' => 0,
                'spend_subscription_first' => true,
            ]);
        }

        return $user_credit;
    }

    // when user purchase annual subscription, need to 12x the deposit amount
    public static function deposit(int $user_id, int $amount, string $type, ?string $description = null): bool
    {
        if ($amount <= 0) {
            throw new \InvalidArgumentException('Amount must be positive');
        }

        if (! in_array($type, [UserCredit::TYPE_SUBSCRIPTION, UserCredit::TYPE_ALACARTE])) {
            throw new \InvalidArgumentException('Type must be subscription or alacarte');
        }

        return DB::transaction(function () use ($user_id, $amount, $type, $description) {
            $user_credit = self::getUserCredits($user_id);

            if ($type === UserCredit::TYPE_SUBSCRIPTION) {
                $user_credit->subscription_credits += $amount;
            } else {
                $user_credit->alacarte_credits += $amount;
            }

            $user_credit->save();
            self::logTransaction($user_id, $type, 'deposit', $amount, $user_credit, $description);

            return true;
        });
    }

    public static function spend(int $user_id, int $amount, ?string $description = null, ?bool $use_subscription_first = null): bool
    {
        if ($amount <= 0) {
            throw new \InvalidArgumentException('Amount must be positive');
        }

        if (! self::canSpend($user_id, $amount)) {
            return false;
        }

        return DB::transaction(function () use ($user_id, $amount, $description, $use_subscription_first) {
            $user_credit = self::getUserCredits($user_id);
            $remaining = $amount;
            $subscription_first = $use_subscription_first ?? $user_credit->spend_subscription_first;

            $spend_order = $subscription_first
                ? [UserCredit::TYPE_SUBSCRIPTION, UserCredit::TYPE_ALACARTE]
                : [UserCredit::TYPE_ALACARTE, UserCredit::TYPE_SUBSCRIPTION];

            foreach ($spend_order as $type) {
                if ($remaining <= 0) {
                    break;
                }

                $available = $type === UserCredit::TYPE_SUBSCRIPTION
                    ? $user_credit->subscription_credits
                    : $user_credit->alacarte_credits;

                if ($available > 0) {
                    $to_spend = min($remaining, $available);

                    if ($type === UserCredit::TYPE_SUBSCRIPTION) {
                        $user_credit->subscription_credits -= $to_spend;
                    } else {
                        $user_credit->alacarte_credits -= $to_spend;
                    }

                    self::logTransaction($user_id, $type, 'spend', -$to_spend, $user_credit, $description);
                    $remaining -= $to_spend;
                }
            }

            $user_credit->save();

            return true;
        });
    }

    public static function canSpend(int $user_id, int $amount): bool
    {
        $user_credit = self::getUserCredits($user_id);

        return self::_getTotalBalance($user_credit) >= $amount;
    }

    public static function summary(int $user_id): array
    {
        $user_credit = self::getUserCredits($user_id);

        return [
            'subscription' => $user_credit->subscription_credits,
            'alacarte' => $user_credit->alacarte_credits,
            'total' => self::_getTotalBalance($user_credit),
            'preference' => $user_credit->spend_subscription_first ? 'subscription_first' : 'alacarte_first',
        ];
    }

    public static function balance(int $user_id, ?string $type = null): int
    {
        $user_credit = self::getUserCredits($user_id);

        return match ($type) {
            UserCredit::TYPE_SUBSCRIPTION => $user_credit->subscription_credits,
            UserCredit::TYPE_ALACARTE => $user_credit->alacarte_credits,
            default => self::_getTotalBalance($user_credit),
        };
    }

    private static function _getTotalBalance(UserCredit $user_credit): int
    {
        return $user_credit->subscription_credits + $user_credit->alacarte_credits;
    }

    public static function setSpendSubscriptionFirst(int $user_id, bool $spendSubscriptionFirst): bool
    {
        $user_credit = self::getUserCredits($user_id);
        $user_credit->spend_subscription_first = $spendSubscriptionFirst;

        return $user_credit->save();
    }

    public static function transactionHistory(int $user_id, int $limit = 20): \Illuminate\Database\Eloquent\Collection
    {
        $user_credit = self::getUserCredits($user_id);

        return $user_credit->transactions()->limit($limit)->get();
    }

    private static function logTransaction(int $user_id, string $type, string $operation, int $amount, UserCredit $user_credit, ?string $description = null): void
    {
        UserCreditTransaction::create([
            'user_id' => $user_id,
            'credit_type' => $type,
            'operation' => $operation,
            'amount' => $amount,
            'subscription_balance_after' => $user_credit->subscription_credits,
            'alacarte_balance_after' => $user_credit->alacarte_credits,
            'description' => $description,
        ]);
    }
}
