using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using JetBrains.Annotations;
using Realms;

namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
    /// <summary>
    /// The representation of a specific puzzle in the database. This includes all measurements for this puzzle.
    /// </summary>
    [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
    public class Puzzle : RealmObject
    {
        [PrimaryKey]
        public int ID { get; set; }
        /// <summary>
        /// All puzzle measurements recorded for this puzzle.
        /// </summary>
        public IList<PuzzleMeasurement> Measurements { get; }

        /// <summary>
        /// How much time has been spent on this puzzle in total. This is used to calculate the average time to solve this puzzle.
        /// </summary>
        public float TotalTimeSpentOnPuzzle => Measurements.Sum(measurement => measurement.Time);
        /// <summary>
        /// The average time to solve this puzzle using all measurements recorded for it.
        /// </summary>
        public float AverageTimeToSolve => Measurements.Count > 0 ? TotalTimeSpentOnPuzzle / Measurements.Count : 0f;
        /// <summary>
        /// The normal distribution of all recorded measurements.
        /// </summary>
        public NormalDistribution Distribution => new(TimesAsSamples());

        [UsedImplicitly]
        public Puzzle() {}
        public Puzzle(PuzzleModuleDescription puzzle)
        {
            ID = puzzle.Id;
        }

        /// <summary>
        /// Estimate how long a player in the given percentile will take to solve this puzzle.
        /// </summary>
        public float EstimateTimeToSolve(float percentile) => Distribution.InverseCumulative(percentile);

        private float[] TimesAsSamples()
        {
            var samples = new float[Measurements.Count];
            
            for (var i = 0; i < Measurements.Count; i++)
            {
                samples[i] = Measurements[i].Time;
            }

            return samples;
        }

        public override string ToString()
        {
            return $"{Engine.Theme.GetPuzzle(ID)}: avg. {AverageTimeToSolve.ToTimeSpan():m':'ss} ({string.Join(", ", Measurements)})";
        }
    }
}