// Copyright (c) Strange Loop Games. All rights reserved.
// See LICENSE file in the project root for full license information.


namespace Eco.Mods.TechTree
{
    using Eco.Core.Plugins.Interfaces;
    using Eco.Core.Utils;
    using Eco.Gameplay.Culture;
    using Eco.Gameplay.Culture.CultureItems;
    using Eco.Gameplay.Housing;
    using Eco.Gameplay.Players;
    using Eco.Gameplay.Settlements.Culture;
    using Eco.Shared.Localization;
    using Eco.Shared.Localization.ConstLocs;
    using Eco.Shared.Utils;
    using System;
    using System.Collections.Generic;
    using Eco.Gameplay.Settlements;
    using Eco.Gameplay.Economy.Reputation;
	using Eco.Gameplay.Systems;
	using System.Security.Cryptography;
	using Eco.Shared;

	public class CulturalValues : IModInit
    {
        //Determines how the reputation of an artist maps to the cultural value of art they make.
        static LimitMapper.Config playerReputationToCultureConversion  = new() { InputMin = 0, InputHalflife = 50, OutputAtMin = 0, OutputLimit = 25 };
        static LimitMapper.Config pictureReputationToCultureConversion = new() { InputMin = 0, InputHalflife = 100, OutputAtMin = 0, OutputLimit = 50 };

        public static void Initialize()
        {
            //Big penalty for same settlement reptuatiopn.
            CultureConfig.SameSettlementReputationMutliplier = .1f; 

            //Calculate the cultural value of a given user picture.
            CultureConfig.ArtworkValueCalculation = (worldobj, settlement, picture) =>
            {
                //We look at both the user's reputation and the picture's reputation to determine its value. Get the rep of each
                //and convert it to culture using the limit-conversion defined above.
                //Reputation from citizens with the same settlement counts at a reduction
                var userRep         = ReputationManager.Obj.CalculateWeightedPositiveRep(picture.Creator, RepContributionTowardsCultureValue);
                var picRep          = ReputationManager.Obj.CalculateWeightedPositiveRep(picture, RepContributionTowardsCultureValue);

                var repheaderUser = Header(Localizer.DoStr("artist's"), picture.Creator, userRep);
                var repheaderPic  = Header(Localizer.DoStr("picture's"), picture, picRep);

                LocString Header(LocString source, IHasReputation target, float rep) 
                {
                    var sb = new LocStringBuilder();
                    if (FeatureConfig.Obj.UseSettlementSystem)
                    { 
                        sb.AppendLineLoc($"The {source} {ReputationManager.Obj.GetRepMarkedupName(target, RepTitle)} determines the cultural value of the artwork. Only {TextLoc.PositiveLocStr("Positive")} reputation counts, {TextLoc.NegativeLocStr("negative")} reputation is ignored.");
                        if (settlement != null)
                            sb.AppendLineLoc($"Positive reputation from Citizens of foreign settlements counts more than from Citizens of {settlement.MarkedUpName}");
                        sb.AppendLine();
                    }

                    if (FeatureConfig.Obj.UseSettlementSystem)
                    {
                        sb.AppendLine(Localizer.Do($"Base Positive {ReputationManager.Obj.GetReputationWordWithMarkedupName(target)}: {Text.StyledNum(ReputationManager.Obj.GetPositiveReputation(target))}").Dash());
                        var percent = Text.StyledPercent(1 - CultureConfig.SameSettlementReputationMutliplier);
                        if (settlement != null) sb.AppendLine(Localizer.Do($"Reputation after reducing {settlement.MarkedUpName} Citizen contributions by {percent}: {Text.StyledNum(rep)}").Dash());
                        else                    sb.AppendLine(TextLoc.SubtextLoc($"Note: No settlement applied at the moment, keep in mind that this culture will be reduced when placed in a settlement, with reputation from Citizens of that settlement counting {percent} less. Until then, the cultural value is {Text.StyledInt(0)}"));
                    }
                    else
                        sb.AppendLine(Localizer.NL($"{ReputationManager.Obj.GetRepMarkedupName(target, RepTitle)}: {Text.StyledNum(ReputationManager.Obj.GetPositiveReputation(target))}"));
                    return sb.ToLocString();
                }
                
                //Calculate how reputation value transfers to culture
                var userComponent   = LimitMapper.MapAndDescribe(userRep, playerReputationToCultureConversion, Localizer.DoStr("culture value"), CultureUtils.AsAdjustedReputation, CultureUtils.AsCulture, repheaderUser);
                var picComponent    = LimitMapper.MapAndDescribe(picRep, pictureReputationToCultureConversion, Localizer.DoStr("culture value"), CultureUtils.AsAdjustedReputation, CultureUtils.AsCulture, repheaderPic);
                var finalVal        = Mathf.Min(userComponent.Output, picComponent.Output);
                if (FeatureConfig.Obj.UseSettlementSystem && settlement == null)
                    finalVal = 0;

                //Reenable when fixed: https://app.clickup.com/t/862j4wfmq
                //var table = TableExtensions.MakeTableLoc(Table(picture, userRepCulture), $"Artwork Value");

                var sb = new LocStringBuilder();
                sb.AppendLineLoc($"An artwork's {CultureUtils.Culture} value is determined by the positive reputation of both the work and the artist, whichever is smaller. Negative reputation is ignored.");
                if (CultureConfig.SameSettlementReputationMutliplier != 1f)
                { 
                    sb.AppendLine();
                    sb.AppendLineLoc($"Reputation from the settlement that the artwork is currently placed in is reduced by {Text.StyledPercent(1f - CultureConfig.SameSettlementReputationMutliplier)}.  Thus, foriegn opinion matters more for culture value, and trading artwork can yield net positive culture to two settlements.");
                }

                sb.Append(Text.Table(Table()));
                IEnumerable<(LocString, LocString)> Table()
                { 
                    yield return (Localizer.Do($"Artist ({picture.Creator?.MarkedUpName}):"), userComponent.Description);
                    yield return (Localizer.Do($"Picture:"), picComponent.Description);
                    yield return (TextLoc.BoldLoc($"Final value (lower of two):"), finalVal.AsCulture());
                }
                if (FeatureConfig.Obj.UseSettlementSystem && settlement == null)
                    sb.Append(TextLoc.SubtextLocStr($"Culture reduced to zero because not placed in a Settlement yet."));

                //Now wrap it all in another foldout.
                var header = settlement != null ? Localizer.Do($"Artwork Value in {settlement.MarkedUpName}") : Localizer.Do($"Artwork Value Before Settlement Placement");
                var valFoldout = TextLoc.Foldout(finalVal.AsCulture(), header, sb.ToLocString());

                return ValResult.Make(finalVal, valFoldout);

                //We discount reputation that is given from a citizen of the same settlement this world object is on.
                float RepContributionTowardsCultureValue(IGivesReputation repGiver, float repGiven)
                {
                    if (repGiver is User u && (settlement?.Citizenship.HasCitizen(u) ?? false)) return repGiven * CultureConfig.SameSettlementReputationMutliplier;
                    else                                                                        return repGiven;
                }

            };

            /*IEnumerable<(LocString, LocString)> Table(UserTexture picture, float userRepCulture)
            {
                yield return (Localizer.Do($"Artist's reputation:"),  Localizer.NL($"{Text.StyledNum(picture.Creator?.Reputation ?? 0)} ({picture.Creator?.MarkedUpName ?? CommonLocs.Unknown}) -> {userRepCulture.AsCulture()}"));
                yield return (Localizer.Do($"Picture's reputation:"), TextLoc.StyledNum(0f)); //todo
                yield return (Localizer.Do($"Resulting value:"), userRepCulture.AsCulture());
            }*/
        }

        static LocString RepTitle { get; } = TextLoc.UnderlineLocStr("Reputation");

    }
}
