<?php

namespace App\Http\Controllers\Frontend\TopUp;

use App\Actions\Promo\GetAutoPromoAction;
use App\Actions\Promo\ValidatePromoAction;
use App\Actions\Streamer\ValidateStreamerCodeAction;
use App\Actions\TopUp\CalculateGoldAction;
use App\Http\Controllers\Controller;
use App\Http\Requests\Frontend\TopUp\FetchAutoPromoRequest;
use App\Http\Requests\Frontend\TopUp\ValidatePromoRequest;
use App\Http\Requests\Frontend\TopUp\ValidateStreamerCodeRequest;
use App\Http\Resources\BaseResource;
use App\Http\Resources\Promo\PromoResource;
use App\Http\Resources\Streamer\StreamerResource;
use App\Models\PwFactionBonus;
use App\Models\PwPromo;
use App\Models\Streamer;
use App\Repositories\PaymentSettingsRepository;
use App\Services\Api\API;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;

class TopUpController extends Controller
{
    protected API $api;

    public function __construct(
        API $api,
        private ValidatePromoAction $validatePromo,
        private ValidateStreamerCodeAction $validateStreamerCode,
        private GetAutoPromoAction $getAutoPromo,
        private CalculateGoldAction $calculateGold,
        private PaymentSettingsRepository $settingsRepository,
    ) {
        $this->api = $api;
    }

    /**
     * Get user's characters with faction bonuses
     */
    protected function getUserCharactersWithFactionBonus()
    {
        $user = Auth::user();
        $factionBonuses = PwFactionBonus::pluck('bonus_percentage', 'faction_id')->toArray();

        $characters = [];
        try {
            $roles = $this->api->getRoles($user->ID);
            if (! empty($roles['roles'])) {
                foreach ($roles['roles'] as $role) {
                    $factionData = $this->api->getUserFaction($role['id']);
                    $factionId = $factionData['fid'] ?? 0;
                    $bonusPercentage = $factionBonuses[$factionId] ?? 0;

                    $characters[] = [
                        'id' => $role['id'],
                        'name' => $role['name'],
                        'faction_id' => $factionId,
                        'faction_bonus' => (float) $bonusPercentage,
                    ];
                }
            }
        } catch (\Exception $e) {
            // Ignore error, return empty characters
        }

        return $characters;
    }

    /**
     * Generate gold packages based on gold rate
     */
    protected function generatePackages($goldRate)
    {
        return [
            ['gold' => 100, 'price' => 100 * $goldRate],
            ['gold' => 500, 'price' => 500 * $goldRate],
            ['gold' => 1000, 'price' => 1000 * $goldRate],
            ['gold' => 5000, 'price' => 5000 * $goldRate],
            ['gold' => 10000, 'price' => 10000 * $goldRate],
        ];
    }

    /**
     * Verify character belongs to user
     */
    protected function verifyCharacterOwnership($userId, $characterId)
    {
        try {
            $rolesData = $this->api->getRoles($userId);
            $roles = collect($rolesData['roles'] ?? []);

            return $roles->firstWhere('id', $characterId);
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * Get faction bonus for a character
     */
    protected function getFactionBonus($characterId)
    {
        try {
            $factionData = $this->api->getUserFaction($characterId);
            $factionId = $factionData['fid'] ?? 0;
            $factionBonus = PwFactionBonus::where('faction_id', $factionId)->first();

            return [
                'faction_id' => $factionId,
                'bonus_percentage' => $factionBonus ? (float) $factionBonus->bonus_percentage : 0,
            ];
        } catch (\Exception $e) {
            return ['faction_id' => 0, 'bonus_percentage' => 0];
        }
    }

    /**
     * Process promo code and calculate discount
     */
    protected function processPromoCode($promoCode, $amount, $userId, $goldRate = 1000)
    {
        if (empty($promoCode)) {
            return [
                'promo' => null,
                'discount' => 0,
                'bonus_gold' => 0,
                'final_amount' => $amount,
            ];
        }

        $promo = PwPromo::where('code', strtoupper($promoCode))->first();

        if ($promo && $promo->isValid() && $promo->canBeUsedByUser($userId)) {
            $discountInfo = $promo->calculateDiscount($amount, $goldRate);

            return [
                'promo' => $promo,
                'discount' => $discountInfo['discount'],
                'bonus_gold' => $discountInfo['bonus_gold'],
                'final_amount' => $amount - $discountInfo['discount'],
            ];
        }

        return [
            'promo' => null,
            'discount' => 0,
            'bonus_gold' => 0,
            'final_amount' => $amount,
        ];
    }

    /**
     * Get best auto promo for given amount and user
     */
    protected function getAutoPromo($amount, $userId, $goldRate = 1000)
    {
        $autoPromo = PwPromo::where('is_active', true)
            ->where('is_auto', true)
            ->where('min_amount', '<=', $amount)
            ->where(function ($query) {
                $query->whereNull('usage_limit')
                    ->orWhereRaw('usage_count < usage_limit');
            })
            ->where(function ($query) use ($userId) {
                $query->where('per_user_limit', 0)
                    ->orWhereRaw('(SELECT COUNT(*) FROM pw_promo_usage WHERE promo_id = pw_promo.id AND user_id = ?) < per_user_limit', [$userId]);
            })
            ->where(function ($query) {
                $query->whereNull('start_date')
                    ->orWhere('start_date', '<=', now());
            })
            ->where(function ($query) {
                $query->whereNull('end_date')
                    ->orWhere('end_date', '>=', now());
            })
            ->orderByDesc('min_amount')
            ->first();

        if (! $autoPromo) {
            return null;
        }

        $discountInfo = $autoPromo->calculateDiscount($amount, $goldRate);

        return [
            'promo' => $autoPromo,
            'bonus_gold' => $discountInfo['bonus_gold'],
        ];
    }

    /**
     * Process streamer code and calculate bonus gold
     */
    protected function processStreamerCode($streamerCode, $baseGold)
    {
        if (empty($streamerCode)) {
            return [
                'streamer' => null,
                'bonus_gold' => 0,
            ];
        }

        $streamer = Streamer::where('code', strtoupper($streamerCode))
            ->where('is_active', true)
            ->first();

        if ($streamer) {
            $streamerBonusGold = floor($baseGold * ($streamer->percentage / 100));

            return [
                'streamer' => $streamer,
                'bonus_gold' => $streamerBonusGold,
            ];
        }

        return [
            'streamer' => null,
            'bonus_gold' => 0,
        ];
    }

    /**
     * Calculate total gold with all bonuses
     */
    protected function calculateGoldAmount($amount, $goldRate, $factionBonusPercentage, $promoBonusGold, $streamerBonusGold)
    {
        $baseGold = floor($amount / $goldRate);
        $factionBonusGold = floor($baseGold * ($factionBonusPercentage / 100));

        return $baseGold + $factionBonusGold + $promoBonusGold + $streamerBonusGold;
    }

    /**
     * Process streamer reward when payment is successful
     */
    protected function processStreamerReward($streamerCode, $transactionAmount, $goldRate)
    {
        if (empty($streamerCode)) {
            return;
        }

        $streamer = Streamer::where('code', $streamerCode)
            ->where('is_active', true)
            ->first();

        if (! $streamer) {
            return;
        }

        $baseGold = floor($transactionAmount / $goldRate);
        $streamerReward = $streamer->calculateReward($baseGold);

        if ($streamerReward > 0) {
            $streamer->addGoldReward($streamerReward);
        }
    }

    /**
     * Validate promo or streamer code.
     */
    public function validatePromo(ValidatePromoRequest $request): JsonResponse
    {
        $result = $this->validatePromo->handle(
            code: $request->getCode(),
            amount: $request->getAmount(),
            userId: auth()->id(),
        );

        if (! $result->valid) {
            return BaseResource::makeEnvelope(
                resource: null,
                success: false,
                error: $result->message,
            );
        }

        $data = ['type' => $result->type, 'message' => $result->message];

        if ($result->type === 'promo' && $result->promo) {
            $data['promo'] = $result->promo;
        }

        if ($result->type === 'streamer' && $result->streamer) {
            $data['streamer'] = $result->streamer;
        }

        return BaseResource::makeEnvelope(resource: $data);
    }

    /**
     * Validate streamer code.
     */
    public function validateStreamerCode(ValidateStreamerCodeRequest $request): JsonResponse
    {
        $result = $this->validateStreamerCode->handle($request->getCode());

        if (! $result['valid']) {
            return BaseResource::makeEnvelope(
                resource: null,
                success: false,
                error: $result['message'],
            );
        }

        return BaseResource::makeEnvelope(resource: [
            'message' => $result['message'],
            'streamer' => $result['streamer'],
        ]);
    }

    /**
     * Fetch auto promo for given amount.
     */
    public function fetchAutoPromos(FetchAutoPromoRequest $request): JsonResponse
    {
        $goldRate = $this->settingsRepository->getActiveGoldRate();

        $autoPromo = $this->getAutoPromo->handle(
            amount: $request->getAmount(),
            userId: auth()->id(),
            goldRate: $goldRate,
        );

        if (! $autoPromo) {
            return BaseResource::makeEnvelope(resource: [
                'auto_promo' => null,
                'message' => '',
            ]);
        }

        $promo = $autoPromo->promo;
        $usageLimit = $promo->usage_limit;
        $remainingSlots = $usageLimit ? ($usageLimit - $promo->usage_count) : null;

        $message = 'Add On Promo +'.(int) $promo->discount_value.'% Bonus'
            .($usageLimit ? ' ('.$remainingSlots.'/'.$usageLimit.' Available)' : '');

        return BaseResource::makeEnvelope(resource: [
            'auto_promo' => [
                'id' => $promo->id,
                'code' => $promo->code,
                'name' => $promo->name,
                'discount_type' => $promo->discount_type,
                'discount_value' => (float) $promo->discount_value,
                'discount_amount' => 0,
                'bonus_gold' => $autoPromo->bonusGold,
                'is_auto' => true,
                'usage_count' => $promo->usage_count,
                'usage_limit' => $promo->usage_limit,
                'remaining_slots' => $remainingSlots,
            ],
            'message' => $message,
        ]);
    }
}
