using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Net.Eagle0.Eagle.Api;
using Net.Eagle0.Eagle.Api.Command.Util;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Views;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace eagle {
    using ProvinceId = Int32;
    using HeroId = Int32;

    public class ManagePrisonersCommandSelector : CommandSelector {
        // Unity accessors
        public Toggle ReleaseToggle;
        public Toggle MoveToggle;
        public Toggle ExileToggle;
        public Toggle ExecuteToggle;
        public Toggle ReturnToggle;
        public ToggleGroup decisionToggleGroup;

        public TMP_Dropdown moveToDropdown;

        public HeroDetailsController heroDetails;

        public TMP_Text MessageText;

        public Button NextHeroButton;

        public HeroView SelectedHero => SelectedPrisonerToManage.Prisoner.Hero;

        public override AvailableCommand.SealedValueOneofCase CommandType =>
                AvailableCommand.SealedValueOneofCase.ManagePrisonerCommand;

        public override List<HeroId> TargetedHeroIds => new() { SelectedHero.Id };

        public override bool HeroIsTargetable(HeroId heroId) {
            if (_availableCommand == null || _availableCommand.ManagePrisonerCommand == null) {
                return false;
            }
            return ManagePrisonersCommand.Prisoners.Any(p => p.Prisoner.Hero.Id == heroId);
        }

        public override void AddTargetedHero(HeroId heroId) {
            // Find the prisoner with this heroId and select it
            for (int i = 0; i < ManagePrisonersCommand.Prisoners.Count; i++) {
                if (ManagePrisonersCommand.Prisoners[i].Prisoner.Hero.Id == heroId) {
                    _selectedHeroIndex = i;
                    DisplaySelectedHero();
                    return;
                }
            }
        }
        public override string HeaderString => "Manage Prisoners";
        public override string CommitButtonString =>
                $"Commit {DisplayNames.PrisonerManagementTypeName(SelectedOption)}";

        private int _selectedHeroIndex;
        private PrisonerToManage SelectedPrisonerToManage =>
                ManagePrisonersCommand.Prisoners[_selectedHeroIndex];

        private ManagePrisonersAvailableCommand ManagePrisonersCommand =>
                _availableCommand.ManagePrisonerCommand;
        private ProvinceId ActingProvinceId => ManagePrisonersCommand.ActingProvinceId;

        public override bool WarnOnCommitButton => false;
        public override string CommitWarningText =>
                $"This will create a treaty with {_model.FactionName(SelectedHero.FactionId.Value)}";

        private List<PrisonerManagementOption> MoveOptions =>
                SelectedPrisonerToManage.AvailableOptions
                        .Where(opt => opt.SealedValueCase ==
                                      PrisonerManagementOption.SealedValueOneofCase.Move)
                        .ToList();

        protected override ProvinceId? TargetedProvince {
            get {
                if (MoveToggle.isOn && moveToDropdown.value >= 0 &&
                    moveToDropdown.value < MoveOptions.Count) {
                    return MoveOptions[moveToDropdown.value].Move.ToProvinceId;
                }
                return null;
            }
            set {
                if (!value.HasValue) return;

                var moveOptions = MoveOptions;
                int index = moveOptions.FindIndex(opt => opt.Move.ToProvinceId == value.Value);
                if (index != -1) {
                    MoveToggle.isOn = true;
                    moveToDropdown.value = index;
                    moveToDropdown.gameObject.SetActive(true);
                }
            }
        }

        private PrisonerManagementOption SelectedOption {
            get {
                if (ExecuteToggle.isOn)
                    return SelectedPrisonerToManage.AvailableOptions.First(
                            opt => opt.SealedValueCase ==
                                   PrisonerManagementOption.SealedValueOneofCase.Execute);
                else if (MoveToggle.isOn)
                    return SelectedPrisonerToManage.AvailableOptions
                            .Where(opt => opt.SealedValueCase ==
                                          PrisonerManagementOption.SealedValueOneofCase.Move)
                            .ElementAt(moveToDropdown.value);
                else if (ReleaseToggle.isOn)
                    return SelectedPrisonerToManage.AvailableOptions.First(
                            opt => opt.SealedValueCase ==
                                   PrisonerManagementOption.SealedValueOneofCase.Release);
                else if (ExileToggle.isOn)
                    return SelectedPrisonerToManage.AvailableOptions.First(
                            opt => opt.SealedValueCase ==
                                   PrisonerManagementOption.SealedValueOneofCase.Exile);
                else if (ReturnToggle.isOn)
                    return SelectedPrisonerToManage.AvailableOptions.First(
                            opt => opt.SealedValueCase ==
                                   PrisonerManagementOption.SealedValueOneofCase.Return);
                else
                    throw new ArgumentOutOfRangeException("Unknown prisoner management option");
            }
        }

        protected override void SetUpUI() {
            if (_selectedHeroIndex >= ManagePrisonersCommand.Prisoners.Count)
                _selectedHeroIndex = 0;
            NextHeroButton.gameObject.SetActive(ManagePrisonersCommand.Prisoners.Count > 1);
            DisplaySelectedHero();
        }

        public override SelectedCommand Command => new SelectedCommand {
            ManagePrisonersCommand =
                    new ManagePrisonersSelectedCommand {
                        PrisonerHeroId = SelectedHero.Id,
                        ChosenOption = SelectedOption
                    }
        };

        public void NextHeroButtonClicked() {
            _selectedHeroIndex++;
            if (_selectedHeroIndex >= ManagePrisonersCommand.Prisoners.Count) {
                _selectedHeroIndex = 0;
            }

            DisplaySelectedHero();
        }

        public void MoveToggled(bool value) { moveToDropdown.gameObject.SetActive(value); }

        private void DisplaySelectedHero() {
            heroDetails.SetHero(SelectedHero, _model);

            moveToDropdown.options =
                    SelectedPrisonerToManage.AvailableOptions
                            .Where(opt => opt.SealedValueCase ==
                                          PrisonerManagementOption.SealedValueOneofCase.Move)
                            .Select(opt => _model.Provinces[opt.Move.ToProvinceId].Name)
                            .Select(name => new TMP_Dropdown.OptionData(name))
                            .ToList();

            MessageText.text = GetQuestMessagesForPrisoner(SelectedHero.Id);

            var options = SelectedPrisonerToManage.AvailableOptions;
            bool hasRelease = options.Any(
                    opt => opt.SealedValueCase ==
                           PrisonerManagementOption.SealedValueOneofCase.Release);
            bool hasMove = options.Any(
                    opt => opt.SealedValueCase ==
                           PrisonerManagementOption.SealedValueOneofCase.Move);
            bool hasExile = options.Any(
                    opt => opt.SealedValueCase ==
                           PrisonerManagementOption.SealedValueOneofCase.Exile);
            bool hasExecute = options.Any(
                    opt => opt.SealedValueCase ==
                           PrisonerManagementOption.SealedValueOneofCase.Execute);
            bool hasReturn = options.Any(
                    opt => opt.SealedValueCase ==
                           PrisonerManagementOption.SealedValueOneofCase.Return);

            ConfigureToggle(ReleaseToggle, hasRelease);
            ConfigureToggle(MoveToggle, hasMove);
            ConfigureToggle(ExileToggle, hasExile);
            ConfigureToggle(ExecuteToggle, hasExecute);
            ConfigureToggle(ReturnToggle, hasReturn);

            // Select first available option (priority order: Release > Move > Exile > Execute >
            // Return)
            if (hasRelease) ReleaseToggle.isOn = true;
            else if (hasMove)
                MoveToggle.isOn = true;
            else if (hasExile)
                ExileToggle.isOn = true;
            else if (hasExecute)
                ExecuteToggle.isOn = true;
            else if (hasReturn)
                ReturnToggle.isOn = true;

            // Update dropdown visibility based on selected toggle
            moveToDropdown.gameObject.SetActive(MoveToggle.isOn);
        }

        private string GetQuestMessagesForPrisoner(HeroId prisonerId) {
            var questMessages = new List<string>();

            foreach (var province in _model.Provinces.Values) {
                if (province.FullInfo?.UnaffiliatedHeroes == null) continue;

                foreach (var uh in province.FullInfo.UnaffiliatedHeroes) {
                    if (uh.RecruitmentInfo?.Status != RecruitmentStatus.HasQuest) continue;
                    var quest = uh.RecruitmentInfo.Quest;
                    if (quest?.Details == null) continue;

                    var (matches, actionVerb) = GetPrisonerQuestInfo(quest.Details, prisonerId);
                    if (!matches) continue;

                    var heroName = GetHeroDisplayName(uh.HeroId);
                    questMessages.Add($"{heroName} wants you to {actionVerb} this prisoner.");
                }
            }

            return questMessages.Count > 0 ? string.Join("\n", questMessages) : "";
        }

        private (bool matches, string actionVerb)
                GetPrisonerQuestInfo(QuestDetails details, HeroId prisonerId) {
            switch (details.SealedValueCase) {
                case QuestDetails.SealedValueOneofCase.ExecutePrisonerQuest:
                    if (details.ExecutePrisonerQuest.PrisonerHeroId == prisonerId) {
                        return (true, "execute");
                    }
                    break;
                case QuestDetails.SealedValueOneofCase.ExilePrisonerQuest:
                    if (details.ExilePrisonerQuest.PrisonerHeroId == prisonerId) {
                        return (true, "exile");
                    }
                    break;
                case QuestDetails.SealedValueOneofCase.FreePrisonerQuest:
                    if (details.FreePrisonerQuest.PrisonerHeroId == prisonerId) {
                        return (true, "release");
                    }
                    break;
                case QuestDetails.SealedValueOneofCase.ReturnPrisonerQuest:
                    if (details.ReturnPrisonerQuest.PrisonerHeroId == prisonerId) {
                        var factionName =
                                _model.FactionName(details.ReturnPrisonerQuest.ToFactionId);
                        return (true, $"return to {factionName}");
                    }
                    break;
            }
            return (false, null);
        }

        private string GetHeroDisplayName(HeroId heroId) {
            if (!_model.Heroes.TryGetValue(heroId, out var hero)) { return "A free hero"; }
            var textEntry = ClientTextProvider.Provider.GetTextEntry(hero.NameTextId);
            return textEntry != null && !string.IsNullOrEmpty(textEntry.Text) ? textEntry.Text
                                                                              : "A free hero";
        }

        private const float DisabledAlpha = 0.35f;

        private void ConfigureToggle(Toggle toggle, bool available) {
            if (toggle == null) return;

            toggle.gameObject.SetActive(true);
            toggle.interactable = available;

            if (!available) { toggle.isOn = false; }

            toggle.group = available ? decisionToggleGroup : null;

            var canvasGroup = toggle.GetComponent<CanvasGroup>();
            if (canvasGroup == null) {
                canvasGroup = toggle.gameObject.AddComponent<CanvasGroup>();
            }
            canvasGroup.alpha = available ? 1f : DisabledAlpha;
        }
    }
}