namespace JobTrackerApi.Services
{
    public sealed record StageDurationPoint(string Stage, int Order, double MedianDays, int Count);

    /// <summary>One job's position: its canonical stage and when it entered that stage.</summary>
    public sealed record StageOccupancy(string Status, DateTime EnteredStageAtUtc);

    /// <summary>
    /// Pure time-in-stage analytics: for each active pipeline stage, the median number of days
    /// the jobs currently sitting there have been waiting. Closed stages (Rejected/Ghosted) and
    /// the terminal success stage (Offer) are excluded — "how long has this been stuck" only
    /// makes sense for stages you still act on.
    /// </summary>
    public static class StageAnalytics
    {
        public static List<StageDurationPoint> TimeInStage(IEnumerable<StageOccupancy> jobs, DateTime nowUtc)
        {
            var byStage = jobs
                .Select(j => (Stage: JobPipeline.Normalize(j.Status), Days: Math.Max(0, (nowUtc - j.EnteredStageAtUtc).TotalDays)))
                .Where(x => JobPipeline.Stages.Any(s => s.Key == x.Stage && s.Category == PipelineCategory.Active))
                .GroupBy(x => x.Stage);

            var points = new List<StageDurationPoint>();
            foreach (var group in byStage)
            {
                var days = group.Select(x => x.Days).OrderBy(x => x).ToList();
                points.Add(new StageDurationPoint(
                    Stage: group.Key,
                    Order: JobPipeline.OrderOf(group.Key),
                    MedianDays: Median(days),
                    Count: days.Count));
            }

            return points.OrderBy(p => p.Order).ToList();
        }

        private static double Median(IReadOnlyList<double> sorted)
        {
            if (sorted.Count == 0) return 0;
            var mid = sorted.Count / 2;
            var median = sorted.Count % 2 == 0 ? (sorted[mid - 1] + sorted[mid]) / 2d : sorted[mid];
            return Math.Round(median, 1);
        }
    }
}
