<?php

namespace App\Http\Controllers\Frontend\TopSpender;

use App\Http\Controllers\Controller;
use App\Models\TopspenderCharacterCache;
use App\Models\TopspenderClaim;
use App\Models\TopspenderReward;
use App\Services\Api\API;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;

class TopSpenderController extends Controller
{
    protected API $api;

    public function __construct(API $api)
    {
        $this->api = $api;
    }

    /**
     * Display top spenders from all payment gateways (filtered by month/year + previous month with rewards)
     */
    public function index(Request $request)
    {
        $search = $request->input('search', '');
        $perPage = $request->input('per_page', 10);

        // Get filter month and year (default to current month/year)
        $filterMonth = $request->input('month', now()->month);
        $filterYear = $request->input('year', now()->year);

        // Validate month and year
        $filterMonth = (int) $filterMonth;
        $filterYear = (int) $filterYear;

        if ($filterMonth < 1 || $filterMonth > 12) {
            $filterMonth = now()->month;
        }
        if ($filterYear < 2020 || $filterYear > now()->year + 1) {
            $filterYear = now()->year;
        }

        // Get top spenders for selected month/year
        $topSpenders = $this->getTopSpenders($search, $perPage, $filterMonth, $filterYear);

        // Get previous month for reward claiming
        $prevMonth = now()->subMonth()->month;
        $prevYear = now()->subMonth()->year;

        // Get previous month top 3 spenders
        $previousMonthTop3 = $this->getPreviousMonthTop3($prevMonth, $prevYear);

        // Check if rewards exist for previous month
        $hasRewardsConfigured = TopspenderReward::hasRewardsForPeriod($prevMonth, $prevYear);

        // Get current user's claim status and eligibility
        $user = Auth::user();
        $claimData = $this->getUserClaimData($user->ID, $prevMonth, $prevYear, $previousMonthTop3);

        // Get user's characters for claim modal
        $userCharacters = $this->getUserCharacters($user->ID);

        // Generate month options for filter (last 12 months + current + next)
        $monthOptions = $this->getMonthOptions();

        return Inertia::render('frontend/topspender/index', [
            'topSpenders' => $topSpenders,
            'previousMonthTop3' => $previousMonthTop3,
            'hasRewardsConfigured' => $hasRewardsConfigured,
            'claimData' => $claimData,
            'userCharacters' => $userCharacters,
            'filters' => [
                'search' => $search,
                'per_page' => $perPage,
                'month' => $filterMonth,
                'year' => $filterYear,
            ],
            'monthOptions' => $monthOptions,
            'currentMonth' => now()->format('F'),
            'currentYear' => now()->year,
            'previousMonth' => now()->subMonth()->format('F'),
            'previousYear' => $prevYear,
        ]);
    }

    /**
     * Generate month options for filter (last 12 months + current)
     */
    private function getMonthOptions(): array
    {
        $options = [];
        $now = now();

        // Generate options for last 12 months + current month
        for ($i = 11; $i >= 0; $i--) {
            $date = $now->copy()->subMonths($i);
            $options[] = [
                'value' => $date->month,
                'label' => $date->format('F'),
                'year' => $date->year,
            ];
        }

        return $options;
    }

    /**
     * Claim reward for top spender
     */
    public function claimReward(Request $request)
    {
        $request->validate([
            'role_id' => 'required|integer',
        ]);

        $user = Auth::user();
        $prevMonth = now()->subMonth()->month;
        $prevYear = now()->subMonth()->year;

        // Get previous month top 3
        $previousMonthTop3 = $this->getPreviousMonthTop3($prevMonth, $prevYear);

        // Check if user is in top 3
        $userRank = null;
        foreach ($previousMonthTop3 as $spender) {
            if ($spender['user_id'] === $user->ID) {
                $userRank = $spender['rank'];
                break;
            }
        }

        if (! $userRank) {
            return back()->withErrors(['error' => 'You are not eligible for a reward.']);
        }

        // Check if already claimed
        if (TopspenderClaim::hasUserClaimed($user->ID, $prevMonth, $prevYear)) {
            return back()->withErrors(['error' => 'You have already claimed your reward.']);
        }

        // Get reward for this rank (only active rewards can be claimed)
        $reward = TopspenderReward::getRewardForRank($prevMonth, $prevYear, $userRank);
        if (! $reward) {
            return back()->withErrors(['error' => 'No active reward configured for your rank.']);
        }

        // Verify character belongs to user
        $roleId = $request->input('role_id');
        $roleBelongsToUser = $this->verifyCharacterOwnership($user->ID, $roleId);
        if (! $roleBelongsToUser) {
            return back()->withErrors(['error' => 'Invalid character selected.']);
        }

        // Get character name from API
        $roleName = 'Unknown';
        try {
            $roleBase = $this->api->getRoleBase($roleId);
            if ($roleBase && isset($roleBase['name'])) {
                $roleName = $roleBase['name'];
            }
        } catch (\Exception $e) {
            \Log::error('Failed to get role name for claim: '.$e->getMessage());
        }

        // Send item via in-game mail using API directly with protect_type
        $mailSent = false;
        try {
            $item = [
                'id' => $reward->item_id,
                'pos' => 0,
                'count' => $reward->item_count,
                'max_count' => 50000,
                'data' => '',
                'proctype' => $reward->protect_type ?? 0, // protect_type from reward config
                'expire_date' => 0,
                'guid1' => 0,
                'guid2' => 0,
                'mask' => 0,
            ];

            $result = $this->api->sendMail(
                $roleId,
                $reward->mail_title,
                $reward->mail_message ?? 'Congratulations! You have won the Top Spender reward!',
                0, // gold
                $item
            );

            $mailSent = $result !== false;
        } catch (\Exception $e) {
            \Log::error('Failed to send reward mail: '.$e->getMessage());
        }

        // Record the claim
        TopspenderClaim::create([
            'user_id' => $user->ID,
            'month' => $prevMonth,
            'year' => $prevYear,
            'rank' => $userRank,
            'role_id' => $roleId,
            'role_name' => $roleName,
            'reward_id' => $reward->id,
            'claimed_at' => now(),
            'mail_sent' => $mailSent,
        ]);

        if ($mailSent) {
            return back()->with('success', 'Reward claimed successfully! Check your in-game mailbox.');
        } else {
            return back()->with('warning', 'Reward recorded but mail delivery may have failed. Please contact admin if you don\'t receive it.');
        }
    }

    /**
     * Get top 3 spenders for previous month (user-based, showing most frequent character)
     */
    private function getPreviousMonthTop3(int $month, int $year): array
    {
        // Union all successful transactions from all gateways
        $ipaymuQuery = DB::table('ipaymu_transactions')
            ->select('user_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year);

        $duitkuQuery = DB::table('duitku_transactions')
            ->select('user_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year);

        $tripayQuery = DB::table('tripay_transactions')
            ->select('user_id', 'amount', 'gold_amount')
            ->where('status', 'paid')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year);

        $pakasirQuery = DB::table('pakasir_transactions')
            ->select('user_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year);

        // Combine all transactions
        $allTransactions = $ipaymuQuery
            ->unionAll($duitkuQuery)
            ->unionAll($tripayQuery)
            ->unionAll($pakasirQuery);

        // Group by user and get top 3
        $results = DB::table(DB::raw("({$allTransactions->toSql()}) as all_transactions"))
            ->mergeBindings($allTransactions)
            ->select(
                'all_transactions.user_id',
                DB::raw('SUM(all_transactions.amount) as total_amount'),
                DB::raw('SUM(all_transactions.gold_amount) as total_gold')
            )
            ->join('users', 'all_transactions.user_id', '=', 'users.ID')
            ->groupBy('all_transactions.user_id')
            ->orderBy('total_amount', 'desc')
            ->limit(3)
            ->get();

        $top3 = [];
        $rank = 1;
        foreach ($results as $item) {
            $user = DB::table('users')
                ->select('ID', 'name', 'truename')
                ->where('ID', $item->user_id)
                ->first();

            // Get most frequently used character for top-ups
            $characterData = $this->getMostFrequentTopupCharacter($item->user_id, $month, $year);

            // Check if reward exists for this rank
            $reward = TopspenderReward::getRewardForRank($month, $year, $rank);

            $top3[] = [
                'rank' => $rank,
                'user_id' => $item->user_id,
                'character_name' => $characterData['name'],
                'character_class' => $characterData['class'],
                'truename' => $user->truename ?? '-',
                'total_amount' => (int) $item->total_amount,
                'total_gold' => (int) $item->total_gold,
                'has_reward' => $reward !== null,
                'reward' => $reward ? [
                    'item_name' => $reward->item_name,
                    'item_count' => $reward->item_count,
                ] : null,
            ];
            $rank++;
        }

        return $top3;
    }

    /**
     * Get user's claim data
     */
    private function getUserClaimData(int $userId, int $month, int $year, array $top3): array
    {
        // FIRST: Check if user has already claimed (regardless of current top 3 status)
        $claim = TopspenderClaim::getUserClaim($userId, $month, $year);
        if ($claim) {
            return [
                'eligible' => true,
                'claimed' => true,
                'claimed_at' => $claim->claimed_at->format('Y-m-d H:i'),
                'character' => $claim->role_name,
                'rank' => $claim->rank,
                'reward' => null,
            ];
        }

        // Check if user is in top 3
        $userRank = null;
        foreach ($top3 as $spender) {
            if ($spender['user_id'] === $userId) {
                $userRank = $spender['rank'];
                break;
            }
        }

        if (! $userRank) {
            return [
                'eligible' => false,
                'claimed' => false,
                'rank' => null,
                'reward' => null,
            ];
        }

        // Get reward for this rank
        $reward = TopspenderReward::getRewardForRank($month, $year, $userRank);

        return [
            'eligible' => true,
            'claimed' => false,
            'rank' => $userRank,
            'reward' => $reward ? [
                'item_name' => $reward->item_name,
                'item_count' => $reward->item_count,
            ] : null,
        ];
    }

    /**
     * Get user's characters
     */
    private function getUserCharacters(int $userId): array
    {
        try {
            $roles = $this->api->getRoles($userId);

            // API returns 'roles' key, not 'users'
            if (! isset($roles['roles']) || empty($roles['roles'])) {
                return [];
            }

            $characters = [];
            foreach ($roles['roles'] as $role) {
                // API returns 'id' not 'roleid'
                if (isset($role['id']) && $role['id'] > 0) {
                    $characters[] = [
                        'id' => $role['id'],
                        'name' => $role['name'] ?? 'Unknown',
                    ];
                }
            }

            return $characters;
        } catch (\Exception $e) {
            \Log::error('Failed to get user characters: '.$e->getMessage());

            return [];
        }
    }

    /**
     * Verify character belongs to user
     */
    private function verifyCharacterOwnership(int $userId, int $roleId): bool
    {
        try {
            $roles = $this->api->getRoles($userId);
            // API returns 'roles' key, not 'users'
            if (! isset($roles['roles'])) {
                return false;
            }

            foreach ($roles['roles'] as $role) {
                // API returns 'id' not 'roleid'
                if (isset($role['id']) && $role['id'] == $roleId) {
                    return true;
                }
            }

            return false;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Get top spenders from all payment gateways (filtered by month/year)
     * Groups by user_id but displays the character most frequently used for top-ups
     */
    private function getTopSpenders($search = '', $perPage = 10, $month = null, $year = null)
    {
        // Use provided month/year or default to current
        $currentMonth = $month ?? now()->month;
        $currentYear = $year ?? now()->year;

        // Union all successful transactions from all gateways (current month only)
        $ipaymuQuery = DB::table('ipaymu_transactions')
            ->select('user_id', 'character_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $currentMonth)
            ->whereYear('created_at', $currentYear);

        $duitkuQuery = DB::table('duitku_transactions')
            ->select('user_id', 'character_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $currentMonth)
            ->whereYear('created_at', $currentYear);

        $tripayQuery = DB::table('tripay_transactions')
            ->select('user_id', 'character_id', 'amount', 'gold_amount')
            ->where('status', 'paid')
            ->whereMonth('created_at', $currentMonth)
            ->whereYear('created_at', $currentYear);

        $pakasirQuery = DB::table('pakasir_transactions')
            ->select('user_id', 'character_id', 'amount', 'gold_amount')
            ->where('status', 'success')
            ->whereMonth('created_at', $currentMonth)
            ->whereYear('created_at', $currentYear);

        // Combine all transactions
        $allTransactions = $ipaymuQuery
            ->unionAll($duitkuQuery)
            ->unionAll($tripayQuery)
            ->unionAll($pakasirQuery);

        // Group by user and calculate totals
        $query = DB::table(DB::raw("({$allTransactions->toSql()}) as all_transactions"))
            ->mergeBindings($allTransactions)
            ->select(
                'all_transactions.user_id',
                DB::raw('SUM(all_transactions.amount) as total_amount'),
                DB::raw('SUM(all_transactions.gold_amount) as total_gold')
            )
            ->join('users', 'all_transactions.user_id', '=', 'users.ID')
            ->groupBy('all_transactions.user_id');

        // Add search filter
        if (! empty($search)) {
            $query->where(function ($q) use ($search) {
                $q->where('users.truename', 'like', "%{$search}%")
                    ->orWhere('users.name', 'like', "%{$search}%")
                    ->orWhere('users.ID', 'like', "%{$search}%");
            });
        }

        // Order by total amount descending
        $query->orderBy('total_amount', 'desc');

        // Get paginated results
        $results = $query->paginate($perPage);

        // Add user details and ranking
        $rank = ($results->currentPage() - 1) * $perPage + 1;
        $results->getCollection()->transform(function ($item) use (&$rank, $currentMonth, $currentYear) {
            $user = DB::table('users')
                ->select('ID', 'name', 'truename')
                ->where('ID', $item->user_id)
                ->first();

            // Get the most frequently used character for top-ups
            $characterData = $this->getMostFrequentTopupCharacter($item->user_id, $currentMonth, $currentYear);

            return (object) [
                'rank' => $rank++,
                'user_id' => $item->user_id,
                'character_name' => $characterData['name'],
                'character_class' => $characterData['class'],
                'truename' => $user->truename ?? '-',
                'total_amount' => (int) $item->total_amount,
                'total_gold' => (int) $item->total_gold,
            ];
        });

        return $results;
    }

    /**
     * Get the character most frequently used for top-ups by counting transactions
     */
    private function getMostFrequentTopupCharacter(int $userId, int $month, int $year): array
    {
        // Count transactions and sum amounts per character_id for this user
        $ipaymuChars = DB::table('ipaymu_transactions')
            ->select('character_id', DB::raw('COUNT(*) as transaction_count'), DB::raw('SUM(amount) as total_amount'))
            ->where('user_id', $userId)
            ->where('status', 'success')
            ->whereNotNull('character_id')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year)
            ->groupBy('character_id');

        $duitkuChars = DB::table('duitku_transactions')
            ->select('character_id', DB::raw('COUNT(*) as transaction_count'), DB::raw('SUM(amount) as total_amount'))
            ->where('user_id', $userId)
            ->where('status', 'success')
            ->whereNotNull('character_id')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year)
            ->groupBy('character_id');

        $tripayChars = DB::table('tripay_transactions')
            ->select('character_id', DB::raw('COUNT(*) as transaction_count'), DB::raw('SUM(amount) as total_amount'))
            ->where('user_id', $userId)
            ->where('status', 'paid')
            ->whereNotNull('character_id')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year)
            ->groupBy('character_id');

        $pakasirChars = DB::table('pakasir_transactions')
            ->select('character_id', DB::raw('COUNT(*) as transaction_count'), DB::raw('SUM(amount) as total_amount'))
            ->where('user_id', $userId)
            ->where('status', 'success')
            ->whereNotNull('character_id')
            ->whereMonth('created_at', $month)
            ->whereYear('created_at', $year)
            ->groupBy('character_id');

        // Combine and find the character with highest total amount
        $allChars = $ipaymuChars
            ->unionAll($duitkuChars)
            ->unionAll($tripayChars)
            ->unionAll($pakasirChars);

        $mostUsedChar = DB::table(DB::raw("({$allChars->toSql()}) as char_counts"))
            ->mergeBindings($allChars)
            ->select('character_id', DB::raw('SUM(transaction_count) as total_count'), DB::raw('SUM(total_amount) as total_amount'))
            ->groupBy('character_id')
            ->orderBy('total_amount', 'desc')
            ->orderBy('total_count', 'desc')
            ->first();

        if (! $mostUsedChar || ! $mostUsedChar->character_id) {
            // Fallback: get user's first character
            return $this->getMostUsedCharacter($userId, $month, $year);
        }

        $characterId = $mostUsedChar->character_id;

        // Get character name and class from API with caching
        try {
            $roleBase = $this->api->getRoleBase($characterId);
            if ($roleBase && isset($roleBase['name'])) {
                TopspenderCharacterCache::storeCache(
                    $userId,
                    $month,
                    $year,
                    $roleBase['name'],
                    $roleBase['cls'] ?? null
                );

                return [
                    'name' => $roleBase['name'],
                    'class' => $roleBase['cls'] ?? null,
                ];
            }
        } catch (\Exception $e) {
            // API failed — try cache fallback
            $cached = TopspenderCharacterCache::getCachedCharacter($userId, $month, $year);
            if ($cached) {
                return [
                    'name' => $cached->character_name,
                    'class' => $cached->character_class,
                ];
            }
        }

        // Final fallback
        return $this->getMostUsedCharacter($userId, $month, $year);
    }

    /**
     * Get the first active character for a user
     * Note: character_id in transaction tables is typically NULL, so we fetch directly from API
     */
    private function getMostUsedCharacter(int $userId, int $month, int $year): array
    {
        // Try to get character from API with caching
        try {
            $roles = $this->api->getRoles($userId);

            if (isset($roles['roles']) && ! empty($roles['roles'])) {
                // Get first active character
                foreach ($roles['roles'] as $role) {
                    if (isset($role['id']) && $role['id'] > 0) {
                        $characterName = $role['name'] ?? 'Unknown';
                        $characterClass = null;

                        // getRoles doesn't return class, so we need to fetch it from getRoleBase
                        try {
                            $roleBase = $this->api->getRoleBase($role['id']);
                            if ($roleBase && isset($roleBase['cls'])) {
                                $characterClass = $roleBase['cls'];
                            }
                        } catch (\Exception $e) {
                            \Log::debug('Failed to get role base for character: '.$role['id']);
                        }

                        // Store to cache
                        TopspenderCharacterCache::storeCache(
                            $userId,
                            $month,
                            $year,
                            $characterName,
                            $characterClass
                        );

                        return [
                            'name' => $characterName,
                            'class' => $characterClass,
                        ];
                    }
                }
            }
        } catch (\Exception $e) {
            // API failed — try cache fallback
            $cached = TopspenderCharacterCache::getCachedCharacter($userId, $month, $year);
            if ($cached) {
                return [
                    'name' => $cached->character_name,
                    'class' => $cached->character_class,
                ];
            }
        }

        // Fallback: get truename from users table
        $user = DB::table('users')->where('ID', $userId)->first();

        return [
            'name' => $user->truename ?? 'Unknown',
            'class' => null,
        ];
    }
}
