using RealCV.Application.Data;
using RealCV.Application.Interfaces;
using RealCV.Application.Models;

namespace RealCV.Infrastructure.Services;

public sealed class EducationVerifierService : IEducationVerifierService
{
    private const int MinimumDegreeYears = 1;
    private const int MaximumDegreeYears = 8;
    private const int MinimumGraduationAge = 18;

    public EducationVerificationResult Verify(EducationEntry education)
    {
        var institution = education.Institution;

        // Check for diploma mill first (highest priority flag)
        if (DiplomaMills.IsDiplomaMill(institution))
        {
            return new EducationVerificationResult
            {
                ClaimedInstitution = institution,
                Status = "DiplomaMill",
                IsVerified = false,
                IsDiplomaMill = true,
                IsSuspicious = true,
                VerificationNotes = "Institution not found in accredited institutions database",
                ClaimedStartDate = education.StartDate,
                ClaimedEndDate = education.EndDate,
                DatesArePlausible = true,
                ClaimedQualification = education.Qualification,
                ClaimedSubject = education.Subject
            };
        }

        // Check for suspicious patterns
        if (DiplomaMills.HasSuspiciousPattern(institution))
        {
            return new EducationVerificationResult
            {
                ClaimedInstitution = institution,
                Status = "Suspicious",
                IsVerified = false,
                IsDiplomaMill = false,
                IsSuspicious = true,
                VerificationNotes = "Institution not found in recognised institutions database",
                ClaimedStartDate = education.StartDate,
                ClaimedEndDate = education.EndDate,
                DatesArePlausible = true,
                ClaimedQualification = education.Qualification,
                ClaimedSubject = education.Subject
            };
        }

        // Check if it's a recognised UK institution
        var officialName = UKInstitutions.GetOfficialName(institution);
        if (officialName != null)
        {
            var (datesPlausible, dateNotes) = CheckDatePlausibility(education.StartDate, education.EndDate);

            return new EducationVerificationResult
            {
                ClaimedInstitution = institution,
                MatchedInstitution = officialName,
                Status = "Recognised",
                IsVerified = true,
                IsDiplomaMill = false,
                IsSuspicious = false,
                VerificationNotes = institution.Equals(officialName, StringComparison.OrdinalIgnoreCase)
                    ? "Verified UK higher education institution"
                    : $"Matched to official name: {officialName}",
                ClaimedStartDate = education.StartDate,
                ClaimedEndDate = education.EndDate,
                DatesArePlausible = datesPlausible,
                DatePlausibilityNotes = dateNotes,
                ClaimedQualification = education.Qualification,
                ClaimedSubject = education.Subject
            };
        }

        // Not in our database - could be international or unrecognised
        var isUnknownInstitution = string.IsNullOrWhiteSpace(institution) ||
                                   institution.Equals("Unknown Institution", StringComparison.OrdinalIgnoreCase) ||
                                   institution.Equals("Unknown", StringComparison.OrdinalIgnoreCase);

        return new EducationVerificationResult
        {
            ClaimedInstitution = institution,
            Status = "Unknown",
            IsVerified = false,
            IsDiplomaMill = false,
            IsSuspicious = false,
            VerificationNotes = isUnknownInstitution ? null : "Institution not found in UK recognised institutions database. May be an international institution.",
            ClaimedStartDate = education.StartDate,
            ClaimedEndDate = education.EndDate,
            DatesArePlausible = true,
            ClaimedQualification = education.Qualification,
            ClaimedSubject = education.Subject
        };
    }

    public List<EducationVerificationResult> VerifyAll(
        List<EducationEntry> education,
        List<EmploymentEntry>? employment = null)
    {
        var results = new List<EducationVerificationResult>();

        foreach (var edu in education)
        {
            var result = Verify(edu);

            // If we have employment data, check for timeline issues
            if (employment?.Count > 0 && result.ClaimedEndDate.HasValue)
            {
                var (timelinePlausible, timelineNotes) = CheckEducationEmploymentTimeline(
                    result.ClaimedEndDate.Value,
                    employment);

                if (!timelinePlausible)
                {
                    result = result with
                    {
                        DatesArePlausible = false,
                        DatePlausibilityNotes = CombineNotes(result.DatePlausibilityNotes, timelineNotes)
                    };
                }
            }

            results.Add(result);
        }

        // Check for overlapping education periods
        CheckOverlappingEducation(results);

        return results;
    }

    private static (bool isPlausible, string? notes) CheckDatePlausibility(DateOnly? startDate, DateOnly? endDate)
    {
        if (!startDate.HasValue || !endDate.HasValue)
        {
            return (true, null);
        }

        var start = startDate.Value;
        var end = endDate.Value;

        // End date should be after start date
        if (end <= start)
        {
            return (false, "End date is before or equal to start date");
        }

        // Check course duration is reasonable
        var years = (end.ToDateTime(TimeOnly.MinValue) - start.ToDateTime(TimeOnly.MinValue)).TotalDays / 365.25;

        if (years < MinimumDegreeYears)
        {
            return (false, $"Course duration ({years:F1} years) is unusually short for a degree");
        }

        if (years > MaximumDegreeYears)
        {
            return (false, $"Course duration ({years:F1} years) is unusually long");
        }

        // Check if graduation date is in the future
        if (end > DateOnly.FromDateTime(DateTime.UtcNow))
        {
            return (true, "Graduation date is in the future - possibly currently studying");
        }

        return (true, null);
    }

    private static (bool isPlausible, string? notes) CheckEducationEmploymentTimeline(
        DateOnly graduationDate,
        List<EmploymentEntry> employment)
    {
        // Find the earliest employment start date
        var earliestEmployment = employment
            .Where(e => e.StartDate.HasValue)
            .OrderBy(e => e.StartDate)
            .FirstOrDefault();

        if (earliestEmployment?.StartDate == null)
        {
            return (true, null);
        }

        var employmentStart = earliestEmployment.StartDate.Value;

        // If someone claims to have started full-time work significantly before graduating,
        // that's suspicious (unless it's clearly an internship/part-time role)
        var monthsBeforeGraduation = (graduationDate.ToDateTime(TimeOnly.MinValue) -
            employmentStart.ToDateTime(TimeOnly.MinValue)).TotalDays / 30;

        if (monthsBeforeGraduation > 24) // More than 2 years before graduation
        {
            var isLikelyInternship = earliestEmployment.JobTitle.Contains("intern", StringComparison.OrdinalIgnoreCase) ||
                                     earliestEmployment.JobTitle.Contains("placement", StringComparison.OrdinalIgnoreCase) ||
                                     earliestEmployment.JobTitle.Contains("trainee", StringComparison.OrdinalIgnoreCase);

            if (!isLikelyInternship)
            {
                return (false, $"Employment at {earliestEmployment.CompanyName} started {monthsBeforeGraduation:F0} months before claimed graduation");
            }
        }

        return (true, null);
    }

    private static void CheckOverlappingEducation(List<EducationVerificationResult> results)
    {
        var datedResults = results
            .Where(r => r.ClaimedStartDate.HasValue && r.ClaimedEndDate.HasValue)
            .ToList();

        for (var i = 0; i < datedResults.Count; i++)
        {
            for (var j = i + 1; j < datedResults.Count; j++)
            {
                var edu1 = datedResults[i];
                var edu2 = datedResults[j];

                if (PeriodsOverlap(
                    edu1.ClaimedStartDate!.Value, edu1.ClaimedEndDate!.Value,
                    edu2.ClaimedStartDate!.Value, edu2.ClaimedEndDate!.Value))
                {
                    // Find the actual index in the original results list
                    var idx1 = results.IndexOf(edu1);
                    var idx2 = results.IndexOf(edu2);

                    if (idx1 >= 0)
                    {
                        results[idx1] = edu1 with
                        {
                            DatePlausibilityNotes = CombineNotes(
                                edu1.DatePlausibilityNotes,
                                $"Overlaps with education at {edu2.ClaimedInstitution}")
                        };
                    }

                    if (idx2 >= 0)
                    {
                        results[idx2] = edu2 with
                        {
                            DatePlausibilityNotes = CombineNotes(
                                edu2.DatePlausibilityNotes,
                                $"Overlaps with education at {edu1.ClaimedInstitution}")
                        };
                    }
                }
            }
        }
    }

    private static bool PeriodsOverlap(DateOnly start1, DateOnly end1, DateOnly start2, DateOnly end2)
    {
        return start1 < end2 && start2 < end1;
    }

    private static string? CombineNotes(string? existing, string? additional)
    {
        if (string.IsNullOrEmpty(additional))
            return existing;
        if (string.IsNullOrEmpty(existing))
            return additional;
        return $"{existing}; {additional}";
    }
}
