using System;
using System.Collections.Generic;
using System.Linq;
using Net.Eagle0.Eagle.Api;
using Net.Eagle0.Eagle.Api.Command.Util;
using Net.Eagle0.Eagle.Common;
using TMPro;
using UnityEditor;

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

    public class DefendCommandSelector : CommandSelector {
        private GeneratedTextListener warningTextListener;
        private string cachedWarningText = "";
        private int RallyPointProvinceIndex => rallyPointDropdown.value;
        private List<CombatUnit> SelectedActingUnits => unitSelector.SelectedActingUnits;

        public TMP_Text marchText;
        public TMP_Text commanderCountText;
        public TMP_Text troopCountText;

        public EventBasedUnitSelector unitSelector;
        public TMP_Dropdown rallyPointDropdown;

        public override AvailableCommand.SealedValueOneofCase CommandType =>
                AvailableCommand.SealedValueOneofCase.DefendCommand;
        public override string HeaderString => "Defend";
        public override string CommitButtonString => "Commit Defend";

        public override List<HeroId> TargetedHeroIds =>
                unitSelector.SelectedIds.Where(u => u.heroId.HasValue)
                        .Select(u => u.heroId.Value)
                        .ToList();
        public override bool HeroIsTargetable(HeroId heroId) =>
                unitSelector.HeroIsTargetable(heroId);
        public override void AddTargetedHero(HeroId heroId) => unitSelector.AddTargetedHero(heroId);
        public override void RemoveTargetedHero(HeroId heroId) =>
                unitSelector.RemoveTargetedHero(heroId);

        public override List<BattalionId> TargetedBattalionIds =>
                unitSelector.SelectedIds.Where(u => u.battalion != null)
                        .Select(u => u.battalion.Id)
                        .ToList();
        public override bool BattalionIsTargetable(BattalionId bid) =>
                unitSelector.BattalionIsTargetable(bid);
        public override void AddTargetedBattalion(BattalionId bid) =>
                unitSelector.AddTargetedBattalion(bid);
        public override void RemoveTargetedBattalion(BattalionId bid) =>
                unitSelector.RemoveTargetedBattalion(bid);

        private DefendAvailableCommand DefendCommand => _availableCommand.DefendCommand;
        private ProvinceId ActingProvinceId => DefendCommand.ActingProvinceId;

        private ProvinceId? RallyPointProvinceId =>
                DefendCommand.AvailableFleeProvinceIds.Count >
                0? DefendCommand.AvailableFleeProvinceIds[RallyPointProvinceIndex] : null;

        private bool BadlyConstructedUnits =>
                unitSelector.HeroIdsLeadingInappropriateUnits.Count > 0;

        private bool NoBattalions =>
                unitSelector.BattalionCount == 0 && DefendCommand.AvailableBattalions.Count > 0;

        public override bool WarnOnCommitButton => BadlyConstructedUnits || NoBattalions;
        public override string CommitWarningText {
            get {
                if (!(WarnOnCommitButton)) return null;

                string txt = "";
                if (NoBattalions) { txt += "You have not selected any troops.\n\n"; }
                if (BadlyConstructedUnits) {
                    var count = unitSelector.HeroIdsLeadingInappropriateUnits.Count;
                    if (count == 1) {
                        txt += !string.IsNullOrEmpty(cachedWarningText)
                                       ? cachedWarningText
                                       : "A hero is leading a battalion type that would restrict their abilities.\n\n";
                    } else {
                        txt += "Some of your heroes are leading battalion types that would restrict their abilities.\n\n";
                    }
                }

                return txt;
            }
        }

        protected override ProvinceId? TargetedProvince {
            get => RallyPointProvinceId;

            set {
                if (value.HasValue) {
                    int index = DefendCommand.AvailableFleeProvinceIds.IndexOf(value.Value);
                    rallyPointDropdown.value = index != -1 ? index : 0;
                }
            }
        }

        public override bool EnableCommitButton => unitSelector.UnitsAreValid;
        public override string DisabledCommitButtonReason => unitSelector.InvalidUnitsReason;

        private void SetUpUnitSelector() {
            var dict = new Dictionary<
                    HeroId,
                    List<SuitableBattalions.Types.BattalionIdWithSuitability>>();
            foreach (var kv in DefendCommand.SuitableBattalionsForHeroes) {
                dict.Add(kv.Key, kv.Value.BattalionIdsWithSuitability.ToList());
            }

            List<CombatUnit> recommendedUnits = DefendCommand.RecommendedUnits == null
                                                        ? new List<CombatUnit>()
                                                        : DefendCommand.RecommendedUnits.ToList();

            var province = _model.Provinces[ActingProvinceId];
            var availableBattalionIds =
                    DefendCommand.AvailableBattalions.Select(b => b.BattalionId).ToList();
            var orderedBattalions = ProvinceUtils.OrderedBattalions(province)
                                            .Where(batt => availableBattalionIds.Contains(batt.Id))
                                            .ToList();

            unitSelector.SetUpUnits(
                    _model,
                    DefendCommand.AvailableHeroIds.ToList(),
                    orderedBattalions,
                    dict,
                    recommendedUnits);
        }

        protected override void SetUpUI() {
            if (DefendCommand.AvailableFleeProvinceIds.Count > 0) {
                var availableRallyPointProvinceNames =
                        DefendCommand.AvailableFleeProvinceIds
                                .Select(pid => _model.Provinces[pid].Name)
                                .ToList();

                rallyPointDropdown.ClearOptions();
                rallyPointDropdown.AddOptions(availableRallyPointProvinceNames);
                rallyPointDropdown.value = 0;
                rallyPointDropdown.gameObject.SetActive(true);
            } else {
                rallyPointDropdown.gameObject.SetActive(false);
            }

            var attackingFactionNames = DefendCommand.HostileFactionIds
                                                .Select(factionId => _model.FactionName(factionId))
                                                .ToList();
            marchText.text = $"Attack from {String.Join(", ", attackingFactionNames)}!";

            commanderCountText.text = $"{DefendCommand.HostileHeroCount}";
            troopCountText.text = $"{DefendCommand.HostileTroopCount}";

            SetUpUnitSelector();

            SetupWarningTextListener();
        }

        private void ClearWarningTextListener() {
            if (warningTextListener != null) {
                ClientTextProvider.Provider.RemoveListener(warningTextListener);
                warningTextListener = null;
            }
        }

        private void SetupWarningTextListener() {
            ClearWarningTextListener();

            // Only set up if there's exactly one hero with inappropriate units
            if (unitSelector.HeroIdsLeadingInappropriateUnits.Count == 1) {
                var heroId = unitSelector.HeroIdsLeadingInappropriateUnits[0];
                var hero = _model.Heroes[heroId];

                if (!string.IsNullOrEmpty(hero.NameTextId)) {
                    warningTextListener = new GeneratedTextListener(
                            hero.NameTextId,
                            "",
                            " is leading a battalion type that would restrict their abilities.\n\n",
                            0,
                            (index, text) => { cachedWarningText = text; });

                    ClientTextProvider.Provider.AddListener(warningTextListener);
                }
            }
        }

        public override SelectedCommand Command {
            get {
                var dc = new DefendSelectedCommand { FleeProvinceId = RallyPointProvinceId };
                dc.DefendingUnits.AddRange(SelectedActingUnits);

                var cmd = new SelectedCommand { DefendCommand = dc };

                return cmd;
            }
        }

        void OnDestroy() { ClearWarningTextListener(); }
    }
}
