using YatzyLibrary;

namespace YatzyGame.Components
{
    public class GameController
    {
        YatzyCheckers yatzyCheckers = new YatzyCheckers();
        private Dictionary<string, Player> Players = new Dictionary<string, Player>();
        public bool gameStarted = false;

        public int rerolls = 0;

        public Dictionary<int, int> Dices = new Dictionary<int, int>();
        public List<int> selectedDices = new List<int>();
        public Player currentPlayer;
        public string gameID;
        public bool onlineMultiplayer = false;
        public string joinCode = string.Empty;

        public GameController(string gameID, bool onlineMultiplayer, string joinCode)
        {
            this.gameID = gameID;
            this.onlineMultiplayer = onlineMultiplayer;
            this.joinCode = joinCode;
        }

        public void AddPlayer(string name)
        {
            Players.Add(name, new Player(name));
        }

        public void AddPlayer(string name, string playerID, bool owner = false)
        {
            Players.Add(name, new Player(name, playerID, owner));
        }

        public void RemovePlayer(string name)
        {
            Players.Remove(name);
        }

        public Player GetPlayer(string name)
        {
            return Players.FirstOrDefault(x => x.Key.Equals(name)).Value;
        }

        public List<Player> GetPlayers()
        {
            return Players.Select(x => x.Value).ToList();
        }

        public Dictionary<ScoreLine, int> GetPlayerScores(string name)
        {
            return GetPlayer(name).scores;
        }

        public void SetOrUpdatePlayerScore(string name, ScoreLine type, int value)
        {
            var playerScores = GetPlayerScores(name);
            if (!playerScores.ContainsKey(type))
            {
                playerScores.Add(type, value);
            }
            else
            {
                playerScores[type] = value;
            }
        }

        public void startGame()
        {
            this.gameStarted = true;
            StartTurn(GetPlayers().FirstOrDefault());
        }

        public void stopGame()
        {
            this.gameStarted = false;
            // Players.Clear();
        }

        public List<int> ThrowMany(List<Dice> dices)
        {
            var retval = new List<int>();
            foreach (var dice in dices)
            {
                retval.Add(dice.Throw());
            }

            return retval;
        }

        public void StartTurn(Player player)
        {
            CheckForBonusesAndUpdateTopSum();

            this.rerolls = 2;
            this.selectedDices.Clear();

            Dices.Clear();

            for (int i = 0; i < 5; i++)
            {
                Dices.Add(i, new Dice(6).Throw());
            }

            currentPlayer = player;
        }

        public void SelectDice(int key)
        {
            if (selectedDices.Contains(key))
            {
                selectedDices.Remove(key);
            }
            else
            {
                selectedDices.Add(key);
            }
        }

        public void RerollSelected()
        {
            if (rerolls <= 0)
                return;

            foreach (var key in selectedDices)
            {
                Dices[key] = new Dice(6).Throw();
            }

            rerolls--;

            selectedDices.Clear();
        }

        public void InsertPoint(string type, List<int> dices)
        {
            var result = 0;

            switch (type)
            {
                case "one":
                    result = yatzyCheckers.SingleThingy(dices, 1).value;
                    break;
                case "two":
                    result = yatzyCheckers.SingleThingy(dices, 2).value;
                    break;
                case "three":
                    result = yatzyCheckers.SingleThingy(dices, 3).value;
                    break;
                case "four":
                    result = yatzyCheckers.SingleThingy(dices, 4).value;
                    break;
                case "five":
                    result = yatzyCheckers.SingleThingy(dices, 5).value;
                    break;
                case "six":
                    result = yatzyCheckers.SingleThingy(dices, 6).value;
                    break;
                case "sum":
                case "bonus":

                    break;
                case "one_pair":
                    result = yatzyCheckers.PairChecker(dices, 1).value;
                    break;
                case "two_pair":
                    result = yatzyCheckers.PairChecker(dices, 2).value;
                    break;
                case "three_matching":
                    result = yatzyCheckers.SameNumberChecker(dices, 3).value;
                    break;
                case "four_matching":
                    result = yatzyCheckers.SameNumberChecker(dices, 4).value;
                    break;
                case "little_straight":
                    result = yatzyCheckers.StraightChecker(dices, "small").value;
                    break;
                case "large_straight":
                    result = yatzyCheckers.StraightChecker(dices, "large").value;
                    break;
                case "house":
                    result = yatzyCheckers.FullHouse(dices).value;
                    break;
                case "chance":
                    result = yatzyCheckers.Chance(dices).value;
                    break;
                case "yatzy":
                    result = yatzyCheckers.Yatzy(dices).value;
                    break;
                default:
                    break;
            }

            var enu = (ScoreLine)Enum.Parse(typeof(ScoreLine), type);

            SetOrUpdatePlayerScore(currentPlayer.name, enu, result);

            NextTurn();
        }

        public void NextTurn()
        {
            var nextUser = GetNextKey(Players, currentPlayer.name);

            var nextPlayer = Players.Where(x => x.Key.Equals(nextUser)).First().Value;

            StartTurn(nextPlayer);
        }

        static TKey GetNextKey<TKey, TValue>(Dictionary<TKey, TValue> dict, TKey currentKey)
        {
            var keys = dict.Keys.ToList();
            int index = keys.IndexOf(currentKey);

            if (index == -1)
                throw new ArgumentException("Key not found in dictionary.");

            int nextIndex = (index + 1) % keys.Count; // Wrap around
            return keys[nextIndex];
        }

        public void CheckForBonusesAndUpdateTopSum()
        {
            var scoresToGet = new[]
            {
                ScoreLine.one,
                ScoreLine.two,
                ScoreLine.three,
                ScoreLine.four,
                ScoreLine.four,
                ScoreLine.five,
                ScoreLine.six
            };

            foreach (var player in Players)
            {
                if (player.Value.scores.Where(x => scoresToGet.Contains(x.Key)).Select(x => x.Value).Sum() >= 63)
                {
                    SetOrUpdatePlayerScore(player.Key, ScoreLine.bonus, 50);
                }
                else if (scoresToGet.All(req => player.Value.scores.ContainsKey(req)))
                {
                    SetOrUpdatePlayerScore(player.Key, ScoreLine.bonus, 0);
                }

                SetOrUpdatePlayerScore(player.Key, ScoreLine.top_sum, player.Value.scores.Where(x => scoresToGet.Contains(x.Key)).Select(x => x.Value).Sum());
            }
        }

        public static string TypeLabel(ScoreLine scoreLine)
        {
            return scoreLine switch
            {
                ScoreLine.one => "Ones",
                ScoreLine.two => "Twos",
                ScoreLine.three => "Threes",
                ScoreLine.four => "Fours",
                ScoreLine.five => "Fives",
                ScoreLine.six => "Sixes",
                ScoreLine.top_sum => "Top Sum",
                ScoreLine.bonus => "Bonus",
                ScoreLine.one_pair => "One Pair",
                ScoreLine.two_pair => "Two Pair",
                ScoreLine.three_matching => "Three of a Kind",
                ScoreLine.four_matching => "Four of a Kind",
                ScoreLine.little_straight => "Small Straight",
                ScoreLine.large_straight => "Large Straight",
                ScoreLine.house => "Full House",
                ScoreLine.chance => "Chance",
                ScoreLine.yatzy => "Yatzy",
                _ => "Unknown"
            };
        }

        public Player GetPlayerById(string id)
        {
            return Players.Where(x => x.Value.playerId.Equals(id)).First().Value;
        }
    }

    public class Player
    {
        public Player(string name)
        {
            this.name = name;
            this.scores = new Dictionary<ScoreLine, int>();
        }
        public Player(string name, string playerID, bool owner)
        {
            this.name = name;
            this.scores = new Dictionary<ScoreLine, int>();
            this.playerId = playerID;
            this.Owner = owner;
        }
        public string name { get; set; }
        public Dictionary<ScoreLine, int> scores { get; set; }
        public string playerId { get; set; }
        public bool Owner { get; set; } 
    }

    public enum ScoreLine
    {
        one,
        two,
        three,
        four,
        five,
        six,
        top_sum,
        bonus,
        one_pair,
        two_pair,
        three_matching,
        four_matching,
        little_straight,
        large_straight,
        house,
        chance,
        yatzy
    }
}