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

namespace eagle {
    using ProvinceId = Int32;

    public class IssueOrdersCommandSelector : CommandSelector {
        private List<GeneratedTextListener> rulingHeroListeners = new();
        IList<ProvinceOrderType> availableOrderTypes;
        Dictionary<ProvinceId, ProvinceOrderType> currentOrders;
        List<ProvinceOrders> newOrders;
        List<ProvinceId> provinceIds;
        List<ProvinceId?> availableFocusProvinces;
        List<String> availableFocusProvinceNames;
        ProvinceId? SelectedFocusProvince => availableFocusProvinces[focusDropdown.value];
        ProvinceId? startingFocusProvince;

        public TMP_Dropdown focusDropdown;
        public GameObject scrollContent;
        public GameObject provinceOrdersRowPrefab;

        private IssueOrdersAvailableCommand IssueOrdersAvailableCommand =>
                _availableCommand.IssueOrdersCommand;
        private ProvinceId ActingProvinceId => IssueOrdersAvailableCommand.ActingProvinceId;

        protected override ProvinceId? TargetedProvince {
            get => SelectedFocusProvince;
            set {
                var index = availableFocusProvinces.IndexOf(value);
                if (index == -1) return;

                focusDropdown.value = index;
            }
        }

        private List<ProvinceId?> GetAvailableFocusProvinces() {
            var pids = _model.ProvincesForFaction(_model.PlayerId.Value);
            var optionals = new List<ProvinceId?> { null };
            optionals.AddRange(pids.Select(x => (ProvinceId?)x));
            return optionals;
        }

        private string OptionalProvinceName(ProvinceId? pid) {
            if (pid.HasValue) return _model.Provinces[pid.Value].Name;
            else
                return "None";
        }

        public override AvailableCommand.SealedValueOneofCase CommandType =>
                AvailableCommand.SealedValueOneofCase.IssueOrdersCommand;
        override public string HeaderString => "Issue Orders";
        override public string CommitButtonString => "Commit Orders";

        override public bool EnableCommitButton =>
                (newOrders.Any(x => x.Orders != currentOrders[x.ProvinceId])) ||
                (SelectedFocusProvince != startingFocusProvince);
        public override string DisabledCommitButtonReason => "No province orders have changed";

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

        override protected void SetUpUI() {
            ClearRulingHeroListeners();
            availableOrderTypes = IssueOrdersAvailableCommand.AvailableOrders;
            currentOrders = IssueOrdersAvailableCommand.CurrentOrders.ToDictionary(
                    x => x.ProvinceId,
                    x => x.Orders);

            newOrders = IssueOrdersAvailableCommand.CurrentOrders.ToList();
            newOrders.Sort(
                    (x, y) => _model.Provinces[x.ProvinceId].Name.CompareTo(
                            _model.Provinces[y.ProvinceId].Name));

            provinceIds = newOrders.Select(x => x.ProvinceId).ToList();

            foreach (Transform row in scrollContent.transform) { Destroy(row.gameObject); }

            var parentTransform = scrollContent.transform;

            var oddColor = provinceOrdersRowPrefab.GetComponent<Image>().color;
            oddColor.a = 0;

            for (int i = 0; i < provinceIds.Count; i++) {
                var pid = provinceIds[i];
                var province = _model.Provinces[pid];

                var newRow = Instantiate(
                        provinceOrdersRowPrefab,
                        new Vector3(0, 0, 0),
                        new Quaternion(0, 0, 0, 0),
                        parentTransform);

                // FIXME: I don't know why this reset of the z-position is necessary
                var position = newRow.GetComponent<RectTransform>().anchoredPosition3D;
                position.z = 0;
                newRow.GetComponent<RectTransform>().anchoredPosition3D = position;

                var textComponents = newRow.GetComponentsInChildren<TextMeshProUGUI>();
                textComponents[0].text = province.Name;
                textComponents[1].text = GUIUtils.ConditionString(province.FullInfo.Support);

                // Set up ruling hero name with listener
                var rulingHeroTextComponent = textComponents[2];
                rulingHeroTextComponent.text = "";  // Start empty, will be populated by listener

                if (province.FullInfo.RulingHeroId.HasValue) {
                    var rulingHero = _model.Heroes[province.FullInfo.RulingHeroId.Value];
                    if (!string.IsNullOrEmpty(rulingHero.NameTextId)) {
                        var listener = new GeneratedTextListener(
                                rulingHero.NameTextId,
                                "",
                                "",
                                i,
                                (index, text) => {
                                    if (index < scrollContent.transform.childCount) {
                                        var rowTransform = scrollContent.transform.GetChild(index);
                                        var rowTextComponents =
                                                rowTransform
                                                        .GetComponentsInChildren<TextMeshProUGUI>();
                                        if (rowTextComponents.Length > 2) {
                                            rowTextComponents[2].text = text;
                                        }
                                    }
                                });

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

                textComponents[3].text = province.FullInfo.RulingFactionHeroIds.Count.ToString();

                var ordersDropdown = newRow.GetComponentInChildren<TMP_Dropdown>();
                ordersDropdown.ClearOptions();
                ordersDropdown.AddOptions(
                        availableOrderTypes.Select(tp => DisplayNames.ProvinceOrderTypeNames[tp])
                                .ToList());
                ordersDropdown.value = availableOrderTypes.IndexOf(currentOrders[pid]);
                int index = i;
                ordersDropdown.onValueChanged.AddListener(
                        newValue => ProvinceOrdersValueChanged(index, newValue));

                if (i % 2 == 1) { newRow.GetComponent<Image>().color = oddColor; }
            }

            startingFocusProvince =
                    _model.MaybeDestroyedFaction(_model.PlayerId.Value).FocusProvinceId;
            availableFocusProvinces = GetAvailableFocusProvinces();
            availableFocusProvinceNames =
                    availableFocusProvinces.Select(OptionalProvinceName).ToList();

            focusDropdown.ClearOptions();
            focusDropdown.AddOptions(availableFocusProvinceNames);
            focusDropdown.value = availableFocusProvinces.IndexOf(startingFocusProvince);
        }

        override public SelectedCommand Command {
            get {
                var sentOrders = newOrders.Where(x => x.Orders != currentOrders[x.ProvinceId]);
                var ordersCommand = new IssueOrdersSelectedCommand();
                ordersCommand.NewOrders.Add(sentOrders);
                ordersCommand.NewFocusProvince = SelectedFocusProvince;
                return new SelectedCommand { IssueOrdersCommand = ordersCommand };
            }
        }

        public void ProvinceOrdersValueChanged(int index, Int32 newValue) {
            newOrders[index].Orders = availableOrderTypes[newValue];
        }

        void OnDestroy() { ClearRulingHeroListeners(); }
    }
}
