using Jovian.SaveSystem;
using Jovian.ZoneSystem;
using Nox.Core;
using Nox.Platform;
using Nox.Game.UI;
using System.Linq;
using UnityEngine;
using UnityEngine.AddressableAssets;
using PlayMode = Nox.Core.PlayMode;

namespace Nox.Game {
    public class AdventureData {
        public bool isPartyMoving;
        public int currentDay = 0;
        public int suppliesAvailable = -1;
        public float currentTime = -1f;
        public DayPhase currentDayPhase = DayPhase.Morning;
    }

    public class AdventurePlayMode : IPlayMode {
        private readonly PlatformSettings platformSettings;
        private readonly PlayModeSettings bootstrapSettings;
        private readonly GameDataState gameDataState;
        private readonly ISaveSystem saveSystem;
        private PartyDefinition partyDefinition;
        private AdventureData adventureData;
        private AdventureModePrefabs scenePrefabs;
        private ICameraController cameraController;
        private MapReference mapRef;
        private PartyMovementHandler partyMovementHandler;
        private PartyReference partyRef;
        private MapLocationsReference mapLocationsReference;
        private InputSystem_Actions inputActions;
        private AdventureView adventureView;
        private ZoneSystem zoneSystem;
        private GuiReferences guiReferences;
        private AdventureSettings adventureSettings;
        private TimeHandler timeHandler;
        private PartyInventoryHandler partyInventoryHandler;

        public AdventurePlayMode(
            PlatformSettings platformSettings,
            PartyDefinition partyDefinition,
            PlayModeSettings bootstrapSettings,
            GameDataState gameDataState,
            ISaveSystem saveSystem,
            AdventureSettings adventureSettings,
            AdventureData adventureData) {
            this.platformSettings = platformSettings;
            this.partyDefinition = partyDefinition;
            this.bootstrapSettings = bootstrapSettings;
            this.gameDataState = gameDataState;
            this.saveSystem = saveSystem;
            this.adventureSettings = adventureSettings;
            this.adventureData = adventureData;
        }

        public bool IsGameModeInitialized { get; private set; }

        public void EnterPlayMode() {
            inputActions = platformSettings.inputSettings.inputActions;
            if(IsGameModeInitialized) {
                inputActions.Player.Enable();
                inputActions.UI.PauseMenu.Enable();
                partyMovementHandler.ConsumeNextClick();
                return;
            }
            Addressables.LoadSceneAsync(bootstrapSettings.gameModeData.FirstOrDefault(g => g.playMode == PlayMode.Adventure)?.scene)
                .WaitForCompletion().ActivateAsync().completed += InitializeGameMode;
        }

        private void InitializeGameMode(AsyncOperation obj) {
            inputActions.Player.Enable();
            inputActions.UI.PauseMenu.Enable();
            Debug.Log("Entering Adventure Play Mode");
            if(partyDefinition == null) {
                var sessions = saveSystem.GetAllSessions().OrderByDescending(s => s.lastSaveDateUtc).ToList();
                if(sessions.Count == 0) {
                    return;
                }

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

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

                NoxSaveData.RestoreSavedData(saveSystem, gameDataState, ref adventureData);
                Debug.LogWarning("AdventurePlayMode started from the Adventure Scene. Loading the last Autosave");
            }

            scenePrefabs ??= Addressables.LoadAssetAsync<AdventureModePrefabs>("AdventureMapPrefabs").WaitForCompletion();
            mapRef ??= Object.FindFirstObjectByType<MapReference>();
            partyRef ??= Object.FindFirstObjectByType<PartyReference>();
            if(partyRef && gameDataState.savedPartyPosition.HasValue) {
                partyRef.transform.position = gameDataState.savedPartyPosition.Value;
            }
            mapLocationsReference ??= Object.FindFirstObjectByType<MapLocationsReference>();
            if(!mapRef) {
                mapRef ??= Object.Instantiate(scenePrefabs.mapReferencePrefab);
            }
            cameraController ??= new CameraController(platformSettings, mapRef);
            cameraController.Initialize();

            if(adventureData.suppliesAvailable == -1) {
                adventureData.suppliesAvailable = adventureSettings.maxSupplies;
            }
            if(Mathf.Approximately(adventureData.currentTime, -1f)) {
                adventureData.currentTime = 0.25f;
            }

            partyInventoryHandler ??= new PartyInventoryHandler(adventureData, adventureSettings);
            partyInventoryHandler.Initialize();

            timeHandler ??= new TimeHandler(adventureSettings, adventureData);
            zoneSystem ??= new ZoneSystem(mapRef.zonesObjectHolder);

            partyMovementHandler ??= new PartyMovementHandler(partyRef, cameraController, mapLocationsReference, platformSettings.inputSettings, zoneSystem, adventureData, adventureSettings);
            partyMovementHandler.Initialize();

            guiReferences ??= Object.FindFirstObjectByType<GuiReferences>();
            adventureView ??= new AdventureView(gameDataState, guiReferences, inputActions, adventureData, adventureSettings);
            adventureView.Initialize();

            IsGameModeInitialized = true;
        }

        public void Tick() {
            if(!IsGameModeInitialized) {
                return;
            }

            timeHandler.Tick();
            partyInventoryHandler.Tick();
            partyMovementHandler.Tick();
            adventureView.Tick();

            if(inputActions.UI.PauseMenu.WasPerformedThisFrame()) {
                gameDataState.ChangePlayMode(PlayMode.PauseMenu);
            }
        }
        public void LateTick() {
            if(!IsGameModeInitialized) {
                return;
            }
            cameraController.Tick();
        }
        public NoxSavedDataSet CaptureNoxSaveData() {
            return new NoxSavedDataSet {
                activePlayMode = PlayMode.Adventure,
                partyDefinition = partyDefinition,
                partyPosition = partyRef ? SerializableVector3.FromVector3(partyRef.transform.position) : SerializableVector3.Zero
            };
        }

        public void ExitGameMode() {
            inputActions.Player.Disable();
            inputActions.UI.PauseMenu.Disable();
        }
        public void Dispose() {
            cameraController.Dispose();
            partyMovementHandler.Dispose();
        }
    }

}
