using System;
using System.Collections.Generic;
using Intrepid.Diagnostics;
using Intrepid.Integration.Persistence.DTOs;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Integration.Persistence
{
    public abstract class AbstractDataStorage<T, V> : SingletonMonoBehaviour<T>, IDataStorage
        where T : AbstractDataStorage<T, V>
        where V : VersionableDTO
    {
        [GroupInjections, SerializeField] private GameSlotPersistenceService gameSlotPersistenceService;
        
        public abstract int Order { get; }
        protected abstract void ValidateDataOrError(List<string> errors);
        protected abstract V Marshal();
        protected abstract void Unmarshal(GameDataSlotType slotType, GameSlotDTO gameSlot);
        
        public bool IsDataValid(out List<string> errors)
        {
            errors = new List<string>();
            ValidateDataOrError(errors);
            return errors.Count == 0;
        }
        
        public void MarshalData(GameSlotDTO gameSlot)
        {
            var v = Marshal();
            switch (v)
            {
                case SentinelDTO sentinelData:
                    gameSlot.SentinelData = sentinelData;
                    gameSlot.SentinelData.Version = Versions.Sentinel;
                    break;
                case WorldDTO worldData:
                    gameSlot.WorldData = worldData;
                    gameSlot.WorldData.Version = Versions.World;
                    break;
                default:
                    throw new Exception($"Cannot marshal data of type {v.GetType()} to GameSlotDTO");
            }
        }

        protected virtual void AwakeDataStorage()
        {
        }

        private void Awake()
        {
            SingletonInstantiation(false, _ => AwakeDataStorage());
        }

        private void OnEnable()
        {
            StorageAssembler.Instance.Register(this);
            var slotType = gameSlotPersistenceService.CurrentGameDataSlotTypeLoaded;
            var gameSlot = gameSlotPersistenceService.CurrentGameSlot;
            Unmarshal(slotType, gameSlot);
        }

        private void OnDisable()
        {
            StorageAssembler.Instance.Unregister(this);
        }
    }
}