<?php

namespace App\Http\Controllers\Frontend\Event;

use App\Http\Controllers\Controller;
use App\Models\Event;
use App\Models\EventClaim;
use App\Services\Api\API;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;

class EventController extends Controller
{
    protected API $api;

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

    /**
     * Display active events and user's claim status
     *
     * OPTIMIZED: Fixed N+1 query problem by:
     * 1. Caching registration count (single query)
     * 2. Eager loading all user claims in one query
     * 3. Computing eligibility without redundant DB calls
     */
    public function index()
    {
        $user = Auth::user();
        $userId = $user->ID;

        // Get all active events (single query)
        $activeEvents = Event::getActiveEvents();

        // Get event IDs for batch query
        $eventIds = $activeEvents->pluck('id')->toArray();

        // OPTIMIZATION: Get registration count once (was being called per-event)
        $currentRegistrationCount = Event::getCurrentRegistrationCount();

        // OPTIMIZATION: Load all user claims in single query (fixes N+1)
        $userClaims = EventClaim::where('user_id', $userId)
            ->whereIn('event_id', $eventIds)
            ->get()
            ->keyBy('event_id');

        // Process events with cached data (no additional queries)
        $eventsData = [];
        foreach ($activeEvents as $event) {
            // Use cached registration count instead of calling isMilestoneReached() per event
            $milestoneReached = $currentRegistrationCount >= $event->milestone;
            $isEligible = $milestoneReached;

            // Use pre-loaded claims (no query per event)
            $claim = $userClaims->get($event->id);
            $isClaimed = $claim && $claim->claimed_at !== null;

            $eventsData[] = [
                'id' => $event->id,
                'name' => $event->name,
                'type' => $event->type,
                'milestone' => $event->milestone,
                'item_name' => $event->item_name,
                'item_id' => $event->item_id,
                'item_count' => $event->item_count,
                'min_level' => $event->min_level ?? 1,
                'is_eligible' => $isEligible,
                'is_claimed' => $isClaimed,
                'claimed_at' => $isClaimed ? $claim->claimed_at->format('Y-m-d H:i') : null,
                'claimed_character' => $isClaimed ? $claim->role_name : null,
                'milestone_reached' => $milestoneReached,
                'progress' => min(100, round(($currentRegistrationCount / $event->milestone) * 100)),
                'start_date' => $event->start_date?->format('Y-m-d H:i'),
                'end_date' => $event->end_date?->format('Y-m-d H:i'),
            ];
        }

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

        return Inertia::render('frontend/event/index', [
            'events' => $eventsData,
            'currentRegistrationCount' => $currentRegistrationCount,
            'userCharacters' => $userCharacters,
        ]);
    }

    /**
     * Claim all available rewards
     */
    public function claimAll(Request $request)
    {
        $request->validate([
            'role_id' => 'required|integer',
        ]);

        $user = Auth::user();
        $userId = $user->ID;
        $roleId = $request->input('role_id');
        $ipAddress = $request->ip();

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

        // Get active events
        $activeEvents = Event::getActiveEvents();
        \Log::info('Claim All - User ID: '.$userId.', IP: '.$ipAddress.', Active events count: '.$activeEvents->count());

        $claimedCount = 0;
        $failedCount = 0;
        $ipBlockedCount = 0;

        // Get character name and level from API
        $roleName = 'Unknown';
        $roleLevel = 0;
        try {
            $roleBase = $this->api->getRoleBase($roleId);
            if ($roleBase) {
                $roleName = $roleBase['name'] ?? 'Unknown';
            }

            $roleStatus = $this->api->getRoleStatus($roleId);
            if ($roleStatus) {
                $roleLevel = $roleStatus['level'] ?? 0;
            }
        } catch (\Exception $e) {
            \Log::error('Failed to get role info for event claim all: '.$e->getMessage());
        }

        foreach ($activeEvents as $event) {
            // Check if user is eligible and not already claimed
            $isEligible = $event->isUserEligible($userId);
            $hasClaimed = EventClaim::hasUserClaimed($userId, $event->id);
            $hasIpClaimed = EventClaim::hasIpClaimedEvent($ipAddress, $event->id);
            \Log::info("Event {$event->id}: Eligible={$isEligible}, Claimed={$hasClaimed}, IP Claimed={$hasIpClaimed}");

            if (! $isEligible) {
                continue;
            }

            if ($hasClaimed) {
                continue;
            }

            // Check if IP has already claimed this event
            if ($hasIpClaimed) {
                $ipBlockedCount++;
                \Log::warning("IP {$ipAddress} already claimed event {$event->id}");

                continue;
            }

            // Check character level requirement
            if ($roleLevel < ($event->min_level ?? 1)) {
                // Skip if level is too low
                continue;
            }

            // Send item via in-game mail
            $mailSent = false;
            try {
                $item = [
                    'id' => $event->item_id,
                    'pos' => 0,
                    'count' => $event->item_count,
                    'max_count' => $event->item_count,
                    'data' => $event->octet ?? '',
                    'proctype' => 0,
                    'expire_date' => 0,
                    'guid1' => 0,
                    'guid2' => 0,
                    'mask' => $event->mask ?? 1,
                ];

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

                $mailSent = $result !== false;
                \Log::info("Event {$event->id}: Mail sent=".($mailSent ? 'yes' : 'no'));
            } catch (\Exception $e) {
                \Log::error('Failed to send event reward mail for event '.$event->id.': '.$e->getMessage());
                $failedCount++;

                continue;
            }

            // Record the claim with IP address
            EventClaim::updateOrCreate(
                [
                    'event_id' => $event->id,
                    'user_id' => $userId,
                ],
                [
                    'role_id' => $roleId,
                    'role_name' => $roleName,
                    'claimed_at' => now(),
                    'mail_sent' => $mailSent,
                    'ip_address' => $ipAddress,
                ]
            );

            $claimedCount++;
        }

        if ($claimedCount > 0) {
            $message = "Successfully claimed {$claimedCount} reward(s)!";
            if ($failedCount > 0) {
                $message .= " Failed to claim {$failedCount} reward(s).";
            }
            if ($ipBlockedCount > 0) {
                $message .= " {$ipBlockedCount} reward(s) already claimed from this IP.";
            }
            \Log::info('Claim All success: '.$message);

            return redirect()->back()->with('success', $message);
        } else {
            if ($ipBlockedCount > 0) {
                return redirect()->back()->withErrors(['error' => 'This reward has already been claimed from your IP address. One IP can only claim once per event.']);
            }
            \Log::info('Claim All: No eligible rewards');

            return redirect()->back()->with('info', 'No eligible rewards available to claim.');
        }
    }

    /**
     * Claim reward for an event
     */
    public function claim(Request $request)
    {
        $request->validate([
            'event_id' => 'required|integer|exists:events,id',
            'role_id' => 'required|integer',
        ]);

        $user = Auth::user();
        $userId = $user->ID;
        $eventId = $request->input('event_id');
        $roleId = $request->input('role_id');
        $ipAddress = $request->ip();

        // Get the event
        $event = Event::find($eventId);
        if (! $event || ! $event->is_active) {
            return redirect()->back()->withErrors(['error' => 'Event not found or not active.']);
        }

        // Check if user is eligible
        if (! $event->isUserEligible($userId)) {
            return redirect()->back()->withErrors(['error' => 'You are not eligible for this event reward.']);
        }

        // Check if already claimed by user
        if (EventClaim::hasUserClaimed($userId, $eventId)) {
            return redirect()->back()->withErrors(['error' => 'You have already claimed this reward.']);
        }

        // Check if IP has already claimed this event
        if (EventClaim::hasIpClaimedEvent($ipAddress, $eventId)) {
            \Log::warning("IP {$ipAddress} attempted to claim event {$eventId} again (user: {$userId})");

            return redirect()->back()->withErrors(['error' => 'This reward has already been claimed from your IP address. One IP can only claim once per event.']);
        }

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

        // Get character name and level from API
        $roleName = 'Unknown';
        $roleLevel = 0;
        try {
            $roleBase = $this->api->getRoleBase($roleId);
            if ($roleBase) {
                $roleName = $roleBase['name'] ?? 'Unknown';
            }

            $roleStatus = $this->api->getRoleStatus($roleId);
            if ($roleStatus) {
                $roleLevel = $roleStatus['level'] ?? 0;
            }
        } catch (\Exception $e) {
            \Log::error('Failed to get role info for event claim: '.$e->getMessage());
        }

        // Check character level requirement
        if ($roleLevel < ($event->min_level ?? 1)) {
            return redirect()->back()->withErrors(['error' => "Character level {$roleLevel} is below the minimum requirement of level {$event->min_level}."]);
        }

        // Send item via in-game mail
        $mailSent = false;
        try {
            $item = [
                'id' => $event->item_id,
                'pos' => 0,
                'count' => $event->item_count,
                'max_count' => $event->item_count,
                'data' => $event->octet ?? '',
                'proctype' => 0,
                'expire_date' => 0,
                'guid1' => 0,
                'guid2' => 0,
                'mask' => $event->mask ?? 1,
            ];

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

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

        // Record the claim with IP address
        EventClaim::updateOrCreate(
            [
                'event_id' => $eventId,
                'user_id' => $userId,
            ],
            [
                'role_id' => $roleId,
                'role_name' => $roleName,
                'claimed_at' => now(),
                'mail_sent' => $mailSent,
                'ip_address' => $ipAddress,
            ]
        );

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

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

            if (! isset($roles['roles']) || empty($roles['roles'])) {
                return [];
            }

            $characters = [];
            foreach ($roles['roles'] as $role) {
                if (isset($role['id']) && $role['id'] > 0) {
                    $level = 1;
                    try {
                        $roleStatus = $this->api->getRoleStatus($role['id']);
                        if ($roleStatus && isset($roleStatus['level'])) {
                            $level = $roleStatus['level'];
                        }
                    } catch (\Exception $e) {
                        // Keep default level 1 if fetch fails
                    }

                    $characters[] = [
                        'id' => $role['id'],
                        'name' => $role['name'] ?? 'Unknown',
                        'level' => $level,
                    ];
                }
            }

            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);
            if (! isset($roles['roles'])) {
                return false;
            }

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

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