<?php

namespace App\Services\Games;

use App\Models\User;
use App\Models\WordlingPlay;
use App\Models\WordlingWord;
use Illuminate\Support\Facades\DB;

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

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

            // Check if game is active
            if (! $wordlingWord->isActive()) {
                return [
                    'success' => false,
                    'message' => 'This game is not active and cannot be played.',
                ];
            }

            $maxAttempts = $wordlingWord->max_attempts ?? 6;

            $wordlingPlay = WordlingPlay::firstOrCreate(
                [
                    'user_id' => $user->id,
                    'wordling_word_id' => $wordlingWord->id,
                ],
                [
                    'try_count' => 0,
                    'is_correct' => false,
                    'user_submissions' => [],
                ]
            );

            if ($wordlingPlay->isCompleted()) {
                $message = $wordlingPlay->is_correct
                    ? 'You have already guessed the correct word'
                    : 'You have already used all your attempts';

                return [
                    'success' => false,
                    'message' => $message,
                ];
            }

            $result = $this->evaluateGuess($userWord, $wordlingWord->secret_word);
            $isCorrect = $userWord === $wordlingWord->secret_word;
            $newTryCount = $wordlingPlay->try_count + 1;

            $wordlingPlay->update([
                'try_count' => $newTryCount,
                'is_correct' => $isCorrect,
                'user_submissions' => array_merge($wordlingPlay->user_submissions ?? [], [$result]),
                'completed_at' => $isCorrect || $newTryCount >= $maxAttempts ? now() : null,
            ]);

            $message = '';

            if ($isCorrect) {
                $message = 'You have guessed the correct word';
            } elseif ($newTryCount >= $maxAttempts) {
                $message = 'You have used all your attempts';
            } else {
                $message = 'You have '.($maxAttempts - $newTryCount).' attempts left';
            }

            return [
                'success' => true,
                'data' => [
                    'try_count' => $newTryCount,
                    'user_input_word' => $userWord,
                    'is_correct' => $isCorrect,
                    'max_attempts' => $maxAttempts,
                    'remaining_attempts' => max(0, $maxAttempts - $newTryCount),
                    'result' => $result,
                    'user_submissions' => $wordlingPlay->user_submissions,
                    'message' => $message,
                ],
            ];
        });
    }

    public function getSubmissions($user, array $data)
    {
        $gameType = $data['game_type'];
        $wordlingWord = $this->findActiveGame($data, $gameType);

        $wordlingPlay = WordlingPlay::where('user_id', $user->id)
            ->where('wordling_word_id', $wordlingWord->id)
            ->select('id', 'try_count', 'is_correct', 'completed_at', 'user_submissions')
            ->first();

        return [
            'wordling_word' => $wordlingWord,
            'wordling_play' => $wordlingPlay,
        ];
    }

    public function getGameResult($user, array $data)
    {
        $gameType = $data['game_type'];
        $wordlingWord = $this->findActiveGame($data, $gameType);

        $currentPlay = WordlingPlay::with('wordlingWord')
            ->where('wordling_word_id', $wordlingWord->id)
            ->where('user_id', $user->id)
            ->first();

        $playerStats = $this->calculatePlayerStats($user->id, $gameType);
        $roundState = $currentPlay ? $this->getRoundState($currentPlay) : null;

        return [
            'player_stats' => $playerStats,
            'round_state' => $roundState,
            'game_info' => [
                'game_id' => $wordlingWord->game_id,
                'secret_word_length' => $wordlingWord->secret_word_length,
                'max_attempts' => $wordlingWord->max_attempts,
                'game_type' => $wordlingWord->game_type,
                'game_date' => $wordlingWord->game_date->toDateString(),
                'status' => $wordlingWord->status->value,
                'is_active' => $wordlingWord->isActive(),
            ],
        ];
    }

    public function getGamesList($user, array $filters)
    {
        $gameType = $filters['game_type'] ?? 'wordling_main';

        $query = WordlingWord::query()
            ->where('game_type', $gameType)
            ->where('status', 'active')
            ->select('id', 'game_id', 'game_type', 'max_attempts', 'game_date', 'secret_word_length', 'status');

        $firstGameDate = WordlingWord::where('game_type', $gameType)
            ->where('status', 'active')
            ->min('game_date');

        $latestGameDate = WordlingWord::where('game_type', $gameType)
            ->where('status', 'active')
            ->max('game_date');

        // Logic for single game retrieval (latest) if no filters
        if (empty($filters['game_id']) && empty($filters['month']) && empty($filters['date'])) {
            $query->orderBy('game_date', 'desc');
            $game = $query->first();

            if (! $game) {
                 return [
                    'success' => false,
                    'message' => 'No active games found for the specified type.'
                ];
            }

            $userPlay = WordlingPlay::where('user_id', $user->id)
                ->where('wordling_word_id', $game->id)
                ->first();

            $status = $this->calculateUserPlayStatus($userPlay, $game);

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

            $gameWithStatus = [
                'id' => $game->id,
                'game_id' => $game->game_id,
                'game_type' => $game->game_type,
                'max_attempts' => $game->max_attempts,
                'game_date' => $game->game_date,
                'formatted_date' => $formattedDate,
                'secret_word_length' => $game->secret_word_length,
                'user_play_status' => $status,
                'game_number' => $gameNumber,
                'status' => $game->status->value,
                'is_active' => $game->isActive(),
            ];

            return [
                'success' => true,
                'data' => [
                    'games' => [
                        $formattedDate => [$gameWithStatus],
                    ],
                    'month' => date('Y-m', strtotime($game->game_date)),
                    'start_date' => $formattedDate,
                    'end_date' => $formattedDate,
                    'first_game_date' => $firstGameDate,
                    'latest_game_date' => $latestGameDate,
                    'game_type' => $gameType,
                ]
            ];
        }

        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();

        if ($games->isEmpty()) {
            return [
                'success' => false,
                'message' => 'No active games found for the specified criteria.'
            ];
        }

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

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

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

            return [
                'id' => $game->id,
                'game_id' => $game->game_id,
                'game_type' => $game->game_type,
                'max_attempts' => $game->max_attempts,
                'game_date' => $game->game_date,
                'formatted_date' => $formattedDate,
                'secret_word_length' => $game->secret_word_length,
                'user_play_status' => $status,
                'game_number' => $gameNumber,
                'status' => $game->status->value,
                'is_active' => $game->isActive(),
            ];
        });

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

        return [
            'success' => true,
            'data' => [
                'games' => $gamesByDate,
                'month' => $month,
                'start_date' => $startDate,
                'end_date' => $endDate,
                'first_game_date' => $firstGameDate,
                'latest_game_date' => $latestGameDate,
                'game_type' => $gameType,
            ]
        ];
    }

    public function getActiveGamesList($user, array $filters)
    {
        $gameType = $filters['game_type'] ?? 'wordling_main';

        $query = WordlingWord::query()
            ->where('game_type', $gameType)
            ->where('status', 'active')
            ->select('id', 'game_id', 'game_type', 'max_attempts', 'game_date', 'secret_word_length', 'status');

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

        $games = $query->orderBy('game_date', 'desc')->get();

        if ($games->isEmpty()) {
            return [
                'success' => false,
                'message' => 'No active games found for the specified criteria.'
            ];
        }

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

        $gamesWithStatus = $games->map(function ($game) use ($userPlays) {
            $userPlay = $userPlays->get($game->id);
            $status = $this->calculateUserPlayStatus($userPlay, $game);
            $formattedDate = date('Y-m-d', strtotime($game->game_date));

            return [
                'id' => $game->id,
                'game_id' => $game->game_id,
                'game_type' => $game->game_type,
                'max_attempts' => $game->max_attempts,
                'game_date' => $game->game_date,
                'formatted_date' => $formattedDate,
                'secret_word_length' => $game->secret_word_length,
                'user_play_status' => $status,
                'status' => $game->status->value,
                'is_active' => true,
            ];
        });

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

        return [
            'success' => true,
            'data' => [
                'games' => $gamesByDate,
                'game_type' => $gameType,
                'message' => 'Active games retrieved successfully',
            ]
        ];
    }

    protected function calculateUserPlayStatus($userPlay, $game)
    {
        $status = 'NOT_PLAYED';

        if ($userPlay) {
            $hasSubmissions = ! empty($userPlay->user_submissions);
            if ($userPlay->is_correct) {
                $status = 'SOLVED';
            } elseif (! $hasSubmissions) {
                $status = 'NOT_PLAYED';
            } elseif ($userPlay->try_count >= $game->max_attempts) {
                $status = 'FAILED';
            } else {
                $status = 'UNFINISHED';
            }
        }
        return $status;
    }

    protected function findActiveGame(array $filters, string $gameType)
    {
        $query = WordlingWord::query()
            ->where('game_type', $gameType)
            ->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']);
            }
        }

        $game = $query->first();

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

        return $game;
    }

    protected function evaluateGuess(string $guess, string $target): array
    {
        $target = str_split($target);
        $guess = str_split($guess);
        $result = [];

        for ($i = 0; $i < count($guess); $i++) {
            if (isset($target[$i]) && $guess[$i] === $target[$i]) {
                $result[$i] = ['letter' => $guess[$i], 'status' => 'correct'];
                $target[$i] = null;
                $guess[$i] = null;
            }
        }

        for ($i = 0; $i < count($guess); $i++) {
            if ($guess[$i] === null) {
                continue;
            }

            if (($key = array_search($guess[$i], $target, true)) !== false) {
                $result[$i] = ['letter' => $guess[$i], 'status' => 'present'];
                $target[$key] = null;
            } else {
                $result[$i] = ['letter' => $guess[$i], 'status' => 'absent'];
            }
        }

        return $result;
    }

    protected function calculatePlayerStats(string $userId, string $gameType): array
    {
        $userPlays = WordlingPlay::with('wordlingWord')
            ->where('user_id', $userId)
            ->whereHas('wordlingWord', function ($query) use ($gameType) {
                $query->where('game_type', $gameType)
                    ->where('status', 'active');
            })
            ->whereNotNull('completed_at')
            ->orderBy('completed_at')
            ->get();

        $totalWins = 0;
        $totalLosses = 0;
        $currentWinStreak = 0;
        $longestWinStreak = 0;
        $winTurns = [];
        $lastGameDate = null;
        $lastGameId = null;

        $tempWinStreak = 0;

        foreach ($userPlays as $play) {
            if ($play->is_correct) {
                $totalWins++;
                $tempWinStreak++;
                $winTurns[] = $play->try_count;

                $longestWinStreak = max($longestWinStreak, $tempWinStreak);
            } else {
                $totalLosses++;
                $tempWinStreak = 0;
            }

            $lastGameDate = $play->completed_at->toDateString();
            $lastGameId = $play->wordlingWord->game_id;
        }

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

        $user = User::find($userId);

        return [
            'game_id' => $lastGameId ?? '',
            'total_wins' => $totalWins,
            'total_losses' => $totalLosses,
            'current_win_streak' => $currentWinStreak,
            'longest_win_streak' => $longestWinStreak,
            'win_turns' => $winTurns,
            'last_game_date' => $lastGameDate ?? '',
            'last_game_id' => $lastGameId ?? 0,
            'created_at' => $user->created_at->timestamp,
            'updated_at' => now()->timestamp,
            'additional_metrics' => [
                'total_games_played' => $totalWins + $totalLosses,
                'win_rate' => $totalWins + $totalLosses > 0
                    ? round(($totalWins / ($totalWins + $totalLosses)) * 100, 2)
                    : 0,
                'average_turns_to_win' => count($winTurns) > 0
                    ? round(array_sum($winTurns) / count($winTurns), 2)
                    : 0,
            ],
        ];
    }

    protected function getRoundState(WordlingPlay $play): array
    {
        $status = 'in_progress';

        if ($play->is_correct) {
            $status = 'won';
        } elseif ($play->try_count >= $play->max_attempts) {
            $status = 'lost';
        }

        $timeSolvedInSec = $play->completed_at
            ? $play->created_at->diffInSeconds($play->completed_at)
            : null;

        return [
            'is_correct' => (bool) $play->is_correct,
            'status' => $status,
            'round' => [
                'try_count' => $play->try_count,
                'max_attempts' => $play->max_attempts,
                'remaining_attempts' => $play->getRemainingAttemptsAttribute(),
                'completed_at' => $play->completed_at?->toISOString(),
                'user_submissions' => $play->user_submissions,
            ],
            'time_solved_in_sec' => $timeSolvedInSec,
            'secret_word' => $play->isCompleted()
                ? $play->wordlingWord->secret_word
                : null,
        ];
    }
}
