using Kalaha.Api.Controllers.Helper;
using Kalaha.Api.Database;
using Kalaha.Api.Database.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
using System.Security.Claims;

namespace Kalaha.Api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize] // Zugriff nur mit JWT erlaubt
    public class GameController : Controller
    {

        private readonly KalahaDbContext _context;

        public GameController(KalahaDbContext context)
        {
            _context = context;
        }

        [HttpPost("create")]
        public async Task<IActionResult> CreateGame()
        {
            // Die User-ID aus dem JWT-Token auslesen
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId))
            {
                return Unauthorized("Ungültiges Token.");
            }

            var newGame = new Game()
            {
                Player1Id = userId,
                State = GameState.Waiting,
                BoardState = new int[] { 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 0 }
            };

            _context.Games.Add(newGame);
            await _context.SaveChangesAsync();

            return Ok(new
            {
                message = "Spiel erfolgreich erstellt",
                gameId = newGame.GameId
            });
        }

        [HttpPost("{gameId}/join")]
        public async Task<IActionResult> JoinGame([FromRoute] Guid gameId)
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var player2Id))
            {
                return Unauthorized("Ungültiges Token");
            }


            Game? game = await _context.Games.FindAsync(gameId);

            if (game == null)
            {
                return NotFound("Ein Spiel mit dieser ID existiert nicht.");
            }

            if (game.State != GameState.Waiting || game.Player2Id != null)
            {
                return BadRequest("Dieses Spiel ist bereits voll oder wurde schon beendet.");
            }

            if (game.Player1Id == player2Id)
            {
                return BadRequest("Du kannst nicht deinem eigenen Spiel beitreten.");
            }

            game.Player2Id = player2Id;
            game.State = GameState.Running;

            GameControllerHelper.RandomizeStartingPlayer(game);

            try
            {
                await _context.SaveChangesAsync();
                return Ok();
            } catch (DbUpdateConcurrencyException ex)
            {
                return BadRequest("Konnte dem Spiel nicht beitreten. Bitte erneut versuchen");
            }

        }

        [HttpDelete("{gameId}/quit")]
        public async Task<IActionResult> QuitGame([FromRoute] Guid gameId)
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId))
            {
                return Unauthorized("Ungültiges Token");
            }

            Game? game = await _context.Games.FindAsync(gameId);

            try
            {
                if (game != null && game.State == GameState.Waiting && (game.Player1Id == userId || game.Player2Id == userId))
                {
                    _context.Remove(game);
                    await _context.SaveChangesAsync();
                    return Ok(new
                    {
                        message = "Spiel beendet"
                    });
                }
                else if (game != null && game.State == GameState.Running && (game.Player1Id == userId || game.Player2Id == userId))
                {
                    game.State = GameState.Finished;
                    game.WinnerId = game.Player1Id == userId ? game.Player2Id : game.Player1Id;
                    game.FinishedTime = DateTime.UtcNow;
                    await _context.SaveChangesAsync();
                    return Ok(new
                    {
                        message = "Spiel aufgegeben"
                    });
                }
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return BadRequest("Konnte das Spiel nicht beendet. Bitte erneut versuchen");
            }


            return NotFound();
        }

        [HttpPost("{gameId}/move")]
        public async Task<IActionResult> Move([FromRoute] Guid gameId, [FromBody] int bucketPos)
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId)) return Unauthorized("Ungültiges Token");

            Game? game = await _context.Games.FindAsync(gameId);

            if (game == null || game.State != GameState.Running)
            {
                return BadRequest("Das Spiel existiert nicht oder läuft nicht.");
            }

            if (game.ActivePlayerId != userId)
            {
                return BadRequest("Du bist nicht am Zug!");
            }

            // Prüfen, ob die gewählter Bucket gültig ist und nicht leer ist
            bool isPlayer1 = game.Player1Id == userId;
            if ((isPlayer1 && (bucketPos < 0 || bucketPos > 5)) ||
                (!isPlayer1 && (bucketPos < 7 || bucketPos > 12)) ||
                game.BoardState[bucketPos] == 0)
            {
                return BadRequest("Ungültiger Zug.");
            }

            bool extraTurn = GameControllerHelper.PlayMove(game, userId, bucketPos);

            if (game.State != GameState.Finished)
            {
                if (!extraTurn)
                {
                    game.ActivePlayerId = isPlayer1 ? game.Player2Id : game.Player1Id;
                }
            } else
            {
                int player1Score = game.BoardState[6]; // Kalaha von Player 1
                int player2Score = game.BoardState[13]; // Kalaha von Player 2

                if (player1Score > player2Score) game.WinnerId = game.Player1Id;
                else if (player2Score > player1Score) game.WinnerId = game.Player2Id;
                // Bei einem Unentschieden bleibt WinnerId null
                game.FinishedTime = DateTime.UtcNow;
            }

            try
            {
                await _context.SaveChangesAsync();

                return Ok(new
                {
                    gameState = game.State.ToString(),
                    board = game.BoardState,
                    activePlayer = game.ActivePlayerId
                });
            } catch (DbUpdateConcurrencyException ex)
            {
                return BadRequest("Spielzug konnte nicht ausgeführt werden. Bitte erneut versuchen");
            }
        }

        [HttpGet("{gameId}")]
        public async Task<IActionResult> GetGameStatus([FromRoute] Guid gameId)
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId)) return Unauthorized();

            var game = await _context.Games.FindAsync(gameId);

            if (game == null) return NotFound("Spiel nicht gefunden.");

            if (game.Player1Id != userId && game.Player2Id != userId)
            {
                return Forbid("Du bist nicht Teil dieses Spiels.");
            }

            return Ok(new
            {
                gameId = game.GameId,
                state = game.State.ToString(), // "Waiting", "Running" oder "Finished"
                player1Id = game.Player1Id,
                player2Id = game.Player2Id,
                activePlayerId = game.ActivePlayerId,
                board = game.BoardState,
                winnerId = game.WinnerId
            });
        }

        [HttpGet("getUserId")]
        public async Task<IActionResult> GetUserId()
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId)) return Unauthorized();
            return Ok(new
            {
                userId
            });
        }

        [HttpGet("allGames")]
        public async Task<IActionResult> GetAllGames()
        {
            var userIdString = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!Guid.TryParse(userIdString, out var userId)) return Unauthorized();

            // 1. Alle Spiele, in denen ich SPIELER 1 bin
            var gamesAsP1 = from g in _context.Games
                            where g.Player1Id == userId
                            // LEFT JOIN: Falls noch kein Player 2 da ist, stürzt es nicht ab
                            join u in _context.Users on g.Player2Id equals u.Id into uGroup
                            from u in uGroup.DefaultIfEmpty()
                            select new GameListDto
                            {
                                GameId = g.GameId,
                                // Wenn u null ist, gibt es noch keinen Gegner
                                Opponent = u != null ? u.Name : "Wartet auf Gegner...",
                                HasWon = g.WinnerId == null ? null : (g.WinnerId == userId),
                                FinishedTime = g.FinishedTime
                            };

            // 2. Alle Spiele, in denen ich SPIELER 2 bin
            var gamesAsP2 = from g in _context.Games
                            where g.Player2Id == userId
                            join u in _context.Users on g.Player1Id equals u.Id
                            select new GameListDto
                            {
                                GameId = g.GameId,
                                Opponent = u.Name,
                                HasWon = g.WinnerId == null ? null : (g.WinnerId == userId),
                                FinishedTime = g.FinishedTime
                            };

            // 3. Beide Listen mit Concat() verbinden und ausführen
            var result = await gamesAsP1.Concat(gamesAsP2).ToListAsync<GameListDto>();

            return Ok(result);
        }

        public class GameListDto
        {
            public Guid GameId { get; set; }
            public string Opponent { get; set; } = string.Empty;
            public bool? HasWon { get; set; } // null = Spiel läuft noch, true = gewonnen, false = verloren
            public DateTime? FinishedTime { get; set; }
        }
    }
}
