using ClinicalInsightsPro.API.Data;
using ClinicalInsightsPro.API.DTOs;
using ClinicalInsightsPro.API.Models;
using ClinicalInsightsPro.API.Services.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace ClinicalInsightsPro.API.Services;

// Questionnaire-based rule engine that scores an encounter as High or Normal
// risk, combining chart data (conditions, vitals) with clinician-entered
// questionnaire responses. Rule weights are intentionally simple and modular
// so they can be extended, or eventually replaced by an ML model, per scope.
public class RiskScoringService : IRiskScoringService
{
    private readonly ApplicationDbContext _db;
    private const int HighRiskThreshold = 50;

    public RiskScoringService(ApplicationDbContext db)
    {
        _db = db;
    }

    public async Task<RiskAssessmentResultDto> AssessAsync(string patientId, QuestionnaireDto questionnaire)
    {
        var patient = await _db.Patients
            .Include(p => p.Conditions)
            .Include(p => p.Observations)
            .Include(p => p.Allergies)
            .Include(p => p.Medications)
            .Include(p => p.Encounters)
            .FirstOrDefaultAsync(p => p.Id == patientId);

        if (patient == null)
            throw new KeyNotFoundException($"Patient '{patientId}' was not found.");

        int score = 0;
        var factors = new List<string>();

        foreach (var c in patient.Conditions.Where(c => c.ClinicalStatus == "active"))
        {
            switch (c.Code)
            {
                case "E11.9":
                    score += 20; factors.Add("Active Type 2 diabetes (+20)"); break;
                case "I10":
                    score += 15; factors.Add("Essential hypertension (+15)"); break;
                case "J45.909":
                    score += 10; factors.Add("Asthma (+10)"); break;
                default:
                    score += 5; factors.Add($"Active condition: {c.Display} (+5)"); break;
            }
        }

        var latestSystolic = patient.Observations
            .Where(o => o.Code == "8480-6")
            .OrderByDescending(o => o.EffectiveDate)
            .FirstOrDefault();

        if (latestSystolic != null && latestSystolic.Value >= 160)
        {
            score += 20; factors.Add($"Systolic BP {latestSystolic.Value} mmHg — hypertensive urgency range (+20)");
        }
        else if (latestSystolic != null && latestSystolic.Value >= 140)
        {
            score += 10; factors.Add($"Systolic BP {latestSystolic.Value} mmHg — stage 2 hypertension (+10)");
        }

        var latestGlucose = patient.Observations
            .Where(o => o.Code == "2339-0")
            .OrderByDescending(o => o.EffectiveDate)
            .FirstOrDefault();

        if (latestGlucose != null && latestGlucose.Value >= 200)
        {
            score += 15; factors.Add($"Glucose {latestGlucose.Value} mg/dL — poorly controlled (+15)");
        }

        // Allergies — a known high-criticality allergy raises the risk of a
        // future adverse drug event, independent of what's happening today.
        var highCriticalityAllergies = patient.Allergies.Count(a => a.Criticality == "high");
        if (highCriticalityAllergies > 0)
        {
            var allergyPoints = Math.Min(highCriticalityAllergies * 10, 20);
            score += allergyPoints;
            factors.Add($"{highCriticalityAllergies} high-criticality allerg{(highCriticalityAllergies == 1 ? "y" : "ies")} on file (+{allergyPoints})");
        }

        // Medications — polypharmacy (commonly defined as 5+ concurrent
        // medications) is itself an established risk factor for interactions
        // and adverse events, regardless of what the medications are for.
        var activeMedicationCount = patient.Medications.Count(m => m.Status == "active");
        if (activeMedicationCount >= 5)
        {
            score += 12; factors.Add($"Polypharmacy — {activeMedicationCount} active medications (+12)");
        }
        else if (activeMedicationCount >= 3)
        {
            score += 6; factors.Add($"{activeMedicationCount} active medications (+6)");
        }

        // Encounters — frequent recent encounters (visits, admissions, ED
        // visits) in the last 12 months suggest an unstable or worsening
        // condition even if no single encounter looks alarming on its own.
        var recentEncounterCount = patient.Encounters.Count(e => e.EncounterDate >= DateTime.UtcNow.AddMonths(-12));
        if (recentEncounterCount >= 3)
        {
            score += 12; factors.Add($"{recentEncounterCount} encounters within the last 12 months (+12)");
        }
        else if (recentEncounterCount == 2)
        {
            score += 6; factors.Add("2 encounters within the last 12 months (+6)");
        }

        if (questionnaire.Smoker) { score += 10; factors.Add("Current smoker (+10)"); }
        if (questionnaire.FamilyHistoryHeartDisease) { score += 8; factors.Add("Family history of heart disease (+8)"); }
        if (questionnaire.PriorHospitalization) { score += 12; factors.Add("Prior hospitalization within 12 months (+12)"); }
        if (questionnaire.Bmi is >= 30) { score += 8; factors.Add($"BMI {questionnaire.Bmi:F1} — obese range (+8)"); }
        if (questionnaire.PainScore >= 7) { score += 5; factors.Add($"Reported pain score {questionnaire.PainScore}/10 (+5)"); }

        score = Math.Min(score, 100);
        var level = score >= HighRiskThreshold ? "High" : "Normal";

        if (!factors.Any())
            factors.Add("No significant risk factors identified from chart or questionnaire.");

        var assessment = new RiskAssessment
        {
            PatientId = patientId,
            Score = score,
            RiskLevel = level,
            Rationale = string.Join("; ", factors),
            AssessedDate = DateTime.UtcNow
        };

        _db.RiskAssessments.Add(assessment);
        await _db.SaveChangesAsync();

        return new RiskAssessmentResultDto
        {
            PatientId = patientId,
            Score = score,
            RiskLevel = level,
            ContributingFactors = factors,
            AssessedDate = assessment.AssessedDate
        };
    }
}
