using Minesweeper.Config;
using UnityEngine;

namespace Minesweeper.Core
{
    public sealed class GameSettingsService : IGameSettingsService
    {
        private const int MinimumMinesCount = 1;

        private readonly MinesweeperGameConfig config;
        private readonly IGameSettingsStorage storage;
        private GameSettingsValue current;

        public GameSettingsService(MinesweeperGameConfig config, IGameSettingsStorage storage)
        {
            this.config = config;
            this.storage = storage;
            current = LoadInitialSettings();
        }

        public int SizeX => current.SizeX;
        public int SizeY => current.SizeY;
        public int MinesCount => current.MinesCount;
        public GameSettingsValue Current => current;

        public GameSettingsValue Clamp(GameSettingsValue value)
        {
            var sizeX = Mathf.Clamp(value.SizeX, config.MinSizeX, config.MaxSizeX);
            var sizeY = Mathf.Clamp(value.SizeY, config.MinSizeY, config.MaxSizeY);
            EnsureAtLeastTwoCells(ref sizeX, ref sizeY);

            var mines = Mathf.Clamp(value.MinesCount, MinimumMinesCount, GetMaxMines(sizeX, sizeY));
            return new GameSettingsValue(sizeX, sizeY, mines);
        }

        public bool ApplyAndSaveIfChanged(GameSettingsValue value)
        {
            var clamped = Clamp(value);
            if (current.Equals(clamped))
            {
                return false;
            }

            current = clamped;
            storage.Save(current);
            return true;
        }

        public int GetMaxMines(int sizeX, int sizeY)
        {
            return Mathf.Max(MinimumMinesCount, sizeX * sizeY - 1);
        }

        private GameSettingsValue LoadInitialSettings()
        {
            if (storage.TryLoad(out var saved))
            {
                return Clamp(saved);
            }

            return Clamp(new GameSettingsValue(config.MinSizeX, config.MinSizeY, MinimumMinesCount));
        }

        private void EnsureAtLeastTwoCells(ref int sizeX, ref int sizeY)
        {
            if (sizeX * sizeY > 1)
            {
                return;
            }

            if (sizeX < config.MaxSizeX)
            {
                sizeX++;
            }
            else if (sizeY < config.MaxSizeY)
            {
                sizeY++;
            }
        }
    }
}
