using Microsoft.AspNetCore.SignalR.Client;
using qtcnet_client.Forms;
using qtcnet_client.Services;
using QtCNETAPI.Enums;
using QtCNETAPI.Models;
using QtCNETAPI.Schema;
using QtCNETAPI.Services.ApiService;
using System.Threading.Tasks;
using qtcnet_client.Model;

namespace qtcnet_client.Forms.Games
{
    public partial class TicTacToeGame : Form
    {
        private HubConnection? GameHubConnection { get; set; }
        private GameRoom? CurrentRoom { get; set; }

        private IApiService _apiService;
        private ClientConfig _config;
        private AudioService _audioService = new();

        private int WinCount { get; set; }
        private bool JackpotWon { get; set; } = false;
        public TicTacToeGame(IApiService apiService, ClientConfig config)
        {
            _apiService = apiService;
            _config = config;

            InitializeComponent();
        }

        private async void Main_Load(object sender, EventArgs e)
        {
            // first, connect to the game hub
            GameHubConnection = new HubConnectionBuilder()
                .WithUrl($"{_config.ServerBaseUri}/tttgame", options =>
                {
                    options.AccessTokenProvider = () => Task.FromResult(_apiService.SessionToken)!;
                })
                .WithAutomaticReconnect()
                .Build();

            pLoading.Visible = true;
            lblLoadStatus.Text = "Connecting\nTo\nHub...";

            GameHubConnection.On<GameRoom>("WaitingForPlayer", (room) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    if (room.Player1 != null)
                    {
                        lblP1Username.Text = room.Player1.Username;
                        lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
                    }

                    lblLoadStatus.Text = "Waiting\nFor\nPlayer...";
                });
            });

            GameHubConnection.On<GameRoom>("GameStart", (room) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    pLoading.Visible = false;
                    pSymbolSelection.Visible = false;
                });
            });

            GameHubConnection.On<GameRoom, string?>("GameEnd", (room, usernameDisconnected) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    if (room.Player1 == null) lblP1Username.Text = "Username";
                    if (room.Player2 == null) lblP2Username.Text = "Username";

                    ToggleBoardControl(false);

                    switch (room.Status)
                    {
                        case GameStatus.P1Win:
                            {
                                if (_apiService.CurrentUser?.Id == room.Player1?.Id)
                                {
                                    lblLoadStatus.Text = "You Won!";
                                    WinCount++;

                                    if (WinCount >= 3)
                                    {
                                        JackpotWon = true;
                                        lblJackpotWon.Text = "You Won A Spin!";
                                    }
                                }
                                else lblLoadStatus.Text = "You Lost!";
                                lblPlayAgain.Visible = true;
                                btnPlayAgain.Visible = true;
                                break;
                            }
                        case GameStatus.P2Win:
                            {
                                if (_apiService.CurrentUser?.Id == room.Player2?.Id)  
                                { 
                                    lblLoadStatus.Text = "You Won!";
                                    WinCount++;

                                    if (WinCount >= 3)
                                    {
                                        JackpotWon = true;
                                        lblJackpotWon.Text = "You Won A Spin!";
                                    }
                                }
                                else lblLoadStatus.Text = "You Lost!";
                                lblPlayAgain.Visible = true;
                                btnPlayAgain.Visible = true;
                                break;
                            }
                        case GameStatus.PlayerDisconnected:
                            {
                                if (!string.IsNullOrEmpty(usernameDisconnected))
                                    lblLoadStatus.Text = $"Game Ended.\nUser {usernameDisconnected}\nDisconnected.";
                                lblPlayAgain.Visible = false;
                                btnPlayAgain.Visible = false;
                                break;
                            }
                        case GameStatus.NoWin:
                            lblLoadStatus.Text = "It's a draw!";
                            lblPlayAgain.Visible = true;
                            btnPlayAgain.Visible = true;
                            break;
                    }

                    pLoading.Visible = true;
                });
            });

            GameHubConnection.On<GameRoom>("RestartGame", (room) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    if (pLoading.Visible) pLoading.Visible = false;
                });
                ClearBoard();
                ToggleBoardControl(true);

                BeginInvoke(delegate ()
                {
                    List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];

                    foreach (Button button in buttons)
                    {
                        if (button.BackgroundImage == null)
                            button.Enabled = true;
                    }
                });
            });

            GameHubConnection.On<GameRoom>("SelectSymbol", (room) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    if (room.Player1 != null)
                    {
                        lblP1Username.Text = room.Player1.Username;
                        if (!lvUserlist.Items.ContainsKey(room.Player1.Id)) lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
                    }
                    if (room.Player2 != null)
                    {
                        lblP2Username.Text = room.Player2.Username;
                        if (!lvUserlist.Items.ContainsKey(room.Player2.Id)) lvUserlist.Items.Add(room.Player2.Id, room.Player2.Username, room.Player2.Status);
                    }

                    if (pLoading.Visible) pLoading.Visible = false;
                    pSymbolSelection.Visible = true;
                    pBoard.Enabled = true;
                    ToggleAllSquareButtons(false);
                });
            });

            GameHubConnection.On<GameRoom>("SelectingSymbol", (room) =>
            {
                CurrentRoom = room;

                BeginInvoke(delegate ()
                {
                    if (pSymbolSelection.Visible) return;

                    if (room.Player1 != null)
                    {
                        lblP1Username.Text = room.Player1.Username;
                        if (!lvUserlist.Items.ContainsKey(room.Player1.Id)) lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
                    }
                    if (room.Player2 != null)
                    {
                        lblP2Username.Text = room.Player2.Username;
                        if (!lvUserlist.Items.ContainsKey(room.Player2.Id)) lvUserlist.Items.Add(room.Player2.Id, room.Player2.Username, room.Player2.Status);
                    }

                    pLoading.Visible = true;
                    lblLoadStatus.Text = $"Player 1\nIs Selecting\nSymbol...";
                });
            });

            GameHubConnection.On<GameRoom, TicTacToeMove>("UpdateBoard", (room, move) =>
            {
                CurrentRoom = room;

                UpdateBoardUI(move);
            });

            GameHubConnection.On<GameRoom>("StartTurn", (room) =>
            {
                CurrentRoom = room;
                ToggleBoardControl(true);
                AddChatMessage("[SERVER] Your Turn!");
            });

            GameHubConnection.On<GameRoom>("EndTurn", (room) =>
            {
                CurrentRoom = room;
                ToggleBoardControl(false);
                AddChatMessage("[SERVER] Other Players Turn");
            });

            GameHubConnection.On<string>("ReceiveMessage", (msg) =>
            {
                AddChatMessage(msg);
                if (msg.Split('[')[1] != _apiService.CurrentUser?.Username) 
                    _audioService.PlaySoundEffect("sndMessage");
            });

            try { await GameHubConnection.StartAsync(); }
            catch (HttpRequestException ex)
            {
                MessageBox.Show($"Sorry, We Couldn't Connect To The Game Server Due To An Error. Please Try Again Later.\n\n{ex.Message}", "Oops.", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(0);
            }

            lblLoadStatus.Text = "Finding\nRoom...";
            await GameHubConnection.SendAsync("FindRoom");
        }

        private async void TicTacToeGame_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected)
            {
                if(JackpotWon)
                {
                    // start a jackpot spin
                    Random rnd = new();
                    JackpotSpinForm currencyJackpotSpinner = new(_audioService)
                    {
                        CurrencyWon = rnd.Next(0, 200)
                    };
                    var dialogResult = currencyJackpotSpinner.ShowDialog();
                    if (dialogResult == DialogResult.OK)
                    {
                        await _apiService.AddCurrencyToCurrentUser(currencyJackpotSpinner.CurrencyWon, false);
                    }
                }

                // stop and dispose connection
                await GameHubConnection.StopAsync();
                await GameHubConnection.DisposeAsync();
                GameHubConnection = null;
            }
        }

        private async void btnXSelect_Click(object sender, EventArgs e) => await SelectSymbol(TicTacToeSymbol.X);
        private async void btnOSelect_Click(object sender, EventArgs e) => await SelectSymbol(TicTacToeSymbol.O);

        private async void btnPlayAgain_Click(object sender, EventArgs e)
        {
            if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
            {
                await GameHubConnection.SendAsync("RestartGame", CurrentRoom.Id);
            }
        }

        private async void btnSquareX_Click(object sender, EventArgs e)
        {
            // create move
            TicTacToeMove move = new TicTacToeMove
            {
                User = _apiService.CurrentUser!
            };

            if (sender is Button btn)
            {
                switch (btn.Name)
                {
                    case "btnSquare1":
                        move.Point = 1;
                        break;
                    case "btnSquare2":
                        move.Point = 2;
                        break;
                    case "btnSquare3":
                        move.Point = 3;
                        break;
                    case "btnSquare4":
                        move.Point = 4;
                        break;
                    case "btnSquare5":
                        move.Point = 5;
                        break;
                    case "btnSquare6":
                        move.Point = 6;
                        break;
                    case "btnSquare7":
                        move.Point = 7;
                        break;
                    case "btnSquare8":
                        move.Point = 8;
                        break;
                    case "btnSquare9":
                        move.Point = 9;
                        break;
                }
            }

            // send move
            if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
                await GameHubConnection.SendAsync("MakeMove", CurrentRoom.Id, move);
        }

        private async void btnSend_Click(object sender, EventArgs e)
        {
            if ((GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null) &&
                !string.IsNullOrWhiteSpace(rtxtChatbox.Text))
            {
                await GameHubConnection.SendAsync("SendRoomMessage", CurrentRoom.Id, $"[{_apiService.CurrentUser?.Username}] {rtxtChatbox.Text}");
                _audioService.PlaySoundEffect("sndSendClick");
                rtxtChatbox.Clear();
            }
        }

        private void rtxtChatbox_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.Enter)
                btnSend_Click(sender, e);
        }

        private void ToggleBoardControl(bool toggle)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(delegate ()
                {
                    pBoard.Enabled = toggle;
                });
            }
        }

        private void ToggleAllSquareButtons(bool toggle)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(delegate ()
                {
                    List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];

                    foreach (Button button in buttons)
                        button.Enabled = toggle;
                });
            }
        }

        private async Task SelectSymbol(TicTacToeSymbol symbol)
        {
            if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
            {
                await GameHubConnection.SendAsync("SetSymbol", CurrentRoom.Id, symbol);
                ToggleAllSquareButtons(true);
            }
        }

        private void UpdateBoardUI(TicTacToeMove move)
        {
            if (IsHandleCreated && !IsDisposed && CurrentRoom != null)
            {
                // play move made sound
                _audioService.PlaySoundEffect("sndTTTMoveMade");

                Invoke(delegate ()
                {
                    switch (CurrentRoom.Board.Square1)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare1.BackgroundImage = Properties.Resources.X;
                            btnSquare1.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare1.BackgroundImage = Properties.Resources.O;
                            btnSquare1.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square2)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare2.BackgroundImage = Properties.Resources.X;
                            btnSquare2.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare2.BackgroundImage = Properties.Resources.O;
                            btnSquare2.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square3)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare3.BackgroundImage = Properties.Resources.X;
                            btnSquare3.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare3.BackgroundImage = Properties.Resources.O;
                            btnSquare3.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square4)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare4.BackgroundImage = Properties.Resources.X;
                            btnSquare4.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare4.BackgroundImage = Properties.Resources.O;
                            btnSquare4.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square5)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare5.BackgroundImage = Properties.Resources.X;
                            btnSquare5.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare5.BackgroundImage = Properties.Resources.O;
                            btnSquare5.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square6)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare6.BackgroundImage = Properties.Resources.X;
                            btnSquare6.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare6.BackgroundImage = Properties.Resources.O;
                            btnSquare6.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square7)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare7.BackgroundImage = Properties.Resources.X;
                            btnSquare7.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare7.BackgroundImage = Properties.Resources.O;
                            btnSquare7.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square8)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare8.BackgroundImage = Properties.Resources.X;
                            btnSquare8.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare8.BackgroundImage = Properties.Resources.O;
                            btnSquare8.Enabled = false;
                            break;
                    }
                    switch (CurrentRoom.Board.Square9)
                    {
                        case TicTacToeSymbol.X:
                            btnSquare9.BackgroundImage = Properties.Resources.X;
                            btnSquare9.Enabled = false;
                            break;
                        case TicTacToeSymbol.O:
                            btnSquare9.BackgroundImage = Properties.Resources.O;
                            btnSquare9.Enabled = false;
                            break;
                    }
                });
            }
        }

        private void AddChatMessage(string msg)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(delegate ()
                {
                    rtxtChat.AppendText($"{msg}\n");
                });
            }
        }

        private void ClearBoard()
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(delegate ()
                {
                    List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];

                    foreach (Button button in buttons)
                        button.BackgroundImage = null;
                });
            }
        }
    }
}
