using Minesweeper.Core;
using Minesweeper.ECS;

namespace Minesweeper.Commands
{
    public sealed class SelectFieldCommandHandler : IGameCommandHandler<SelectFieldCommand>
    {
        private readonly IGameStateService gameStateService;

        public SelectFieldCommandHandler(IGameStateService gameStateService)
        {
            this.gameStateService = gameStateService;
        }

        public void Handle(SelectFieldCommand command)
        {
            gameStateService.SetState(GameState.FieldSelection);
        }
    }

    public sealed class StartGameCommandHandler : IGameCommandHandler<StartGameCommand>
    {
        private readonly IBoardEcsSyncService boardEcsSyncService;
        private readonly IBoardService boardService;
        private readonly IGamePauseService pauseService;
        private readonly IGameStateService gameStateService;
        private readonly IGameTimerService timerService;

        public StartGameCommandHandler(IBoardService boardService, IBoardEcsSyncService boardEcsSyncService, IGamePauseService pauseService, IGameStateService gameStateService, IGameTimerService timerService)
        {
            this.boardService = boardService;
            this.boardEcsSyncService = boardEcsSyncService;
            this.pauseService = pauseService;
            this.gameStateService = gameStateService;
            this.timerService = timerService;
        }

        public void Handle(StartGameCommand command)
        {
            pauseService.Resume();
            timerService.Reset();
            boardService.InitializeEmptyBoard();
            gameStateService.SetState(GameState.Preparing);
            boardEcsSyncService.SyncBoard(boardService);
            boardEcsSyncService.SyncGameState(gameStateService.Current, false);
        }
    }

    public sealed class OpenCellCommandHandler : IGameCommandHandler<OpenCellCommand>
    {
        private readonly IBoardEcsSyncService boardEcsSyncService;
        private readonly IBoardService boardService;
        private readonly IGameStateService gameStateService;
        private readonly IGamePauseService pauseService;

        public OpenCellCommandHandler(IBoardService boardService, IBoardEcsSyncService boardEcsSyncService, IGameStateService gameStateService, IGamePauseService pauseService)
        {
            this.boardService = boardService;
            this.boardEcsSyncService = boardEcsSyncService;
            this.gameStateService = gameStateService;
            this.pauseService = pauseService;
        }

        public void Handle(OpenCellCommand command)
        {
            if (pauseService.IsPaused)
            {
                return;
            }

            var state = gameStateService.Current;
            if (state != GameState.Preparing && state != GameState.Playing)
            {
                return;
            }

            if (state == GameState.Preparing)
            {
                if (boardService.TryGetCell(command.X, command.Y, out var cell) && cell.IsFlagged)
                {
                    return;
                }

                if (!boardService.GenerateAfterFirstClick(command.X, command.Y))
                {
                    return;
                }
            }

            var result = boardService.OpenCell(command.X, command.Y);
            if (result.Invalid || !result.Changed)
            {
                return;
            }

            if (result.HitMine)
            {
                gameStateService.SetState(GameState.Lost);
            }
            else if (result.Won)
            {
                gameStateService.SetState(GameState.Won);
            }
            else if (state == GameState.Preparing)
            {
                gameStateService.SetState(GameState.Playing);
            }

            boardEcsSyncService.SyncBoard(boardService);
            boardEcsSyncService.SyncGameState(gameStateService.Current, boardService.IsGenerated);
        }
    }

    public sealed class ToggleFlagCommandHandler : IGameCommandHandler<ToggleFlagCommand>
    {
        private readonly IBoardEcsSyncService boardEcsSyncService;
        private readonly IBoardService boardService;
        private readonly IGameStateService gameStateService;
        private readonly IGamePauseService pauseService;

        public ToggleFlagCommandHandler(IBoardService boardService, IBoardEcsSyncService boardEcsSyncService, IGameStateService gameStateService, IGamePauseService pauseService)
        {
            this.boardService = boardService;
            this.boardEcsSyncService = boardEcsSyncService;
            this.gameStateService = gameStateService;
            this.pauseService = pauseService;
        }

        public void Handle(ToggleFlagCommand command)
        {
            if (pauseService.IsPaused)
            {
                return;
            }

            var state = gameStateService.Current;
            if (state != GameState.Preparing && state != GameState.Playing)
            {
                return;
            }

            var result = boardService.ToggleFlag(command.X, command.Y);
            if (result.Invalid || !result.Changed)
            {
                return;
            }

            boardEcsSyncService.SyncBoard(boardService);
            boardEcsSyncService.SyncGameState(gameStateService.Current, boardService.IsGenerated);
        }
    }

    public sealed class RestartCommandHandler : IGameCommandHandler<RestartCommand>
    {
        private readonly IBoardEcsSyncService boardEcsSyncService;
        private readonly IBoardService boardService;
        private readonly IGamePauseService pauseService;
        private readonly IGameStateService gameStateService;
        private readonly IGameTimerService timerService;

        public RestartCommandHandler(IBoardService boardService, IBoardEcsSyncService boardEcsSyncService, IGamePauseService pauseService, IGameStateService gameStateService, IGameTimerService timerService)
        {
            this.boardService = boardService;
            this.boardEcsSyncService = boardEcsSyncService;
            this.pauseService = pauseService;
            this.gameStateService = gameStateService;
            this.timerService = timerService;
        }

        public void Handle(RestartCommand command)
        {
            var shouldNotifyReset = gameStateService.Current == GameState.Preparing;
            pauseService.Resume();
            timerService.Reset();
            boardService.InitializeEmptyBoard();
            gameStateService.SetState(GameState.Preparing);
            boardEcsSyncService.SyncBoard(boardService);
            boardEcsSyncService.SyncGameState(gameStateService.Current, false);

            if (shouldNotifyReset)
            {
                gameStateService.NotifyCurrentStateChanged();
            }
        }
    }

    public sealed class PauseCommandHandler : IGameCommandHandler<PauseCommand>
    {
        private readonly IGamePauseService pauseService;
        private readonly IGameStateService gameStateService;

        public PauseCommandHandler(IGamePauseService pauseService, IGameStateService gameStateService)
        {
            this.pauseService = pauseService;
            this.gameStateService = gameStateService;
        }

        public void Handle(PauseCommand command)
        {
            if (gameStateService.Current == GameState.Playing)
            {
                pauseService.Pause();
            }
        }
    }

    public sealed class ResumeCommandHandler : IGameCommandHandler<ResumeCommand>
    {
        private readonly IGamePauseService pauseService;

        public ResumeCommandHandler(IGamePauseService pauseService)
        {
            this.pauseService = pauseService;
        }

        public void Handle(ResumeCommand command)
        {
            pauseService.Resume();
        }
    }

    public sealed class GoToMenuCommandHandler : IGameCommandHandler<GoToMenuCommand>
    {
        private readonly IBoardEcsSyncService boardEcsSyncService;
        private readonly IGamePauseService pauseService;
        private readonly IGameStateService gameStateService;
        private readonly IGameTimerService timerService;

        public GoToMenuCommandHandler(IBoardEcsSyncService boardEcsSyncService, IGamePauseService pauseService, IGameStateService gameStateService, IGameTimerService timerService)
        {
            this.boardEcsSyncService = boardEcsSyncService;
            this.pauseService = pauseService;
            this.gameStateService = gameStateService;
            this.timerService = timerService;
        }

        public void Handle(GoToMenuCommand command)
        {
            pauseService.Resume();
            timerService.Reset();
            gameStateService.SetState(GameState.FieldSelection);
            boardEcsSyncService.ClearBoard();
            boardEcsSyncService.SyncGameState(gameStateService.Current, false);
        }
    }
}
