using Jovian.SaveSystem;
using Jovian.InGameLogging;
using Nox.Core;
using System;
using ZLinq;
using UnityEngine;
using PlayMode = Nox.Core.PlayMode;

namespace Nox.Game {

    public class NoxSaveData {
        public static NoxSavedDataSet RestoreSavedData(
            ISaveSystem saveSystem,
            GameDataState gameDataState,
            ref AdventureData adventureData,
            IGameLogStore gameLogStore = null) {
            var sessions = saveSystem.GetAllSessions().AsValueEnumerable().OrderByDescending(s => s.lastSaveDateUtc).ToList();
            if(sessions.Count == 0) {
                return null;
            }

            var latestSession = sessions[0];
            var slots = saveSystem.GetSlots(latestSession.sessionId).AsValueEnumerable().OrderByDescending(s => s.timestampUtc).ToList();
            if(slots.Count == 0) {
                return null;
            }

            var latestSlot = slots[0];
            var saveData = saveSystem.Load<NoxSavedDataSet>(latestSlot);
            Debug.Log($"Loaded save {latestSlot.DisplayLabel}");
            if(saveData == null) {
                Debug.LogError("Failed to load save data");
                return null;
            }

            gameDataState.activeSessionId = latestSession.sessionId;
            gameDataState.savedPartyPosition = saveData.partyPosition.ToVector3();
            gameDataState.ActiveParty = saveData.activeParty;
          adventureData.isPartyMoving = saveData.adventureData.isPartyMoving;
            adventureData.isEncounterActive = saveData.adventureData.isEncounterActive;
            adventureData.currentDay = saveData.adventureData.currentDay;
            adventureData.suppliesAvailable = saveData.adventureData.suppliesAvailable;
            adventureData.currentTime = saveData.adventureData.currentTime;
            adventureData.currentDayPhase = saveData.adventureData.currentDayPhase;
            adventureData.completedEncounterIds.Clear();
            adventureData.completedEncounterIds.AddRange(saveData.adventureData.completedEncounterIds);

            if(gameLogStore != null && saveData.gameLogData != null) {
                gameLogStore.RestoreFromSaveData(saveData.gameLogData);
            }

            return saveData;
        }
    }

    /// <summary>
    /// The game's save data snapshot. Contains all state needed to restore a game session.
    /// This is the TData passed to the save system package.
    /// </summary>
    [Serializable]
    public sealed class NoxSavedDataSet {
        // game state
        public PlayMode activePlayMode;

        //game mode specific data
        public AdventureData adventureData;

        // Party
        public PartyDefinition activeParty;
        public SerializableVector3 partyPosition;

        // In-game log
        public GameLogSaveData gameLogData;
    }

    /// <summary>
    /// JSON-friendly Vector3 representation for save data.
    /// </summary>
    [Serializable]
    public struct SerializableVector3 {
        public float x;
        public float y;
        public float z;

        public static SerializableVector3 Zero => new SerializableVector3 { x = 0, y = 0, z = 0 };

        public static SerializableVector3 FromVector3(Vector3 value) {
            return new SerializableVector3 { x = value.x, y = value.y, z = value.z };
        }

        public Vector3 ToVector3() {
            return new Vector3(x, y, z);
        }
    }
}
