using System;
using System.Linq;
using System.Collections.Generic;
using Verse;
using rjw;
using rjw.Modules.Interactions.Objects;
using rjw.Modules.Interactions.Contexts;
using rjw.Modules.Interactions.Extensions;
using rjw.Modules.Interactions.Internals.Implementation;
using rjw.Modules.Interactions.Rules.InteractionRules;
using rjw.Modules.Interactions.Rules.InteractionRules.Implementation;

namespace SCE
{
    public static class InteractionUtility
    {
        public static SexProps MakeSexProps(Pawn pawn, Pawn partner = null, Func<InteractionWithExtension, float> scorer = null, IInteractionRule rule = null, bool useRwjScores = true, float submissivePreferenceWeight = 1f)
        {
            var result = new SexProps() { pawn = pawn, partner = partner };
            JustPickADamnSexType(result, scorer, rule, useRwjScores, submissivePreferenceWeight);
            return result;
        }

        public static void JustPickADamnSexType(SexProps props, Func<InteractionWithExtension, float> scorer = null, IInteractionRule rule = null, bool useRwjScores = true, float submissivePreferenceWeight = 1f)
        {
            rule ??= new ConsensualInteractionRule();
            InteractionContext context = GetUsableContext(props);
            InteractionInternals fml = context.Internals;

            var requirementService = InteractionRequirementService.Instance;
            IEnumerable<InteractionWithExtension> interactions = rule.Interactions.Where(i => requirementService.FufillRequirements(i, fml.Dominant, fml.Submissive));

            Func<InteractionWithExtension, float> finalScorer;
            if (useRwjScores)
            {
                if (scorer != null)
                {
                    finalScorer = (i => scorer(i) * InteractionScoringService.Instance.Score(i, fml.Dominant, fml.Submissive).GetScore(1f));
                }
                else
                {
                    finalScorer = (i => InteractionScoringService.Instance.Score(i, fml.Dominant, fml.Submissive).GetScore(1f));
                }
            }
            else
            {
                finalScorer = scorer;
            }

            interactions.TryRandomElementByWeight(finalScorer, out var interactionWithExtension);
            interactionWithExtension ??= rule.Default;

            context.Internals.Selected = interactionWithExtension;

            InteractionBuilderService.Instance.Build(context);

            Interaction interaction = context.Outputs.Generated;

            props.sexType = interaction.RjwSexType;
            props.rulePack = interaction.RulePack.defName;
            props.dictionaryKey = interaction.InteractionDef.Interaction;
        }

        // Creates an InteractionContext and configures it with basic internals and part restrictions
        public static InteractionContext GetUsableContext(SexProps props)
        {
            InteractionContext context = new InteractionContext(
                new InteractionInputs(){
                    Initiator = props.pawn,
                    Partner = props.partner,
                    IsRape = props.isRape,
                    IsWhoring = props.isWhoring
                }
            );

            InteractionInternals fml = new InteractionInternals() {
                InteractionType = InteractionTypeDetectorService.Instance.DetectInteractionType(context),   /* I've already gone through most of the other IServices, so I'm just going to ignore this one and hope it spits out the right thing by default */
                IsReverse = false,                  /* Just let the caller worry about this part */
                Dominant = new InteractionPawn() {
                    Pawn = props.pawn,
                    Parts = props.pawn.GetSexablePawnParts()
                },
                Submissive = new InteractionPawn() {
                    Pawn = props.partner,
                    Parts = props.partner.GetSexablePawnParts()
                }
            };
            context.Internals = fml;

            BlockedPartDetectorService.Instance.DetectBlockedParts(fml);
            PartPreferenceDetectorService.Instance.DetectPartPreferences(context);

            return context;
        }
    }
}