using Microsoft.Extensions.Logging;
using RealCV.Application.Interfaces;
using RealCV.Application.Models;

namespace RealCV.Infrastructure.Services;

public sealed class TimelineAnalyserService : ITimelineAnalyserService
{
    private readonly ILogger<TimelineAnalyserService> _logger;

    private const int MinimumGapMonths = 3;
    private const int AllowedOverlapMonths = 2;

    public TimelineAnalyserService(ILogger<TimelineAnalyserService> logger)
    {
        _logger = logger;
    }

    public TimelineAnalysisResult Analyse(List<EmploymentEntry> employmentHistory)
    {
        ArgumentNullException.ThrowIfNull(employmentHistory);

        if (employmentHistory.Count == 0)
        {
            _logger.LogDebug("No employment history to analyse");
            return new TimelineAnalysisResult
            {
                TotalGapMonths = 0,
                TotalOverlapMonths = 0,
                Gaps = [],
                Overlaps = []
            };
        }

        // Filter entries with valid dates and sort by start date
        var sortedEmployment = employmentHistory
            .Where(e => e.StartDate.HasValue)
            .OrderBy(e => e.StartDate!.Value)
            .ToList();

        if (sortedEmployment.Count == 0)
        {
            _logger.LogDebug("No employment entries with valid dates to analyse");
            return new TimelineAnalysisResult
            {
                TotalGapMonths = 0,
                TotalOverlapMonths = 0,
                Gaps = [],
                Overlaps = []
            };
        }

        var gaps = DetectGaps(sortedEmployment);
        var overlaps = DetectOverlaps(sortedEmployment);

        var totalGapMonths = gaps.Sum(g => g.Months);
        var totalOverlapMonths = overlaps.Sum(o => o.Months);

        _logger.LogInformation(
            "Timeline analysis complete: {GapCount} gaps ({TotalGapMonths} months), {OverlapCount} overlaps ({TotalOverlapMonths} months)",
            gaps.Count, totalGapMonths, overlaps.Count, totalOverlapMonths);

        return new TimelineAnalysisResult
        {
            TotalGapMonths = totalGapMonths,
            TotalOverlapMonths = totalOverlapMonths,
            Gaps = gaps,
            Overlaps = overlaps
        };
    }

    private List<TimelineGap> DetectGaps(List<EmploymentEntry> sortedEmployment)
    {
        var gaps = new List<TimelineGap>();

        for (var i = 0; i < sortedEmployment.Count - 1; i++)
        {
            var current = sortedEmployment[i];
            var next = sortedEmployment[i + 1];

            // Get the effective end date for the current position
            var currentEndDate = GetEffectiveEndDate(current);
            var nextStartDate = next.StartDate!.Value;

            // Skip if there's no gap or overlap
            if (currentEndDate >= nextStartDate)
            {
                continue;
            }

            var gapMonths = CalculateMonthsDifference(currentEndDate, nextStartDate);

            // Only report gaps of 3+ months
            if (gapMonths >= MinimumGapMonths)
            {
                _logger.LogDebug(
                    "Detected {Months} month gap between {EndDate} and {StartDate}",
                    gapMonths, currentEndDate, nextStartDate);

                gaps.Add(new TimelineGap
                {
                    StartDate = currentEndDate,
                    EndDate = nextStartDate,
                    Months = gapMonths
                });
            }
        }

        return gaps;
    }

    private List<TimelineOverlap> DetectOverlaps(List<EmploymentEntry> sortedEmployment)
    {
        var overlaps = new List<TimelineOverlap>();

        for (var i = 0; i < sortedEmployment.Count; i++)
        {
            for (var j = i + 1; j < sortedEmployment.Count; j++)
            {
                var earlier = sortedEmployment[i];
                var later = sortedEmployment[j];

                var overlap = CalculateOverlap(earlier, later);

                if (overlap is not null && overlap.Value.Months > AllowedOverlapMonths)
                {
                    _logger.LogDebug(
                        "Detected {Months} month overlap between {Company1} and {Company2}",
                        overlap.Value.Months, earlier.CompanyName, later.CompanyName);

                    overlaps.Add(new TimelineOverlap
                    {
                        Company1 = earlier.CompanyName,
                        Company2 = later.CompanyName,
                        OverlapStart = overlap.Value.Start,
                        OverlapEnd = overlap.Value.End,
                        Months = overlap.Value.Months
                    });
                }
            }
        }

        return overlaps;
    }

    private static (DateOnly Start, DateOnly End, int Months)? CalculateOverlap(
        EmploymentEntry earlier,
        EmploymentEntry later)
    {
        if (!earlier.StartDate.HasValue || !later.StartDate.HasValue)
        {
            return null;
        }

        var earlierEnd = GetEffectiveEndDate(earlier);
        var laterStart = later.StartDate.Value;

        // No overlap if earlier job ended before later job started
        if (earlierEnd <= laterStart)
        {
            return null;
        }

        var laterEnd = GetEffectiveEndDate(later);

        // The overlap period
        var overlapStart = laterStart;
        var overlapEnd = earlierEnd < laterEnd ? earlierEnd : laterEnd;

        if (overlapStart >= overlapEnd)
        {
            return null;
        }

        var months = CalculateMonthsDifference(overlapStart, overlapEnd);

        return (overlapStart, overlapEnd, months);
    }

    private static DateOnly GetEffectiveEndDate(EmploymentEntry entry)
    {
        if (entry.EndDate.HasValue)
        {
            return entry.EndDate.Value;
        }

        // If marked as current or no end date, use today
        return DateOnly.FromDateTime(DateTime.UtcNow);
    }

    private static int CalculateMonthsDifference(DateOnly startDate, DateOnly endDate)
    {
        var yearDiff = endDate.Year - startDate.Year;
        var monthDiff = endDate.Month - startDate.Month;
        var totalMonths = (yearDiff * 12) + monthDiff;

        // Add a month if we've passed the day in the month
        if (endDate.Day >= startDate.Day)
        {
            totalMonths++;
        }

        return Math.Max(0, totalMonths);
    }
}
