using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Sirenix.OdinInspector;
using UnityEngine;

namespace VOID.Generic.SaveLoad
{
    /// <summary>
    /// Manages profiles (save files) and caches currently active one.
    /// </summary>
    public class PlayerProfileManager : MonoBehaviour
    {
        public static PlayerProfileManager current { get; private set; }
        
        // Paths
        private static string directoryPath => Path.Combine(Application.persistentDataPath, "profiles");
        private static string fileNameTemplate => "{0}_{1}.profile";
        private static string fileNameMask => "*.profile";
        private static string fileNameWithDefaultProfile => "default";
        
        // Cache
        public PlayerProfile activeProfile { get; private set; }
        
        // Events
        public event Action<PlayerProfile> OnProfileChange; 

        private void Awake()
        {
            if (current)
            {
                Debug.LogWarning("Instance of " + nameof(PlayerProfileManager) + " already exists!");
                Destroy(this);
                return;
            }
            
            // Only one instance for whole game, this should manage player profiles
            // DontDestroyOnLoad works only on root game object so we need to unparent it
            current = this;
            current.transform.parent = null;
            DontDestroyOnLoad(this);
        }

        public PlayerProfile GetDefaultProfile()
        {
            var defaultPath = Path.Combine(directoryPath, fileNameWithDefaultProfile);
            
            // Default file not found
            if (!File.Exists(defaultPath)) return null;
            var guid = File.ReadAllText(defaultPath);
            
            // Profile not found, by guid from default file
            var file = Directory.GetFiles(directoryPath, string.Format(fileNameTemplate, "*", guid)).FirstOrDefault();
            if (file == null) return null;

            return JsonUtility.FromJson<PlayerProfile>(File.ReadAllText(file));
        }

        /// <summary>
        /// Parse all profiles from files and returns them.
        /// There is no caching, it will scan files everytime it is called.
        /// </summary>
        [Button]
        public List<PlayerProfile> GetProfiles()
        {
            if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);

            return Directory.GetFiles(directoryPath, fileNameMask)
                .Select(File.ReadAllText)
                .Select(JsonUtility.FromJson<PlayerProfile>)
                .ToList();
        }

        /// <summary>
        /// Creates new profile with given name and saves it as file.
        /// </summary>
        [Button]
        public PlayerProfile CreateProfile(string name)
        {
            if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);

            var newProfile = new PlayerProfile(name);
            SaveProfile(newProfile);
            
            return newProfile;
        }

        /// <summary>
        /// Removes given profile from files and if its currently active unselects it.
        /// </summary>
        /// <param name="playerProfile"></param>
        [Button]
        public void DeleteProfile([NotNull] PlayerProfile playerProfile)
        {
            if (playerProfile == activeProfile) SelectProfile(null);
            
            if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
            
            File.Delete(GetFullPathFor(playerProfile));
        }
        
        /// <summary>
        /// Saves currently active profile as file.
        /// </summary>
        public void SaveProfile()
        {
            SaveProfile(activeProfile);
        }
        
        /// <summary>
        /// Saves given profile as file.
        /// </summary>
        [Button]
        private void SaveProfile([NotNull] PlayerProfile playerProfile)
        {
            if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
            File.WriteAllText(GetFullPathFor(playerProfile), JsonUtility.ToJson(playerProfile, true));
        }

        /// <summary>
        /// Set active given profile. Will be selected automatically next time.
        /// </summary>
        [Button]
        public void SelectProfile([CanBeNull] PlayerProfile playerProfile)
        {
            activeProfile = playerProfile;
            OnProfileChange?.Invoke(activeProfile);

            // No profile found - create it for user
            activeProfile ??= CreateProfile("New Player Profile");
            
            // Our selected profile will be default one when starting game again
            File.WriteAllText(Path.Combine(directoryPath, fileNameWithDefaultProfile), activeProfile.guid);
        }

        private string GetFullPathFor([NotNull] PlayerProfile playerProfile)
        {
            var fileName = string.Format(fileNameTemplate, playerProfile.name, playerProfile.guid);
            return Path.Combine(directoryPath, fileName);
        }
    }
}