<?php

namespace App\Services\Games;

use App\Enums\GameEnum;
use App\Models\HexlingBadge;
use App\Models\HexlingPlay;
use App\Models\HexlingWord;
use Illuminate\Support\Facades\DB;

class HexlingGameService
{
    /**
     * Helper method to find wordling word based on parameters - Only active games
     */
    public function findActiveGame(array $filters)
    {
        $query = HexlingWord::query()
            ->where('status', GameEnum::ACTIVE)
            ->with(['badges' => function ($q) {
                $q->orderBy('badge_order');
            }]);

        if (empty($filters['game_date'])) {
            $query->orderBy('game_date', 'desc');
        } else {
            $query->whereDate('game_date', $filters['game_date']);
        }

        $game = $query->first();

        if (! $game) {
            abort(404, 'Active game not found');
        }

        return $game;
    }

    /**
     * Handle word checking logic for Hexling
     */
    public function checkWord($user, array $data)
    {
        $userWord = strtolower($data['user_input_word']);

        return DB::transaction(function () use ($user, $userWord, $data) {
            $hexlingWord = $this->findActiveGame($data);

            $secretWords = $hexlingWord->secret_words ?? [];
            $totalLevels = $hexlingWord->total_levels;
            $gameTotalPoints = $hexlingWord->total_points;

            $hexlingPlay = HexlingPlay::firstOrCreate(
                [
                    'user_id' => $user->id,
                    'hexling_word_id' => $hexlingWord->id,
                ],
                [
                    'player_game_level' => 1,
                    'earned_points' => 0,
                    'user_submissions' => [],
                    'earned_badge_ids' => [],
                ]
            );

            // Check if game is already completed
            if ($hexlingPlay->completed_at) {
                return [
                    'success' => false,
                    'message' => 'You have already completed this game',
                    'status_code' => 400
                ];
            }

            $isValidWord = $this->isValidHexlingWord($userWord, $hexlingWord->center_letter, $secretWords);
            $isNewWord = ! in_array($userWord, $hexlingPlay->user_submissions ?? []);

            $userSubmissions = $hexlingPlay->user_submissions ?? [];
            $currentEarnedPoints = $hexlingPlay->earned_points;
            $earnedBadgeIds = $hexlingPlay->earned_badge_ids ?? [];

            // Get full badge data for response
            $earnedBadges = $this->getBadgesByIds($earnedBadgeIds);

            // Default response data (invalid or duplicate)
            $responseData = [
                'user_input_word' => $userWord,
                'is_valid_word' => $isValidWord,
                'is_new_word' => $isNewWord,
                'current_level' => $hexlingPlay->player_game_level,
                'total_levels' => $totalLevels,
                'earned_points' => $currentEarnedPoints,
                'total_points' => $gameTotalPoints,
                'found_words_count' => count(array_intersect($secretWords, $userSubmissions)),
                'total_words_count' => count($secretWords),
                'user_submissions' => $userSubmissions,
                'earned_badges' => $earnedBadges,
                'message' => ! $isValidWord
                    ? 'Invalid word! Must contain the center letter and be in the word list.'
                    : 'You have already submitted this word.',
            ];

            if ($isValidWord && $isNewWord) {
                $userSubmissions[] = $userWord;

                // Calculate word points
                $wordPoints = strlen($userWord);
                $newEarnedPoints = $currentEarnedPoints + $wordPoints;

                // Check for NEW badge achievements (returns badge IDs only)
                $newBadgeIds = $this->checkBadgeAchievements($hexlingWord, $earnedBadgeIds, $newEarnedPoints);

                // Merge new badge IDs with existing ones
                $allEarnedBadgeIds = array_merge($earnedBadgeIds, $newBadgeIds);

                // Get full badge data for response
                $allEarnedBadges = $this->getBadgesByIds($allEarnedBadgeIds);
                $newBadges = $this->getBadgesByIds($newBadgeIds);

                // Calculate new level based on number of badges earned
                $newLevel = $this->calculateLevelFromBadges($allEarnedBadgeIds);

                // Game is completed ONLY when user earns total_points
                $isCompleted = $newEarnedPoints >= $gameTotalPoints;
                $completedAt = $isCompleted ? now() : null;

                $hexlingPlay->update([
                    'player_game_level' => $newLevel,
                    'earned_points' => $newEarnedPoints,
                    'user_submissions' => $userSubmissions,
                    'earned_badge_ids' => $allEarnedBadgeIds,
                    'completed_at' => $completedAt,
                ]);

                $responseData = [
                    'user_input_word' => $userWord,
                    'is_valid_word' => true,
                    'is_new_word' => true,
                    'word_points' => $wordPoints,
                    'current_level' => $newLevel,
                    'total_levels' => $totalLevels,
                    'earned_points' => $newEarnedPoints,
                    'total_points' => $gameTotalPoints,
                    'found_words_count' => count(array_intersect($secretWords, $userSubmissions)),
                    'total_words_count' => count($secretWords),
                    'user_submissions' => $userSubmissions,
                    'earned_badges' => $allEarnedBadges,
                    'earned_badges_count' => count($allEarnedBadges),
                    'new_badges' => $newBadges,
                    'is_completed' => $isCompleted,
                    'message' => 'Great! You found a valid word worth '.$wordPoints.' points!',
                ];
            }

            return [
                'success' => true,
                'data' => $responseData
            ];
        });
    }

    public function getGamesList($user, array $filters)
    {
        $query = HexlingWord::query()
            ->select('id', 'game_id', 'root_word', 'center_letter', 'total_levels', 'total_points', 'game_date')
            ->with(['badges' => function ($q) {
                $q->orderBy('badge_order');
            }]);

        $query->where('status', 'active');

        $firstGameDate = HexlingWord::min('game_date');

        if (isset($filters['date'])) {
            $query->whereDate('game_date', $filters['date']);
            $startDate = $endDate = $filters['date'];
            $month = date('Y-m', strtotime($filters['date']));
        } else {
            $month = $filters['month'] ?? date('Y-m');
            $startDate = date('Y-m-01', strtotime($month));
            $endDate = date('Y-m-t', strtotime($month));
            $query->whereBetween('game_date', [$startDate, $endDate]);
        }

        if (! empty($filters['game_id'])) {
            $query->where('game_id', $filters['game_id']);
        }

        $games = $query
            ->orderBy($filters['sort_by'] ?? 'game_date', 'asc')
            ->get();

        $gameIds = $games->pluck('id');
        $userPlays = HexlingPlay::where('user_id', $user->id)
            ->whereIn('hexling_word_id', $gameIds)
            ->get()
            ->keyBy('hexling_word_id');

        $gamesWithStatus = $games->map(function ($game) use ($userPlays, $firstGameDate) {
            $userPlay = $userPlays->get($game->id);
            $status = 'NOT_PLAYED';

            if ($userPlay) {
                $hasSubmissions = ! empty($userPlay->user_submissions);

                if ($userPlay->completed_at) {
                    $status = 'COMPLETED';
                } elseif (! $hasSubmissions) {
                    $status = 'NOT_PLAYED';
                } else {
                    $status = 'IN_PROGRESS';
                }
            }

            $formattedDate = date('Y-m-d', strtotime($game->game_date));
            $interval = (new \DateTime($firstGameDate))->diff(new \DateTime($game->game_date));
            $gameNumber = $interval->days + 1;

            // Get earned badge IDs and full badge data
            $earnedBadgeIds = $userPlay->earned_badge_ids ?? [];
            $earnedBadges = $this->getBadgesByIds($earnedBadgeIds);

            return [
                'id' => $game->id,
                'game_id' => $game->game_id,
                'root_word' => $game->root_word,
                'center_letter' => $game->center_letter,
                'total_levels' => $game->total_levels,
                'total_points' => $game->total_points,
                'game_date' => $game->game_date,
                'formatted_date' => $formattedDate,
                'badges' => $game->badges,
                'user_play_status' => $status,
                'game_number' => $gameNumber,
                'user_stats' => $userPlay ? [
                    'earned_points' => $userPlay->earned_points,
                    'current_level' => $userPlay->player_game_level,
                    'found_words_count' => count($userPlay->user_submissions ?? []),
                    'earned_badges_count' => count($earnedBadges),
                    'earned_badges' => $earnedBadges,
                    'progress_percentage' => $game->total_points > 0
                        ? round(($userPlay->earned_points / $game->total_points) * 100, 2)
                        : 0,
                ] : null,
            ];
        });

        $gamesByDate = $gamesWithStatus->groupBy('formatted_date');

        return [
            'games' => $gamesByDate,
            'month' => $month,
            'start_date' => $startDate,
            'end_date' => $endDate,
            'first_game_date' => $firstGameDate,
        ];
    }

    public function getSubmissions($user, array $filters)
    {
        $query = HexlingWord::query()
            ->select('id', 'root_word', 'center_letter', 'secret_words', 'total_levels', 'total_points', 'game_date')
            ->with(['badges' => function ($q) {
                $q->orderBy('badge_order');
            }]);

        $query->where('status', 'active');

        if (! empty($filters['game_id'])) {
            $query->where('game_id', $filters['game_id']);
        } else {
            if (empty($filters['game_date'])) {
                $query->orderBy('game_date', 'desc');
            } else {
                $query->whereDate('game_date', $filters['game_date']);
            }
        }

        $hexlingWord = $query->firstOrFail();

        $hexlingPlay = HexlingPlay::where('user_id', $user->id)
            ->where('hexling_word_id', $hexlingWord->id)
            ->select('id', 'player_game_level', 'earned_points', 'completed_at', 'user_submissions', 'earned_badge_ids')
            ->first();

        $secretWords = $hexlingWord->secret_words ?? [];
        $userSubmissions = $hexlingPlay->user_submissions ?? [];
        $earnedBadgeIds = $hexlingPlay->earned_badge_ids ?? [];

        $earnedBadges = $this->getBadgesByIds($earnedBadgeIds);

        $foundWords = array_intersect($secretWords, $userSubmissions);
        $earnedPoints = $hexlingPlay->earned_points ?? 0;

        $currentLevel = $this->calculateLevelFromBadges($earnedBadgeIds);
        $upcomingBadges = $this->getUpcomingBadges($hexlingWord, $earnedBadgeIds);
        $upcomingBadge = $upcomingBadges ? $upcomingBadges[0] : null;

        return [
            'hexling_word' => [
                'id' => $hexlingWord->id,
                'root_word' => $hexlingWord->root_word,
                'center_letter' => $hexlingWord->center_letter,
                'total_levels' => $hexlingWord->total_levels,
                'total_points' => $hexlingWord->total_points,
                'game_date' => $hexlingWord->game_date,
                'secret_words_count' => count($secretWords),
                'badges' => $hexlingWord->badges,
            ],
            'hexling_play' => [
                'id' => $hexlingPlay?->id,
                'player_game_level' => $hexlingPlay?->player_game_level,
                'earned_points' => $hexlingPlay?->earned_points,
                'completed_at' => $hexlingPlay?->completed_at,
                'user_submissions' => $hexlingPlay?->user_submissions,
                'earned_badges' => $earnedBadges,
                'upcoming_badges' => $upcomingBadge,
            ],
            'stats' => [
                'found_words_count' => count($foundWords),
                'total_words_count' => count($secretWords),
                'earned_points' => $earnedPoints,
                'total_points' => $hexlingWord->total_points,
                'current_level' => $currentLevel,
                'upcoming_badges' => $upcomingBadge,
                'current_badge_points' => $earnedPoints - collect($earnedBadges)->sum('badge_points'),
                'earned_badges_count' => count($earnedBadges),
                'progress_percentage' => $hexlingWord->total_points > 0
                    ? round(($earnedPoints / $hexlingWord->total_points) * 100, 2)
                    : 0,
            ],
        ];
    }

    public function getGameResult($user, array $filters)
    {
        $query = HexlingWord::query()
            ->with(['badges' => function ($q) {
                $q->orderBy('badge_order');
            }]);

        $query->where('status', GameEnum::ACTIVE);

        if (! empty($filters['game_id'])) {
            $query->where('game_id', $filters['game_id']);
        } else {
            if (empty($filters['game_date'])) {
                $query->orderBy('game_date', 'desc');
            } else {
                $query->whereDate('game_date', $filters['game_date']);
            }
        }

        $hexlingWord = $query->firstOrFail();

        $currentPlay = HexlingPlay::where('user_id', $user->id)
            ->where('hexling_word_id', $hexlingWord->id)
            ->first();

        $playerStats = $this->calculateHexlingPlayerStats($user->id);
        $gameState = $this->getHexlingGameState($currentPlay, $hexlingWord);

        return [
            'gamePoints' => $gameState['gamePoints'],
            'earnPoints' => $gameState['earnPoints'],
            'secretWords' => $currentPlay && $currentPlay->isCompleted() ? $hexlingWord->secret_words : [],
            'guessedWords' => $gameState['guessedWords'],
            'allBadges' => $hexlingWord->badges,
            'currentBadge' => $gameState['currentBadge'],
            'player_stats' => $playerStats,
            'game_info' => [
                'game_id' => $hexlingWord->game_id,
                'root_word' => $hexlingWord->root_word,
                'center_letter' => $hexlingWord->center_letter,
                'total_levels' => $hexlingWord->total_levels,
                'total_points' => $hexlingWord->total_points,
                'game_date' => $hexlingWord->game_date->toDateString(),
                'secret_words_count' => count($hexlingWord->secret_words ?? []),
            ],
        ];
    }

    protected function checkBadgeAchievements($hexlingWord, array $currentEarnedBadgeIds, int $earnedPoints): array
    {
        $newBadgeIds = [];
        $gameBadges = $hexlingWord->badges->sortBy('badge_order')->values();

        if ($gameBadges->isEmpty()) {
            return $newBadgeIds;
        }

        $cumulativePoints = 0;

        foreach ($gameBadges as $badge) {
            if (in_array($badge->id, $currentEarnedBadgeIds)) {
                $cumulativePoints += $badge->badge_points;
                continue;
            }

            $requiredPoints = $cumulativePoints + $badge->badge_points;

            if ($earnedPoints >= $requiredPoints) {
                $newBadgeIds[] = $badge->id;
                $cumulativePoints += $badge->badge_points;
            } else {
                break;
            }
        }

        return $newBadgeIds;
    }

    protected function getBadgesByIds(array $badgeIds): array
    {
        if (empty($badgeIds)) {
            return [];
        }

        return HexlingBadge::whereIn('id', $badgeIds)
            ->orderBy('badge_order')
            ->get()
            ->toArray();
    }

    protected function calculateLevelFromBadges(array $earnedBadgeIds): int
    {
        return count($earnedBadgeIds) + 1;
    }

    protected function getUpcomingBadges($hexlingWord, array $earnedBadgeIds): array
    {
        $allBadges = $hexlingWord->badges->sortBy('badge_order');

        if (count($earnedBadgeIds) >= $allBadges->count()) {
            return [];
        }

        $upcomingBadges = [];

        foreach ($allBadges as $badge) {
            if (! in_array($badge->id, $earnedBadgeIds)) {
                $upcomingBadges[] = $badge;
            }
        }

        return $upcomingBadges;
    }

    protected function isValidHexlingWord(string $word, string $centerLetter, array $secretWords): bool
    {
        return strpos($word, $centerLetter) !== false && in_array($word, $secretWords);
    }

    protected function calculateHexlingPlayerStats(string $userId): array
    {
        $userPlays = HexlingPlay::with(['word', 'word.badges'])
            ->where('user_id', $userId)
            ->whereNotNull('completed_at')
            ->orderBy('completed_at')
            ->get();

        $totalGamesPlayed = $userPlays->count();
        $totalGamesWon = $userPlays->where('completed_at', '!=', null)->count();
        $totalPointsEarned = $userPlays->sum('earned_points');
        $totalBadgesEarned = 0;
        $longestWinStreak = 0;
        $tempWinStreak = 0;

        foreach ($userPlays as $play) {
            $earnedBadgesInGame = count($play->earned_badge_ids ?? []);
            $totalBadgesEarned += $earnedBadgesInGame;

            if ($play->completed_at) {
                $tempWinStreak++;
                $longestWinStreak = max($longestWinStreak, $tempWinStreak);
            } else {
                $tempWinStreak = 0;
            }
        }

        $currentWinStreak = 0;
        $lastPlay = $userPlays->last();
        if ($lastPlay && $lastPlay->completed_at) {
            $currentWinStreak = $tempWinStreak;
        }

        $winRate = $totalGamesPlayed > 0
            ? round(($totalGamesWon / $totalGamesPlayed) * 100, 2)
            : 0;

        return [
            'total_games_played' => $totalGamesPlayed,
            'total_games_won' => $totalGamesWon,
            'total_points_earned' => $totalPointsEarned,
            'total_badges_earned' => $totalBadgesEarned,
            'current_win_streak' => $currentWinStreak,
            'longest_win_streak' => $longestWinStreak,
            'win_rate' => $winRate,
            'average_points_per_game' => $totalGamesPlayed > 0
                ? round($totalPointsEarned / $totalGamesPlayed, 2)
                : 0,
            'average_badges_per_game' => $totalGamesPlayed > 0
                ? round($totalBadgesEarned / $totalGamesPlayed, 2)
                : 0,
        ];
    }

    protected function getHexlingGameState(?HexlingPlay $play, HexlingWord $hexlingWord): array
    {
        if (! $play) {
            return [
                'score' => 0,
                'guessedWords' => [],
                'wordList' => [],
                'currentBadge' => null,
                'gamePoints' => $hexlingWord->total_points,
                'earnPoints' => 0,
                'isCompleted' => false,
                'currentLevel' => 1,
                'progressPercentage' => 0,
            ];
        }

        $userSubmissions = $play->user_submissions ?? [];
        $earnedPoints = $play->earned_points ?? 0;
        $earnedBadgeIds = $play->earned_badge_ids ?? [];
        $secretWords = $hexlingWord->secret_words ?? [];

        $currentBadge = null;
        if (! empty($earnedBadgeIds)) {
            $currentBadgeId = $hexlingWord->total_levels > $play->player_game_level ? end($earnedBadgeIds) : null;
            $currentBadge = $currentBadgeId ? HexlingBadge::find($currentBadgeId) : null;
        }

        $currentLevel = $this->calculateLevelFromBadges($earnedBadgeIds);
        $wordList = $this->getWordListWithStatus($secretWords, $userSubmissions);

        $isCompleted = $play->completed_at !== null;
        $progressPercentage = $hexlingWord->total_points > 0
            ? round(($earnedPoints / $hexlingWord->total_points) * 100, 2)
            : 0;

        return [
            'score' => $earnedPoints,
            'guessedWords' => $userSubmissions,
            'wordList' => $wordList,
            'currentBadge' => $currentBadge ? [
                'id' => $currentBadge->id,
                'name' => $currentBadge->badge_name,
                'description' => $currentBadge->badge_description,
                'image' => $currentBadge->badge_image,
                'points' => $currentBadge->badge_points,
                'order' => $currentBadge->badge_order,
            ] : null,
            'gamePoints' => $hexlingWord->total_points,
            'earnPoints' => $earnedPoints,
            'isCompleted' => $isCompleted,
            'currentLevel' => $currentLevel,
            'totalLevels' => $hexlingWord->total_levels,
            'progressPercentage' => $progressPercentage,
            'foundWordsCount' => count(array_intersect($secretWords, $userSubmissions)),
            'totalWordsCount' => count($secretWords),
            'earnedBadgesCount' => count($earnedBadgeIds),
            'totalBadgesCount' => $hexlingWord->badges->count(),
        ];
    }

    protected function getWordListWithStatus(array $secretWords, array $guessedWords): array
    {
        $wordList = [];

        foreach ($secretWords as $word) {
            $wordList[] = [
                'word' => $word,
                'isGuessed' => in_array($word, $guessedWords),
                'length' => strlen($word),
                'points' => strlen($word),
            ];
        }

        usort($wordList, function ($a, $b) {
            return $b['length'] - $a['length'];
        });

        return $wordList;
    }
}
