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

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

    public class RansomCommandSelector : CommandSelector {
        private List<GeneratedTextListener> prisonerDropdownListeners = new();
        private List<GeneratedTextListener> tableListeners = new();
        private FactionId SelectedFactionId =>
                AvailableTargetFactionIds[targetFactionDropdown.value];
        private PrisonerToBeRansomed SelectedPrisonerToBeRansomed =>
                AvailablePrisonersToBeRansomed[prisonerToBeRansomedDropdown.value];

        private RansomOfferDetails RansomOffer =>
                DiplomacyCommand.Options
                        .Where(opt => opt.SealedValueCase ==
                                      DiplomacyOption.SealedValueOneofCase.RansomOfferOption)
                        .Select(opt => opt.RansomOfferOption)
                        .First(opt => opt.TargetFactionId == SelectedFactionId)
                        .RansomOffer;

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

        private DiplomacyAvailableCommand DiplomacyCommand => _availableCommand.DiplomacyCommand;
        private ProvinceId ActingProvinceId => DiplomacyCommand.ActingProvinceId;

        public TMP_Dropdown targetFactionDropdown;
        public TMP_Dropdown prisonerToBeRansomedDropdown;
        public Slider goldSlider;
        public TMP_Text goldAmount;

        public EventBasedTable hostagesTable;
        public EventBasedTable prisonersTable;

        public override AvailableCommand.SealedValueOneofCase CommandType =>
                AvailableCommand.SealedValueOneofCase.DiplomacyCommand;
        public override string HeaderString => "Offer Ransom";
        public override string CommitButtonString => "Commit Ransom Offer";

        private List<HeroId> HostageHeroIds { get; set; }
        private List<HeroId> PrisonerHeroIds { get; set; }

        public override List<HeroId> TargetedHeroIds {
            get {
                var hids = new List<HeroId>(HostageHeroIds);
                hids.AddRange(PrisonerHeroIds);
                return hids;
            }
        }
        public override bool HeroIsTargetable(HeroId heroId) =>
                AvailableHostages.Contains(heroId) || AvailablePrisoners.Contains(heroId);
        public override void AddTargetedHero(HeroId heroId) {
            if (AvailableHostages.Contains(heroId)) {
                if (!HostageHeroIds.Contains(heroId)) { HostageHeroIds.Add(heroId); }
            } else if (AvailablePrisoners.Contains(heroId)) {
                if (!PrisonerHeroIds.Contains(heroId)) { PrisonerHeroIds.Add(heroId); }
            }

            UpdateTables();
        }
        public override void RemoveTargetedHero(HeroId heroId) {
            HostageHeroIds.Remove(heroId);
            PrisonerHeroIds.Remove(heroId);
        }

        public override List<FactionId> TargetedFactionIds =>
                new List<FactionId> { 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 => PrisonerHeroIds.Exists(
                hid => _model.ActiveFactions[_model.PlayerId.Value].Leaders.Contains(hid));
        public override string CommitWarningText =>
                "You're offering up a faction leader. Are you sure?";

        public override bool EnableCommitButton => GoldOffered > 0 ||
                                                   PrisonerHeroIds.Any() || HostageHeroIds.Any();

        private List<FactionId> AvailableTargetFactionIds { get; set; }
        private List<PrisonerToBeRansomed> AvailablePrisonersToBeRansomed { get; set; }
        private List<HeroId> AvailableHostages { get; set; }
        private List<HeroId> AvailablePrisoners { get; set; }

        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();
                        }
                    }
                }
            }
        }

        private FactionId TargetFactionId(DiplomacyOption option) {
            return option.RansomOfferOption.TargetFactionId;
        }

        private int GoldOffered => (int)goldSlider.value;

        private RansomOfferOption SelectedOption {
            get {
                var opt = new RansomOfferOption {
                    TargetFactionId = SelectedFactionId,
                    RansomOffer =
                            new RansomOfferDetails {
                                PrisonerToBeRansomed = SelectedPrisonerToBeRansomed,
                                GoldOffered = GoldOffered
                            }
                };

                opt.RansomOffer.HostagesOffered.AddRange(RansomOffer.HostagesOffered.Where(
                        ho => HostageHeroIds.Contains(ho.HeroId)));
                opt.RansomOffer.PrisonersOffered.AddRange(RansomOffer.PrisonersOffered.Where(
                        ho => PrisonerHeroIds.Contains(ho.HeroId)));

                return opt;
            }
        }

        protected override void SetUpUI() {
            AvailableTargetFactionIds =
                    DiplomacyCommand.Options
                            .Where(opt => opt.SealedValueCase ==
                                          DiplomacyOption.SealedValueOneofCase.RansomOfferOption)
                            .Select(opt => TargetFactionId(opt))
                            .Distinct()
                            .ToList();

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

            goldSlider.minValue = 0;
            goldSlider.value = 0;

            goldAmount.text = "0";

            HostageHeroIds = new List<HeroId>();
            PrisonerHeroIds = new List<HeroId>();

            SetOptions();
        }

        public void ToggleChanged() {}

        public void SliderChanged() { goldAmount.text = ((int)goldSlider.value).ToString(); }

        public override SelectedCommand Command => new() {
            DiplomacyCommand =
                    new DiplomacySelectedCommand {
                        TargetFactionId = SelectedFactionId,
                        SelectedOption = new DiplomacyOption { RansomOfferOption = SelectedOption },
                        SentHeroId = DiplomacyCommand.AvailableHeroIds.First()
                    }
        };

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

        private void SetOptions() {
            var targetFid = SelectedFactionId;
            var options =
                    _availableCommand.DiplomacyCommand.Options.Select(opt => opt.RansomOfferOption)
                            .Where(opt => opt != null && opt.TargetFactionId == targetFid);
            goldSlider.maxValue = options.First().RansomOffer.GoldOffered;

            AvailablePrisonersToBeRansomed =
                    options.Select(opt => opt.RansomOffer.PrisonerToBeRansomed).ToList();

            prisonerToBeRansomedDropdown.ClearOptions();

            ClearPrisonerDropdownListeners();

            // Create empty options first, text will be populated by listeners
            var emptyOptions = AvailablePrisonersToBeRansomed
                                       .Select(
                                               _ => new TMP_Dropdown.OptionData { text = "" })
                                       .ToList();
            prisonerToBeRansomedDropdown.AddOptions(emptyOptions);

            // Set up listeners for each prisoner's name text
            for (int i = 0; i < AvailablePrisonersToBeRansomed.Count; i++) {
                var prisoner = AvailablePrisonersToBeRansomed[i];
                var hero = _model.Heroes[prisoner.PrisonerHeroId];

                if (!string.IsNullOrEmpty(hero.NameTextId)) {
                    var listener = new GeneratedTextListener(
                            hero.NameTextId,
                            "",
                            "",
                            i,
                            SetPrisonerDropdownText);

                    prisonerDropdownListeners.Add(listener);
                    ClientTextProvider.Provider.AddListener(listener);
                }
            }

            prisonerToBeRansomedDropdown.value = 0;

            AvailableHostages = RansomOffer.HostagesOffered.Select(p => p.HeroId).ToList();
            AvailablePrisoners = RansomOffer.PrisonersOffered.Select(p => p.HeroId).ToList();

            UpdateTables();
        }

        private void ClearPrisonerDropdownListeners() {
            foreach (var listener in prisonerDropdownListeners) {
                ClientTextProvider.Provider.RemoveListener(listener);
            }
            prisonerDropdownListeners.Clear();
        }

        private void ClearTableListeners() {
            foreach (var listener in tableListeners) {
                ClientTextProvider.Provider.RemoveListener(listener);
            }
            tableListeners.Clear();
        }

        private void SetPrisonerDropdownText(int index, string text) {
            if (index >= prisonerToBeRansomedDropdown.options.Count) return;
            prisonerToBeRansomedDropdown.options[index].text = text;
            prisonerToBeRansomedDropdown.RefreshShownValue();
        }

        private void UpdateTables() {
            ClearTableListeners();

            prisonersTable.RowCount = 0;
            int prisonerIndex = 0;
            foreach (HeroId prisonerHid in PrisonerHeroIds) {
                var row = prisonersTable.AddRowWithComponent<AvailableHeroTableRow>();
                var hero = _model.Heroes[prisonerHid];

                // Start with empty name, will be populated by listener
                row.HeroName = "";
                row.Selectable = true;
                row.Selected = false;

                // Set up listener for this table row
                if (!string.IsNullOrEmpty(hero.NameTextId)) {
                    var listener = new GeneratedTextListener(
                            hero.NameTextId,
                            "",
                            "",
                            prisonerIndex,
                            (index, text) => {
                                if (index < prisonersTable.RowCount) {
                                    var tableRow = prisonersTable.GetComponentInChildren<
                                            AvailableHeroTableRow>();
                                    if (tableRow != null) tableRow.HeroName = text;
                                }
                            });

                    tableListeners.Add(listener);
                    ClientTextProvider.Provider.AddListener(listener);
                }
                prisonerIndex++;
            }

            hostagesTable.RowCount = 0;
            int hostageIndex = 0;
            foreach (HeroId hostageHid in HostageHeroIds) {
                var row = hostagesTable.AddRowWithComponent<AvailableHeroTableRow>();
                var hero = _model.Heroes[hostageHid];

                // Start with empty name, will be populated by listener
                row.HeroName = "";
                row.Selectable = true;
                row.Selected = false;

                // Set up listener for this table row
                if (!string.IsNullOrEmpty(hero.NameTextId)) {
                    var listener = new GeneratedTextListener(
                            hero.NameTextId,
                            "",
                            "",
                            hostageIndex,
                            (index, text) => {
                                if (index < hostagesTable.RowCount) {
                                    var tableRow = hostagesTable.GetComponentInChildren<
                                            AvailableHeroTableRow>();
                                    if (tableRow != null) tableRow.HeroName = text;
                                }
                            });

                    tableListeners.Add(listener);
                    ClientTextProvider.Provider.AddListener(listener);
                }
                hostageIndex++;
            }
        }

        public void HostageTableRowClicked(int index) {
            HostageHeroIds.RemoveAt(index);
            UpdateTables();
        }

        public void PrisonerTableRowClicked(int index) {
            PrisonerHeroIds.RemoveAt(index);
            UpdateTables();
        }

        void OnDestroy() {
            ClearPrisonerDropdownListeners();
            ClearTableListeners();
        }
    }
}
