<?php

namespace App\Http\Controllers\Admin\Reward;

use App\Http\Controllers\Controller;
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 Illuminate\Support\Facades\Http;
use Inertia\Inertia;

class TopSpenderRewardController extends Controller
{
    protected API $api;

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

    /**
     * Send rewards to all top spenders for the selected period
     */
    public function sendRewards(Request $request)
    {
        $month = $request->input('month', now()->subMonth()->month);
        $year = $request->input('year', now()->subMonth()->year);

        if (now()->month === 1 && ! $request->has('month')) {
            $month = 12;
            $year = now()->year - 1;
        }

        $top3 = $this->getTopSpendersForPeriod($month, $year);

        if (empty($top3)) {
            return back()->with('error', 'No top spenders found for this period.');
        }

        $rewards = TopspenderReward::where('month', $month)
            ->where('year', $year)
            ->where('is_active', true)
            ->get()
            ->keyBy('rank');

        if ($rewards->isEmpty()) {
            return back()->with('error', 'No active rewards configured for this period.');
        }

        $results = [];

        foreach ($top3 as $spender) {
            $rank = $spender['rank'];
            $userId = $spender['user_id'];

            if (TopspenderClaim::hasUserClaimed($userId, $month, $year)) {
                $results[] = [
                    'user_id' => $userId,
                    'rank' => $rank,
                    'status' => 'skipped',
                    'message' => 'Already claimed',
                ];

                continue;
            }

            $reward = $rewards->get($rank);
            if (! $reward) {
                $results[] = [
                    'user_id' => $userId,
                    'rank' => $rank,
                    'status' => 'skipped',
                    'message' => 'No reward for this rank',
                ];

                continue;
            }

            $roleId = $spender['role_id'];
            $roleName = $spender['role_name'];

            $mailSent = false;
            try {
                $item = [
                    'id' => $reward->item_id,
                    'pos' => 0,
                    'count' => $reward->item_count,
                    'max_count' => 50000,
                    'data' => '',
                    'proctype' => $reward->protect_type ?? 0,
                    'expire_date' => 0,
                    'guid1' => 0,
                    'guid2' => 0,
                    'mask' => $reward->mask ?? 0,
                ];

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

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

            TopspenderClaim::create([
                'user_id' => $userId,
                'month' => $month,
                'year' => $year,
                'rank' => $rank,
                'role_id' => $roleId,
                'role_name' => $roleName,
                'reward_id' => $reward->id,
                'claimed_at' => now(),
                'mail_sent' => $mailSent,
            ]);

            $results[] = [
                'user_id' => $userId,
                'rank' => $rank,
                'status' => $mailSent ? 'sent' : 'failed',
                'message' => $mailSent ? 'Mail sent successfully' : 'Mail delivery may have failed',
            ];
        }

        $sent = collect($results)->where('status', 'sent')->count();
        $failed = collect($results)->where('status', 'failed')->count();
        $skipped = collect($results)->where('status', 'skipped')->count();

        $message = "Rewards processed: {$sent} sent, {$failed} failed, {$skipped} skipped.";

        $this->sendDiscordNotification($month, $year, $top3, $rewards, $results);

        return back()->with('success', $message);
    }

    /**
     * Send Discord webhook notification when rewards are sent
     */
    private function sendDiscordNotification(int $month, int $year, array $top3, $rewards, array $results): void
    {
        $webhookUrl = DB::table('settings')
            ->where('key', 'topspender_discord_webhook_url')
            ->first()?->value;

        if (empty($webhookUrl)) {
            return;
        }

        $monthName = date('F', mktime(0, 0, 0, $month, 1));

        $appName = config('app.name', 'Perfect World');

        $rankLines = [];
        foreach ($top3 as $spender) {
            $rank = $spender['rank'];
            $reward = $rewards->get($rank);
            $rankEmoji = $rank === 1 ? '🥇' : ($rank === 2 ? '🥈' : '🥉');
            $rankLines[] = "{$rankEmoji} Rank #{$rank} - {$spender['role_name']} ({$reward->item_name} x{$reward->item_count})";
        }

        $description = "Top spender rewards for {$monthName} {$year} have been successfully sent to the in-game mailbox for ".count($top3)." winners\n\n".implode("\n", $rankLines);

        $payload = [
            'username' => 'Top Spender Reward',
            'avatar_url' => asset('img/logo.webp'),
            'embeds' => [
                [
                    'title' => "🏆 Top Spender Rewards - {$monthName} {$year}",
                    'description' => $description,
                    'color' => 15105570,
                    'footer' => [
                        'text' => "Perfect World {$appName} • ".now()->format('d M Y H:i'),
                    ],
                    'timestamp' => now()->toISOString(),
                ],
            ],
        ];

        try {
            Http::post($webhookUrl, $payload);
        } catch (\Exception $e) {
            \Log::error('Failed to send Top Spender Discord notification: '.$e->getMessage());
        }
    }

    /**
     * Show settings page
     */
    public function settings()
    {
        $webhookUrl = DB::table('settings')
            ->where('key', 'topspender_discord_webhook_url')
            ->first()?->value ?? '';

        return Inertia::render('admin/reward/topspender/pages/TopSpenderSettingsPage', [
            'settings' => [
                'discord_webhook_url' => $webhookUrl,
            ],
        ]);
    }

    /**
     * Update settings
     */
    public function updateSettings(Request $request)
    {
        $validated = $request->validate([
            'discord_webhook_url' => 'nullable|string|url|max:500',
        ]);

        DB::table('settings')->updateOrInsert(
            ['key' => 'topspender_discord_webhook_url'],
            ['value' => $validated['discord_webhook_url'] ?? '', 'updated_at' => now()]
        );

        return back()->with('success', 'Top Spender settings updated successfully.');
    }

    /**
     * Get top 3 spenders for a given period with their most used character
     */
    private function getTopSpendersForPeriod(int $month, int $year): array
    {
        $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);

        $allTransactions = $ipaymuQuery
            ->unionAll($duitkuQuery)
            ->unionAll($tripayQuery)
            ->unionAll($pakasirQuery);

        $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) {
            $characterData = $this->getMostFrequentTopupCharacter($item->user_id, $month, $year);

            $top3[] = [
                'rank' => $rank,
                'user_id' => $item->user_id,
                'role_id' => $characterData['role_id'],
                'role_name' => $characterData['name'],
                'total_amount' => (int) $item->total_amount,
                'total_gold' => (int) $item->total_gold,
            ];
            $rank++;
        }

        return $top3;
    }

    /**
     * Get the most frequently used character for top-ups
     */
    private function getMostFrequentTopupCharacter(int $userId, int $month, int $year): array
    {
        $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');

        $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) {
            try {
                $roleBase = $this->api->getRoleBase($mostUsedChar->character_id);
                if ($roleBase && isset($roleBase['name'])) {
                    return [
                        'role_id' => $mostUsedChar->character_id,
                        'name' => $roleBase['name'],
                    ];
                }
            } catch (\Exception $e) {
                \Log::debug('Failed to get role base: '.$e->getMessage());
            }
        }

        try {
            $roles = $this->api->getRoles($userId);
            if (isset($roles['roles']) && ! empty($roles['roles'])) {
                foreach ($roles['roles'] as $role) {
                    if (isset($role['id']) && $role['id'] > 0) {
                        return [
                            'role_id' => $role['id'],
                            'name' => $role['name'] ?? 'Unknown',
                        ];
                    }
                }
            }
        } catch (\Exception $e) {
            \Log::debug('Failed to get roles: '.$e->getMessage());
        }

        $user = DB::table('users')->where('ID', $userId)->first();

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

    /**
     * Display list of rewards
     */
    public function index(Request $request)
    {
        $month = $request->input('month', now()->month);
        $year = $request->input('year', now()->year);

        $rewards = TopspenderReward::where('month', $month)
            ->where('year', $year)
            ->orderBy('rank')
            ->get()
            ->map(function ($reward) {
                return [
                    'id' => $reward->id,
                    'rank' => $reward->rank,
                    'rank_label' => $reward->rank_label,
                    'item_id' => $reward->item_id,
                    'item_name' => $reward->item_name,
                    'item_count' => $reward->item_count,
                    'protect_type' => $reward->protect_type,
                    'octet' => $reward->octet,
                    'mask' => $reward->mask,
                    'mail_message' => $reward->mail_message,
                    'is_active' => $reward->is_active,
                    'created_at' => $reward->created_at->format('Y-m-d H:i'),
                ];
            });

        // Check which ranks already have rewards
        $existingRanks = $rewards->pluck('rank')->toArray();
        $availableRanks = array_values(array_diff([1, 2, 3], $existingRanks));

        return Inertia::render('admin/reward/topspender/pages/TopSpenderListPage', [
            'rewards' => $rewards,
            'filters' => [
                'month' => (int) $month,
                'year' => (int) $year,
            ],
            'availableRanks' => $availableRanks,
            'months' => $this->getMonthOptions(),
            'years' => $this->getYearOptions(),
        ]);
    }

    /**
     * Show create form
     */
    public function create(Request $request)
    {
        $month = $request->input('month', now()->month);
        $year = $request->input('year', now()->year);

        // Get existing ranks for this period
        $existingRanks = TopspenderReward::where('month', $month)
            ->where('year', $year)
            ->pluck('rank')
            ->toArray();

        $availableRanks = array_values(array_diff([1, 2, 3], $existingRanks));

        if (empty($availableRanks)) {
            return redirect()->route('admin.reward.topspender.index', [
                'month' => $month,
                'year' => $year,
            ])->with('error', 'All ranks already have rewards for this period.');
        }

        return Inertia::render('admin/reward/topspender/pages/TopSpenderCreatePage', [
            'availableRanks' => $availableRanks,
            'filters' => [
                'month' => (int) $month,
                'year' => (int) $year,
            ],
            'months' => $this->getMonthOptions(),
            'years' => $this->getYearOptions(),
            'masks' => $this->getMasksForSelect(),
        ]);
    }

    /**
     * Store new reward
     */
    public function store(Request $request)
    {
        $validated = $request->validate([
            'month' => 'required|integer|min:1|max:12',
            'year' => 'required|integer|min:2020|max:2100',
            'rank' => 'required|integer|in:1,2,3',
            'item_id' => 'required|integer|min:1',
            'item_name' => 'required|string|max:255',
            'item_count' => 'required|integer|min:1',
            'protect_type' => 'nullable|integer|min:0|max:255',
            'octet' => 'nullable|string',
            'mask' => 'nullable|integer|min:0',
            'mail_message' => 'nullable|string|max:1000',
        ]);

        // Check if reward already exists for this rank/period
        $exists = TopspenderReward::where('month', $validated['month'])
            ->where('year', $validated['year'])
            ->where('rank', $validated['rank'])
            ->exists();

        if ($exists) {
            return back()->withErrors(['rank' => 'Reward for this rank already exists.']);
        }

        $validated['created_by'] = Auth::id();

        TopspenderReward::create($validated);

        return redirect()->route('admin.reward.topspender.index', [
            'month' => $validated['month'],
            'year' => $validated['year'],
        ])->with('success', 'Reward created successfully.');
    }

    /**
     * Show edit form
     */
    public function edit($id)
    {
        $reward = TopspenderReward::findOrFail($id);

        return Inertia::render('admin/reward/topspender/pages/TopSpenderEditPage', [
            'reward' => [
                'id' => $reward->id,
                'month' => $reward->month,
                'year' => $reward->year,
                'rank' => $reward->rank,
                'rank_label' => $reward->rank_label,
                'item_id' => $reward->item_id,
                'item_name' => $reward->item_name,
                'item_count' => $reward->item_count,
                'protect_type' => $reward->protect_type,
                'octet' => $reward->octet,
                'mask' => $reward->mask,
                'mail_message' => $reward->mail_message,
            ],
            'months' => $this->getMonthOptions(),
            'years' => $this->getYearOptions(),
            'masks' => $this->getMasksForSelect(),
        ]);
    }

    /**
     * Update reward
     */
    public function update(Request $request, $id)
    {
        $reward = TopspenderReward::findOrFail($id);

        $validated = $request->validate([
            'item_id' => 'required|integer|min:1',
            'item_name' => 'required|string|max:255',
            'item_count' => 'required|integer|min:1',
            'protect_type' => 'nullable|integer|min:0|max:255',
            'octet' => 'nullable|string',
            'mask' => 'nullable|integer|min:0',
            'mail_message' => 'nullable|string|max:1000',
        ]);

        $reward->update($validated);

        return redirect()->route('admin.reward.topspender.index', [
            'month' => $reward->month,
            'year' => $reward->year,
        ])->with('success', 'Reward updated successfully.');
    }

    /**
     * Delete reward
     */
    public function destroy($id)
    {
        $reward = TopspenderReward::findOrFail($id);
        $month = $reward->month;
        $year = $reward->year;

        $reward->delete();

        return redirect()->route('admin.reward.topspender.index', [
            'month' => $month,
            'year' => $year,
        ])->with('success', 'Reward deleted successfully.');
    }

    /**
     * Toggle reward active status
     */
    public function toggleStatus($id)
    {
        $reward = TopspenderReward::findOrFail($id);
        $reward->is_active = ! $reward->is_active;
        $reward->save();

        $status = $reward->is_active ? 'enabled' : 'disabled';

        return redirect()->route('admin.reward.topspender.index', [
            'month' => $reward->month,
            'year' => $reward->year,
        ])->with('success', "Reward {$status} successfully.");
    }

    /**
     * View claim history
     */
    public function claims(Request $request)
    {
        $month = $request->input('month', now()->subMonth()->month);
        $year = $request->input('year', now()->subMonth()->year);

        // Adjust for January (previous month would be December of previous year)
        if (now()->month === 1 && ! $request->has('month')) {
            $month = 12;
            $year = now()->year - 1;
        }

        $claims = TopspenderClaim::with(['user', 'reward'])
            ->where('month', $month)
            ->where('year', $year)
            ->orderBy('rank')
            ->get()
            ->map(function ($claim) {
                return [
                    'id' => $claim->id,
                    'rank' => $claim->rank,
                    'rank_label' => $claim->rank_label,
                    'user_id' => $claim->user_id,
                    'user_name' => $claim->user->name ?? 'Unknown',
                    'role_id' => $claim->role_id,
                    'role_name' => $claim->role_name,
                    'item_name' => $claim->reward->item_name ?? 'N/A',
                    'item_count' => $claim->reward->item_count ?? 0,
                    'mail_sent' => $claim->mail_sent,
                    'claimed_at' => $claim->claimed_at->format('Y-m-d H:i'),
                ];
            });

        return Inertia::render('admin/reward/topspender/pages/TopSpenderClaimsPage', [
            'claims' => $claims,
            'filters' => [
                'month' => (int) $month,
                'year' => (int) $year,
            ],
            'months' => $this->getMonthOptions(),
            'years' => $this->getYearOptions(),
        ]);
    }

    /**
     * Get month options for select
     */
    private function getMonthOptions(): array
    {
        $months = [];
        for ($i = 1; $i <= 12; $i++) {
            $months[] = [
                'value' => $i,
                'label' => date('F', mktime(0, 0, 0, $i, 1)),
            ];
        }

        return $months;
    }

    /**
     * Get year options for select
     */
    private function getYearOptions(): array
    {
        $currentYear = now()->year;
        $years = [];
        for ($i = $currentYear - 1; $i <= $currentYear + 1; $i++) {
            $years[] = [
                'value' => $i,
                'label' => (string) $i,
            ];
        }

        return $years;
    }

    /**
     * Get masks formatted for select dropdown
     */
    private function getMasksForSelect()
    {
        $masks = config('masks');
        $formatted = [];

        foreach ($masks as $key => $value) {
            if (is_array($value)) {
                // Group
                $formatted[] = [
                    'label' => $key,
                    'options' => array_map(function ($k, $v) {
                        return ['value' => $k, 'label' => $v];
                    }, array_keys($value), $value),
                ];
            } else {
                // Single option
                $formatted[] = [
                    'value' => $key,
                    'label' => $value,
                ];
            }
        }

        return $formatted;
    }
}
