using Jovian.EncounterSystem;
using Jovian.ZoneSystem;
using UnityEngine;

namespace Nox.Game {
    public class EncounterHandler {
        private readonly ZoneSystem zoneSystem;
        private readonly EncounterRegistry encounterRegistry;
        private readonly EncounterView encounterView;
        private string previousZoneId;
        private IEncounter activeEncounter;

        public EncounterHandler(ZoneSystem zoneSystem, EncounterRegistry encounterRegistry, EncounterPrefabs encounterPrefabs) {
            this.zoneSystem = zoneSystem;
            this.encounterRegistry = encounterRegistry;
            encounterView = new EncounterView(encounterPrefabs);
            encounterView.OptionSelected += OnOptionSelected;
        }

        public bool AskForRandomEncounter(ZoneContext zoneContext, string encounterTableId, out IEncounter encounter) {
            return ResolveEncounter(zoneContext, encounterTableId, out encounter);
        }

        public bool AskForRandomEncounterOfType(ZoneContext zoneContext, string encounterTableId, out IEncounter encounter, IEncounterKind encounterKind) {
            return ResolveEncounter(zoneContext, encounterTableId, out encounter, encounterKind);
        }

        public bool AskForEncounter(string encounterId, out IEncounter encounter) {
            return encounterRegistry.GetEncounters().TryGetValue(encounterId, out encounter);
        }

        private bool ResolveEncounter(ZoneContext zoneContext, string encounterTableId, out IEncounter encounter, IEncounterKind encounterKind = null) {
            encounter = null;
            if(zoneContext.isSafe) {
                return false;
            }

            var randomChance = Random.Range(0f, 1f);
            var shouldTrigger = randomChance <= zoneContext.finalEncounterChance;

            if(!shouldTrigger) {
                Debug.Log($"Rolled for encounter '{encounterTableId}': {randomChance:F2}/{zoneContext.finalEncounterChance:F2} -> none");
                return false;
            }

            encounter = encounterKind == null
                ? encounterRegistry.GetRandomEncounter(encounterTableId)
                : encounterRegistry.GetRandomEncounter(encounterTableId, encounterKind);

            var resultName = encounter?.EncounterDefinition?.name ?? "none";
            Debug.Log($"Rolled for encounter '{encounterTableId}': {randomChance:F2}/{zoneContext.finalEncounterChance:F2} -> {resultName}");
            return encounter != null;
        }

        private void VerifyZones(Vector3 position) {
            var zoneContext = zoneSystem.QueryZone(position);
            if(string.IsNullOrEmpty(zoneContext.resolvedZoneId)) {
                return;
            }
            var currentZoneId = zoneContext.resolvedZoneId;

            if(currentZoneId != previousZoneId) {
                if(!string.IsNullOrEmpty(currentZoneId)) {
                    Debug.Log($"Entered zone: {currentZoneId} (encounter: {zoneContext.encounterTableId}, safe: {zoneContext.isSafe})");
                    if(ResolveEncounter(zoneContext, zoneContext.encounterTableId, out var encounter)) {
                        TriggerEncounter(encounter);
                    }

                }
                else if(!string.IsNullOrEmpty(previousZoneId)) {
                    Debug.Log($"Left zone: {previousZoneId}");
                }
                previousZoneId = currentZoneId;
            }
        }

        private void TriggerEncounter(IEncounter encounter) {
            switch(encounter.EncounterDefinition.Kind) {
                case CombatKind:
                    return;
                default:
                    activeEncounter = encounter;
                    encounterView?.SetCurrentEncounter(encounter);
                    encounterView?.Show();
                    break;
            }
        }

        private void OnOptionSelected(int optionIndex) {
            if(activeEncounter == null) {
                return;
            }

            var options = activeEncounter.EncounterDialogOptionSet?.options;
            if(options == null || optionIndex < 0 || optionIndex >= options.Count) {
                return;
            }

            ResolveOption(activeEncounter, options[optionIndex]);
            encounterView?.Hide();
            activeEncounter = null;
        }

        private void ResolveOption(IEncounter encounter, EncounterDialogOption option) {
            if(option?.events == null) {
                return;
            }

            for(var i = 0; i < option.events.Count; i++) {
                var encounterEvent = option.events[i];
                if(encounterEvent == null) {
                    continue;
                }
                switch(encounterEvent) {
                    case ChainToEncounterEvent chain:
                        if(AskForEncounter(chain.nextEncounterId, out var next)) {
                            TriggerEncounter(next);
                            return;
                        }
                        break;
                    case LogEvent log:
                        Debug.Log($"[Encounter '{encounter.EncounterDefinition.id}'] {log.message}");
                        break;
                    case StartCombatEvent _:
                    case GiveRewardEvent _:
                        Debug.Log($"[Encounter] unhandled event {encounterEvent.GetType().Name}");
                        break;
                }
            }
        }

        public void CheckForEncounters(Vector3 position) {
            VerifyZones(position);
        }

        public void Tick() { }

        public void Dispose() {
            if(encounterView != null) {
                encounterView.OptionSelected -= OnOptionSelected;
                encounterView.Dispose();
            }
            activeEncounter = null;
        }
    }
}
