using System;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Views;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using static Net.Eagle0.Eagle.Common.QuestDetails;
using System.Linq;

namespace eagle {
    public class UnaffiliatedHeroRowController : TableRowController, System.IDisposable {
        public RawImage professionImage;
        public TMP_Text heroNameLabel;
        public TMP_Text questLabel;
        public TMP_Text strLabel;
        public TMP_Text agiLabel;
        public TMP_Text wisLabel;
        public TMP_Text chrLabel;
        public TMP_Text conLabel;

        public RawImage archeryImage;
        public RawImage fireImage;

        private DynamicHeroTextUpdater textUpdater = new DynamicHeroTextUpdater();
        private DynamicFactionTextUpdater factionTextUpdater = new DynamicFactionTextUpdater();
        private GeneratedTextListener questTextListener;

        public void SetUnaffiliatedHero(UnaffiliatedHeroBasics uh, IGameModel model) {
            ClearQuestTextListener();
            factionTextUpdater.Dispose();

            professionImage.texture = EagleCommonTextures.Instance.Profession(uh.Profession);
            if (professionImage.texture == null) {
                professionImage.color = Color.clear;
            } else {
                professionImage.color = Color.white;
            }

            // Use the NameTextId from UnaffiliatedHeroBasics
            textUpdater.SetHeroText(heroNameLabel, uh.NameTextId, "Hero");

            switch (uh.RecruitmentInfo.Status) {
                case RecruitmentStatus.HasQuest:
                    SetQuestText(uh.RecruitmentInfo.Quest, model);
                    break;
                case RecruitmentStatus.Sworn: SetSwornRecruitmentStatusText(uh, model); break;
                default:
                    questLabel.text = DisplayNames.ShortRecruitmentStatusString(uh.RecruitmentInfo);
                    break;
            }

            if (model.Heroes.ContainsKey(uh.HeroId)) {
                var hero = model.Heroes[uh.HeroId];

                if (hero.Strength > 0) {
                    strLabel.text = hero.Strength.ToString();
                    agiLabel.text = hero.Agility.ToString();
                    wisLabel.text = hero.Wisdom.ToString();
                    chrLabel.text = hero.Charisma.ToString();
                    conLabel.text = hero.Constitution.ToString();

                    archeryImage.gameObject.SetActive(hero.ArcheryCapable);
                    fireImage.gameObject.SetActive(hero.StartFireCapable);
                } else {
                    strLabel.text = "";
                    agiLabel.text = "";
                    wisLabel.text = "";
                    chrLabel.text = "";
                    conLabel.text = "";
                }
            }
        }

        public void Dispose() {
            textUpdater?.Dispose();
            factionTextUpdater?.Dispose();
            ClearQuestTextListener();
        }

        private void OnDestroy() { Dispose(); }

        private void ClearQuestTextListener() {
            if (questTextListener == null) { return; }

            ClientTextProvider.Provider.RemoveListener(questTextListener);
            questTextListener = null;
        }

        private void SetSwornRecruitmentStatusText(UnaffiliatedHeroBasics uh, IGameModel model) {
            if (!model.Heroes.TryGetValue(uh.HeroId, out var hero) ||
                !(hero.FactionId is int factionId)) {
                questLabel.text = DisplayNames.ShortRecruitmentStatusString(uh.RecruitmentInfo);
                return;
            }

            var factionNamePlaceholder = DynamicTextTemplate.Placeholder(
                    DisplayNames.FactionTextPlaceholders.FactionName);
            factionTextUpdater.SetFactionTextWithCustomFormat(
                    questLabel,
                    model.MaybeDestroyedFaction(factionId),
                    model,
                    $"Dedicated to {factionNamePlaceholder}",
                    DisplayNames.ShortRecruitmentStatusString(uh.RecruitmentInfo));
        }

        private void SetQuestText(Quest quest, IGameModel model) {
            if (quest == null) {
                questLabel.text = "There is nothing you can do";
                return;
            }

            switch (quest.Details.SealedValueCase) {
                case SealedValueOneofCase.DismissSpecificVassalQuest:
                    var targetHeroId = quest.Details.DismissSpecificVassalQuest.TargetHeroId;
                    SetDynamicHeroQuestText(
                            targetHeroId,
                            (heroName) => $"Dismiss {heroName}",
                            "Dismiss vassal",
                            model);
                    break;

                case SealedValueOneofCase.ExecutePrisonerQuest:
                    var executeDetails = quest.Details.ExecutePrisonerQuest;
                    var executeProvinceName = model.Provinces[executeDetails.ProvinceId].Name;
                    SetDynamicHeroQuestText(
                            executeDetails.PrisonerHeroId,
                            (heroName) => $"Execute {heroName} in {executeProvinceName}",
                            $"Execute prisoner in {executeProvinceName}",
                            model);
                    break;

                case SealedValueOneofCase.ExilePrisonerQuest:
                    var exileDetails = quest.Details.ExilePrisonerQuest;
                    var exileProvinceName = model.Provinces[exileDetails.ProvinceId].Name;
                    SetDynamicHeroQuestText(
                            exileDetails.PrisonerHeroId,
                            (heroName) => $"Exile {heroName} from {exileProvinceName}",
                            $"Exile prisoner from {exileProvinceName}",
                            model);
                    break;

                case SealedValueOneofCase.FreePrisonerQuest:
                    var freeDetails = quest.Details.FreePrisonerQuest;
                    var freeProvinceName = model.Provinces[freeDetails.ProvinceId].Name;
                    SetDynamicHeroQuestText(
                            freeDetails.PrisonerHeroId,
                            (heroName) => $"Release {heroName} from prison in {freeProvinceName}",
                            $"Release prisoner from prison in {freeProvinceName}",
                            model);
                    break;

                case SealedValueOneofCase.ReturnPrisonerQuest:
                    var returnDetails = quest.Details.ReturnPrisonerQuest;
                    var returnProvinceName = model.Provinces[returnDetails.ProvinceId].Name;
                    var returnFactionName = model.FactionName(returnDetails.ToFactionId);
                    SetDynamicHeroQuestText(
                            returnDetails.PrisonerHeroId,
                            (heroName) =>
                                    $"Return {heroName} from {returnProvinceName} to {returnFactionName}",
                            $"Return prisoner from {returnProvinceName} to {returnFactionName}",
                            model);
                    break;

                case SealedValueOneofCase.RescueImprisonedLeaderQuest:
                    var rescueDetails = quest.Details.RescueImprisonedLeaderQuest;
                    var imprisoningFactionName =
                            model.FactionName(rescueDetails.ImprisoningFactionId);
                    SetDynamicHeroQuestText(
                            rescueDetails.ImprisonedHeroId,
                            (heroName) => $"Rescue {heroName} from {imprisoningFactionName}",
                            $"Rescue a hero from {imprisoningFactionName}",
                            model);
                    break;

                case SealedValueOneofCase.SwearBrotherhoodWithHeroQuest:
                    var brotherhoodDetails = quest.Details.SwearBrotherhoodWithHeroQuest;
                    SetDynamicHeroQuestText(
                            brotherhoodDetails.TargetHeroId,
                            (heroName) => $"Swear kinship with {heroName}",
                            "Swear kinship with a hero",
                            model);
                    break;

                case SealedValueOneofCase.ApprehendOutlawQuest:
                    var apprehendDetails = quest.Details.ApprehendOutlawQuest;
                    SetDynamicHeroQuestText(
                            apprehendDetails.OutlawHeroId,
                            (heroName) => $"Apprehend the outlaw {heroName}",
                            "Apprehend an outlaw",
                            model);
                    break;

                default:
                    // For all other quests, use the static method
                    questLabel.text = ShortQuestString(quest, model);
                    break;
            }
        }

        private void SetDynamicHeroQuestText(
                int heroId,
                System.Func<string, string> dynamicTextFunc,
                string fallbackText,
                IGameModel model) {
            if (!model.Heroes.ContainsKey(heroId)) {
                questLabel.text = fallbackText;
                return;
            }

            var hero = model.Heroes[heroId];

            // Check if the name is available immediately
            var textEntry = ClientTextProvider.Provider.GetTextEntry(hero.NameTextId);
            if (textEntry != null && !string.IsNullOrEmpty(textEntry.Text)) {
                questLabel.text = dynamicTextFunc(textEntry.Text);
            } else {
                // Set fallback text and create listener for dynamic updates
                questLabel.text = fallbackText;

                ClearQuestTextListener();

                // Create new listener for dynamic updates
                questTextListener =
                        new GeneratedTextListener(hero.NameTextId, "", "", 0, (index, heroName) => {
                            if (questLabel != null) { questLabel.text = dynamicTextFunc(heroName); }
                        });
                ClientTextProvider.Provider.AddListener(questTextListener);
            }
        }

        static string GetHeroName(IGameModel model, int heroId) {
            if (!model.Heroes.TryGetValue(heroId, out var hero)) { return "a hero"; }
            var textEntry = ClientTextProvider.Provider.GetTextEntry(hero.NameTextId);
            return textEntry != null && !string.IsNullOrEmpty(textEntry.Text) ? textEntry.Text
                                                                              : "a hero";
        }

        static string ShortQuestString(Quest quest, IGameModel model) {
            if (quest == null) { return $"There is nothing you can do"; }

            switch (quest.Details.SealedValueCase) {
                case SealedValueOneofCase.AllianceQuest:
                    return $"Secure an alliance with another faction";

                case SealedValueOneofCase.DefeatFactionQuest:
                    return $"Defeat {model.FactionName(quest.Details.DefeatFactionQuest.TargetFactionId)}";

                case SealedValueOneofCase.AlmsToProvinceQuest: {
                    var province = model.Provinces[quest.Details.AlmsToProvinceQuest.ProvinceId];
                    var desiredFood = quest.Details.AlmsToProvinceQuest.TotalFood;
                    var remainingFood = Math.Max(0, desiredFood - quest.ComponentsFulfilled);
                    var remainingText =
                            quest.ComponentsFulfilled > 0 ? $" ({remainingFood} remaining)" : "";
                    return $"Give {desiredFood} food in alms in {province.Name}{remainingText}";
                }

                case SealedValueOneofCase.AlmsAcrossRealmQuest: {
                    var desiredFood = quest.Details.AlmsAcrossRealmQuest.TotalFood;
                    var remainingFood = Math.Max(0, desiredFood - quest.ComponentsFulfilled);
                    var remainingText =
                            quest.ComponentsFulfilled > 0 ? $" ({remainingFood} remaining)" : "";
                    return $"Give {desiredFood} food in alms across the realm{remainingText}";
                }

                case SealedValueOneofCase.GiveToHeroesInProvinceQuest: {
                    var province =
                            model.Provinces[quest.Details.GiveToHeroesInProvinceQuest.ProvinceId];
                    var desiredGold = quest.Details.GiveToHeroesInProvinceQuest.TotalGold;
                    var remainingGold = Math.Max(0, desiredGold - quest.ComponentsFulfilled);
                    var remainingText =
                            quest.ComponentsFulfilled > 0 ? $" ({remainingGold} remaining)" : "";
                    return $"Give {desiredGold} gold to heroes in {province.Name}{remainingText}";
                }

                case SealedValueOneofCase.GiveToHeroesAcrossRealmQuest: {
                    var desiredGold = quest.Details.GiveToHeroesAcrossRealmQuest.TotalGold;
                    var remainingGold = Math.Max(0, desiredGold - quest.ComponentsFulfilled);
                    var remainingText =
                            quest.ComponentsFulfilled > 0 ? $" ({remainingGold} remaining)" : "";
                    return $"Give {desiredGold} gold to heroes across the realm{remainingText}";
                }

                case SealedValueOneofCase.ImproveAgricultureQuest:
                    return $"Raise Agriculture to {(int)Math.Ceiling(quest.Details.ImproveAgricultureQuest.DesiredValue)}";

                case SealedValueOneofCase.ImproveEconomyQuest:
                    return $"Raise Economy to {(int)Math.Ceiling(quest.Details.ImproveEconomyQuest.DesiredValue)}";

                case SealedValueOneofCase.ImproveInfrastructureQuest:
                    return $"Raise Infrastructure to {(int)Math.Ceiling(quest.Details.ImproveInfrastructureQuest.DesiredValue)}";

                case SealedValueOneofCase.SpecificExpansionQuest:
                    return $"Expand to {model.Provinces[quest.Details.SpecificExpansionQuest.ProvinceId].Name}";

                case SealedValueOneofCase.DismissSpecificVassalQuest:
                    // This case is handled by SetQuestText with dynamic updates, this is fallback
                    // only
                    return "Dismiss vassal";

                case SealedValueOneofCase.WealthQuest:
                    return $"Acquire {quest.Details.WealthQuest.Gold} gold and {quest.Details.WealthQuest.Food} food";

                case SealedValueOneofCase.UpgradeBattalionQuest:
                    return $"Raise a {DisplayNames.LongBattalionTypeNames[quest.Details.UpgradeBattalionQuest.BattalionTypeId]} to {(int)Math.Ceiling(quest.Details.UpgradeBattalionQuest.MinimumTraining)} training and {(int)Math.Ceiling(quest.Details.UpgradeBattalionQuest.MinimumArmament)} armament";

                case SealedValueOneofCase.GrandArmyQuest:
                    return $"Have {quest.Details.GrandArmyQuest.TotalTroopCount} troops";

                case SealedValueOneofCase.TruceWithFactionQuest:
                    return $"Sign a truce with {model.FactionName(quest.Details.TruceWithFactionQuest.TargetFactionId)}";

                case SealedValueOneofCase.TruceCountQuest: {
                    var currentTruceString = "";
                    if (model.PlayerId is int fid) {
                        var currentTruces = model.ActiveFactions[fid].FactionRelationships.Count(
                                fr => fr.RelationshipLevel !=
                                      FactionRelationshipView.Types.RelationshipLevel.Hostile);
                        if (currentTruces == 0) {
                            currentTruceString = " (currently have none)";
                        } else {
                            currentTruceString = " (currently have " + currentTruces + ")";
                        }
                    }
                    return $"Have truces with {quest.Details.TruceCountQuest.TruceCount} factions" +
                           currentTruceString;
                }

                case SealedValueOneofCase.RescueImprisonedLeaderQuest: {
                    // This case is handled by SetQuestText with dynamic updates, this is fallback
                    // only
                    var details = quest.Details.RescueImprisonedLeaderQuest;
                    var heroName = GetHeroName(model, details.ImprisonedHeroId);
                    return $"Rescue {heroName} from {model.FactionName(details.ImprisoningFactionId)}";
                }

                case SealedValueOneofCase.ExpandToProvincesQuest: {
                    var targetCount = quest.Details.ExpandToProvincesQuest.TargetProvinceCount;
                    // A province is "developed" when support >= 40 (MinSupportForTaxes)
                    var currentCount =
                            model.PlayerId.HasValue
                                    ? model.ProvincesForFaction(model.PlayerId.Value)
                                              .Count(pid => model.Provinces[pid]
                                                                    .FullInfo?.Support?.Stat >= 40)
                                    : 0;
                    return $"Have {targetCount} developed provinces (currently {currentCount})";
                }

                case SealedValueOneofCase.TotalDevelopmentQuest: {
                    var details = quest.Details.TotalDevelopmentQuest;
                    var province = model.Provinces[details.ProvinceId];
                    var target = (int)Math.Ceiling(details.DesiredTotalDevelopment);
                    var info = province.FullInfo;
                    var current =
                            (int)((info.Economy - info.EconomyDevastation) +
                                  (info.Agriculture - info.AgricultureDevastation) +
                                  (info.Infrastructure - info.InfrastructureDevastation));
                    return $"Raise total development to {target} in {province.Name} (currently {current})";
                }

                case SealedValueOneofCase.SuppressRiotByForceQuest:
                    return "Suppress a riot by force";

                case SealedValueOneofCase.FightBeastsAloneQuest:
                    return "Have a hero fight beasts alone";

                case SealedValueOneofCase.DevelopProvincesQuest: {
                    var details = quest.Details.DevelopProvincesQuest;
                    var targetMonths = quest.ComponentCount;
                    var currentMonths = quest.ComponentsFulfilled;
                    var currentProvinces =
                            model.PlayerId.HasValue
                                    ? model.ProvincesForFaction(model.PlayerId.Value)
                                              .Count(pid => model.Provinces[pid]
                                                                    .FullInfo?.ProvinceOrders ==
                                                            ProvinceOrderType.Develop)
                                    : 0;
                    var statusText = currentProvinces >= details.TargetProvinceCount
                                             ? "currently on target"
                                             : "not currently meeting";
                    return $"Maintain {details.TargetProvinceCount} provinces with Develop orders for {targetMonths} months ({currentMonths}/{targetMonths} months, {statusText})";
                }

                case SealedValueOneofCase.MobilizeProvincesQuest: {
                    var details = quest.Details.MobilizeProvincesQuest;
                    var targetMonths = quest.ComponentCount;
                    var currentMonths = quest.ComponentsFulfilled;
                    var currentProvinces =
                            model.PlayerId.HasValue
                                    ? model.ProvincesForFaction(model.PlayerId.Value)
                                              .Count(pid => model.Provinces[pid]
                                                                    .FullInfo?.ProvinceOrders ==
                                                            ProvinceOrderType.Mobilize)
                                    : 0;
                    var statusText = currentProvinces >= details.TargetProvinceCount
                                             ? "currently on target"
                                             : "not currently meeting";
                    return $"Maintain {details.TargetProvinceCount} provinces with Mobilize orders for {targetMonths} months ({currentMonths}/{targetMonths} months, {statusText})";
                }

                case SealedValueOneofCase.BattalionDiversityQuest:
                    return "Have 3 different battalion types, each near full strength";

                case SealedValueOneofCase.ReleaseAllPrisonersQuest:
                    return "Release all prisoners (don't execute, move, or trade them)";

                case SealedValueOneofCase.SwearBrotherhoodWithHeroQuest:
                    return "Swear kinship with a hero";

                case SealedValueOneofCase.BetrayAllyQuest: {
                    var details = quest.Details.BetrayAllyQuest;
                    return $"Break alliance with {model.FactionName(details.TargetFactionId)}";
                }

                case SealedValueOneofCase.BorderSecurityQuest: {
                    var details = quest.Details.BorderSecurityQuest;
                    var provinceName = model.Provinces[details.TargetProvinceId].Name;
                    return $"Occupy all provinces bordering {provinceName}";
                }

                case SealedValueOneofCase.FocusProvinceQuest: {
                    var details = quest.Details.FocusProvinceQuest;
                    var provinceName = model.Provinces[details.TargetProvinceId].Name;
                    var isCurrentFocus =
                            model.PlayerId.HasValue &&
                            model.ActiveFactions[model.PlayerId.Value].FocusProvinceId ==
                                    details.TargetProvinceId;
                    var statusText = isCurrentFocus ? "currently focused" : "not currently focused";
                    return $"Make {provinceName} the focus province for {quest.ComponentCount} months ({quest.ComponentsFulfilled}/{quest.ComponentCount} months, {statusText})";
                }

                case SealedValueOneofCase.WinBattleOutnumberedQuest:
                    return "Win a battle while outnumbered";

                case SealedValueOneofCase.WinBattlesQuest: {
                    var targetBattles = quest.ComponentCount;
                    var wonBattles = quest.ComponentsFulfilled;
                    return $"Win {targetBattles} battles ({wonBattles}/{targetBattles})";
                }

                case SealedValueOneofCase.ApprehendOutlawQuest: {
                    // This case is handled by SetQuestText with dynamic updates, this is fallback
                    // only
                    var details = quest.Details.ApprehendOutlawQuest;
                    var heroName = GetHeroName(model, details.OutlawHeroId);
                    return $"Apprehend the outlaw {heroName}";
                }

                case SealedValueOneofCase.StartBlizzardQuest: {
                    var details = quest.Details.StartBlizzardQuest;
                    var provinceName = model.Provinces[details.ProvinceId].Name;
                    return $"Start a blizzard in {provinceName}";
                }

                case SealedValueOneofCase.StartDroughtQuest: {
                    var details = quest.Details.StartDroughtQuest;
                    var provinceName = model.Provinces[details.ProvinceId].Name;
                    return $"Start a drought in {provinceName}";
                }

                case SealedValueOneofCase.StartEpidemicQuest: {
                    var details = quest.Details.StartEpidemicQuest;
                    var provinceName = model.Provinces[details.ProvinceId].Name;
                    return $"Start an epidemic in {provinceName}";
                }

                case SealedValueOneofCase.SpendOnFeastsQuest: {
                    var details = quest.Details.SpendOnFeastsQuest;
                    return $"Host feasts costing {details.TotalGold} gold ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.SpendOnFeastsInProvinceQuest: {
                    var details = quest.Details.SpendOnFeastsInProvinceQuest;
                    return $"Host feasts here costing {details.TotalGold} gold ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.SpendOnFeastsAcrossRealmQuest: {
                    var details = quest.Details.SpendOnFeastsAcrossRealmQuest;
                    return $"Host feasts across the realm costing {details.TotalGold} gold ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.SendSuppliesQuest: {
                    var details = quest.Details.SendSuppliesQuest;
                    var provinceName = model.Provinces[details.TargetProvinceId].Name;
                    return $"Send {details.TotalFood} food to {provinceName} ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.RestProvinceQuest: {
                    var details = quest.Details.RestProvinceQuest;
                    var provinceName = model.Provinces[details.TargetProvinceId].Name;
                    return $"Keep {provinceName} at rest for {quest.ComponentCount} months ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.ReconProvincesQuest: {
                    var details = quest.Details.ReconProvincesQuest;
                    return $"Recon {details.TargetProvinceCount} provinces ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.ReconSpecificProvincesQuest: {
                    var details = quest.Details.ReconSpecificProvincesQuest;
                    // Show completed provinces at end with strikethrough
                    var provinces =
                            details.TargetProvinceIds
                                    .Select(pid => (province: model.Provinces[pid],
                                                    isKnown: model.Provinces[pid].FullInfo != null))
                                    .OrderBy(p => p.isKnown)  // Unknown first, known last
                                    .Select(p => p.isKnown
                                                         ? $"<color=#888888><s>{p.province.Name}</s></color>"
                                                         : p.province.Name);
                    var knownCount = details.TargetProvinceIds.Count(
                            pid => model.Provinces[pid].FullInfo != null);
                    return $"Recon {string.Join(", ", provinces)} ({knownCount}/{quest.ComponentCount})";
                }

                case SealedValueOneofCase.RepairDevastationQuest: {
                    var details = quest.Details.RepairDevastationQuest;
                    return $"Repair {details.TargetDevastationRepaired} devastation ({quest.ComponentsFulfilled}/{quest.ComponentCount})";
                }

                default:
                    return $"Crosby hasn't gotten to quest of type {quest.Details.SealedValueCase} yet";
            }
        }
    }
}
