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

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

    public class DiplomacyCommandSelector : CommandSelector {
        private HeroId SelectedHeroId => ambassadorDropdownController.SelectedHeroId;
        private FactionId SelectedFactionId =>
                AvailableTargetFactionIds[targetFactionDropdown.value];

        public override bool IsAppropriateFor(AvailableCommand cmd) =>
                cmd.SealedValueCase == CommandType
                && cmd.DiplomacyCommand.Options.Any(
                           opt => opt.SealedValueCase !=
                                  DiplomacyOption.SealedValueOneofCase.RansomOfferOption);

        private DiplomacyAvailableCommand DiplomacyCommand => _availableCommand.DiplomacyCommand;

        public HeroDropdownController ambassadorDropdownController;
        public TMP_Dropdown targetFactionDropdown;
        public TMP_Text costText;

        public Toggle truceToggle;
        public Toggle allianceToggle;
        public Toggle inviteToggle;

        public TMP_Text truceLabel;
        public TMP_Text allianceLabel;
        public TMP_Text inviteLabel;

        public override AvailableCommand.SealedValueOneofCase CommandType =>
                AvailableCommand.SealedValueOneofCase.DiplomacyCommand;
        public override string HeaderString => "Diplomacy";
        public override string CommitButtonString {
            get {
                switch (SelectedDiplomacyOptionType) {
                    case DiplomacyOption.SealedValueOneofCase.TruceOption:
                        return "Commit Truce Offer";
                    case DiplomacyOption.SealedValueOneofCase.AllianceOption:
                        return "Commit Alliance Offer";
                    case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
                        return "Commit Break Alliance";
                    case DiplomacyOption.SealedValueOneofCase.InvitationOption:
                        return "Commit Invitation";
                    default: throw new KeyNotFoundException("Unknown diplomacy option");
                }
            }
        }
        public override List<HeroId> TargetedHeroIds => new List<HeroId> { SelectedHeroId };
        public override bool HeroIsTargetable(HeroId heroId) =>
                DiplomacyCommand.AvailableHeroIds.Contains(heroId);
        public override void AddTargetedHero(HeroId heroId) =>
                ambassadorDropdownController.SelectedHeroId = heroId;

        public override List<FactionId> TargetedFactionIds => new() { SelectedFactionId };
        public override bool FactionIsTargetable(FactionId factionId) =>
                AvailableTargetFactionIds.Contains(factionId);
        public override void AddTargetedFaction(FactionId factionId) =>
                targetFactionDropdown.value = AvailableTargetFactionIds.IndexOf(factionId);

        public override bool WarnOnCommitButton =>
                _model.ActiveFactions[_model.PlayerId.Value].Leaders.Contains(
                        ambassadorDropdownController.SelectedHeroId);
        public override string CommitWarningText =>
                "You're putting your faction leader at risk. Are you sure?";

        private List<FactionId> AvailableTargetFactionIds { get; set; }

        private DiplomacyOption.SealedValueOneofCase SelectedDiplomacyOptionType {
            get {
                if (truceToggle.isOn && HasTruce())
                    return DiplomacyOption.SealedValueOneofCase.TruceOption;
                if (allianceToggle.isOn && HasAlliance())
                    return DiplomacyOption.SealedValueOneofCase.AllianceOption;
                if (allianceToggle.isOn && HasBreakAlliance())
                    return DiplomacyOption.SealedValueOneofCase.BreakAllianceOption;
                if (inviteToggle.isOn && HasInvitation())
                    return DiplomacyOption.SealedValueOneofCase.InvitationOption;
                return DiplomacyOption.SealedValueOneofCase.None;
            }
        }

        protected override ProvinceId? TargetedProvince {
            get => null;
            set {
                if (value.HasValue) {
                    var targetFaction = _model.Provinces[value.Value].RulingFactionId;
                    if (targetFaction.HasValue) {
                        var dropdownIndex = AvailableTargetFactionIds.IndexOf(targetFaction.Value);
                        if (dropdownIndex != -1) {
                            targetFactionDropdown.value = dropdownIndex;
                            SetOptions();
                            costText.text = SelectedOptionCost.ToString();
                        }
                    }
                }
            }
        }

        private FactionId TargetFactionId(DiplomacyOption option) {
            switch (option.SealedValueCase) {
                case DiplomacyOption.SealedValueOneofCase.TruceOption:
                    return option.TruceOption.TargetFactionId;

                case DiplomacyOption.SealedValueOneofCase.AllianceOption:
                    return option.AllianceOption.TargetFactionId;

                case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
                    return option.BreakAllianceOption.TargetFactionId;

                case DiplomacyOption.SealedValueOneofCase.InvitationOption:
                    return option.InvitationOption.TargetFactionId;

                default: throw new ArgumentException("Unknown diplomacy option");
            }
        }

        private int GoldCost(DiplomacyOption option) {
            switch (option.SealedValueCase) {
                case DiplomacyOption.SealedValueOneofCase.TruceOption:
                    return option.TruceOption.GoldCost;

                case DiplomacyOption.SealedValueOneofCase.AllianceOption:
                    return option.AllianceOption.GoldCost;

                case DiplomacyOption.SealedValueOneofCase.BreakAllianceOption:
                    return option.BreakAllianceOption.GoldCost;

                case DiplomacyOption.SealedValueOneofCase.InvitationOption:
                    return option.InvitationOption.GoldCost;

                default: throw new ArgumentException("Unknown diplomacy option");
            }
        }

        private DiplomacyOption SelectedOption => DiplomacyCommand.Options.FirstOrDefault(
                opt => opt.SealedValueCase == SelectedDiplomacyOptionType &&
                       TargetFactionId(opt) == SelectedFactionId);

        override public void RefreshUI() {
            var previousValue = targetFactionDropdown.value;
            ambassadorDropdownController.AvailableHeroes =
                    DiplomacyCommand.AvailableHeroIds.Select(hid => _model.Heroes[hid]).ToList();
            ambassadorDropdownController.SelectedHeroId = DiplomacyCommand.RecommendedHeroId;

            AvailableTargetFactionIds =
                    DiplomacyCommand.Options
                            .Where(opt => opt.SealedValueCase !=
                                          DiplomacyOption.SealedValueOneofCase.RansomOfferOption)
                            .Select(TargetFactionId)
                            .Distinct()
                            .ToList();

            targetFactionDropdown.ClearOptions();
            targetFactionDropdown.AddOptions(
                    AvailableTargetFactionIds.Select(fid => _model.FactionName(fid)).ToList());

            if (previousValue >= 0 && previousValue < targetFactionDropdown.options.Count)
                targetFactionDropdown.value = previousValue;
            else
                targetFactionDropdown.value = 0;

            SetOptions();
            costText.text = SelectedOptionCost.ToString();
        }

        protected override void SetUpUI() {
            RefreshUI();
            targetFactionDropdown.value = 0;
        }

        public void ToggleChanged() { costText.text = SelectedOptionCost.ToString(); }

        public override SelectedCommand Command => new SelectedCommand {
            DiplomacyCommand =
                    new DiplomacySelectedCommand {
                        SentHeroId = SelectedHeroId,
                        TargetFactionId = SelectedFactionId,
                        SelectedOption = SelectedOption
                    }
        };

        public void TargetDropdownChanged(int value) { SetOptions(); }

        private bool HasTruce() {
            return DiplomacyCommand.Options.Any(
                    opt => opt.TruceOption?.TargetFactionId == SelectedFactionId);
        }

        private bool HasInvitation() {
            return DiplomacyCommand.Options.Any(
                    opt => opt.InvitationOption?.TargetFactionId == SelectedFactionId);
        }

        private bool HasAlliance() {
            return DiplomacyCommand.Options.Any(
                    opt => opt.AllianceOption?.TargetFactionId == SelectedFactionId);
        }
        private bool HasBreakAlliance() {
            return DiplomacyCommand.Options.Any(
                    opt => opt.BreakAllianceOption?.TargetFactionId == SelectedFactionId);
        }

        private void SetOptions() {
            var enableTruce = HasTruce();
            ConditionallyEnable(truceToggle, truceLabel, enableTruce);

            var enableInvite = HasInvitation();
            ConditionallyEnable(inviteToggle, inviteLabel, enableInvite);

            var enableAlliance = HasAlliance();
            var enableBreakAlliance = HasBreakAlliance();
            if (enableBreakAlliance) {
                allianceLabel.text = "Break Alliance";
                ConditionallyEnable(allianceToggle, allianceLabel, true);
            } else {
                allianceLabel.text = "Alliance";
                ConditionallyEnable(allianceToggle, allianceLabel, enableAlliance);
            }

            if (enableTruce) truceToggle.isOn = true;
            else if (enableInvite)
                inviteToggle.isOn = true;
            else if (enableAlliance || enableBreakAlliance)
                allianceToggle.isOn = true;
            else
                throw new ArgumentException("No diplomacy options available");

            costText.text = SelectedOptionCost.ToString();
        }

        private void ConditionallyEnable(Toggle toggle, TMP_Text label, bool enable) {
            toggle.gameObject.SetActive(enable);
            Color color = label.color;
            color.a = enable ? 1.0f : 0.25f;
            label.color = color;
        }

        private int SelectedOptionCost =>
                _availableCommand != null && SelectedOption != null ? GoldCost(SelectedOption) : 0;
    }
}
