// ============================================================
// BILLING CODE SUGGESTIONS — ICD-10 + CPT from note analysis
// Uses NLM Clinical Tables API for ICD-10 lookup (free, no auth)
// ============================================================

var express = require('express');
var router = express.Router();
var { authMiddleware } = require('../middleware/auth');
var logger = require('../utils/logger');

// ── CPT E/M Reference Tables ──────────────────────────────────────

var CPT_EM = {
  outpatient_new: [
    { code: '99202', level: 2, desc: 'New patient, straightforward MDM', time: '15-29 min' },
    { code: '99203', level: 3, desc: 'New patient, low complexity MDM', time: '30-44 min' },
    { code: '99204', level: 4, desc: 'New patient, moderate complexity MDM', time: '45-59 min' },
    { code: '99205', level: 5, desc: 'New patient, high complexity MDM', time: '60-74 min' }
  ],
  outpatient_established: [
    { code: '99211', level: 1, desc: 'Established patient, may not require physician', time: '—' },
    { code: '99212', level: 2, desc: 'Established patient, straightforward MDM', time: '10-19 min' },
    { code: '99213', level: 3, desc: 'Established patient, low complexity MDM', time: '20-29 min' },
    { code: '99214', level: 4, desc: 'Established patient, moderate complexity MDM', time: '30-39 min' },
    { code: '99215', level: 5, desc: 'Established patient, high complexity MDM', time: '40-54 min' }
  ],
  wellvisit_new: [
    { code: '99381', desc: 'Preventive visit, new patient, infant (< 1 year)' },
    { code: '99382', desc: 'Preventive visit, new patient, early childhood (1-4 years)' },
    { code: '99383', desc: 'Preventive visit, new patient, late childhood (5-11 years)' },
    { code: '99384', desc: 'Preventive visit, new patient, adolescent (12-17 years)' },
    { code: '99385', desc: 'Preventive visit, new patient, 18-39 years' }
  ],
  wellvisit_established: [
    { code: '99391', desc: 'Preventive visit, established patient, infant (< 1 year)' },
    { code: '99392', desc: 'Preventive visit, established patient, early childhood (1-4 years)' },
    { code: '99393', desc: 'Preventive visit, established patient, late childhood (5-11 years)' },
    { code: '99394', desc: 'Preventive visit, established patient, adolescent (12-17 years)' },
    { code: '99395', desc: 'Preventive visit, established patient, 18-39 years' }
  ],
  ed: [
    { code: '99281', level: 1, desc: 'ED visit, straightforward MDM' },
    { code: '99282', level: 2, desc: 'ED visit, low complexity MDM' },
    { code: '99283', level: 3, desc: 'ED visit, moderate complexity MDM' },
    { code: '99284', level: 4, desc: 'ED visit, moderate-high complexity MDM' },
    { code: '99285', level: 5, desc: 'ED visit, high complexity MDM' }
  ],
  inpatient_admit: [
    { code: '99221', level: 1, desc: 'Initial hospital care, straightforward/low MDM' },
    { code: '99222', level: 2, desc: 'Initial hospital care, moderate MDM' },
    { code: '99223', level: 3, desc: 'Initial hospital care, high MDM' }
  ],
  inpatient_subsequent: [
    { code: '99231', level: 1, desc: 'Subsequent hospital care, straightforward/low MDM' },
    { code: '99232', level: 2, desc: 'Subsequent hospital care, moderate MDM' },
    { code: '99233', level: 3, desc: 'Subsequent hospital care, high MDM' }
  ],
  inpatient_discharge: [
    { code: '99238', desc: 'Hospital discharge, 30 min or less' },
    { code: '99239', desc: 'Hospital discharge, more than 30 min' }
  ]
};

// ── ROS Systems for counting ──────────────────────────────────────
var ROS_SYSTEMS = [
  'constitutional', 'eyes', 'ent', 'cardiovascular', 'respiratory',
  'gastrointestinal', 'genitourinary', 'musculoskeletal', 'integumentary',
  'neurological', 'psychiatric', 'endocrine', 'hematologic', 'allergic'
];

var PE_SYSTEMS = [
  'general', 'heent', 'eyes', 'ears', 'nose', 'throat', 'neck',
  'cardiovascular', 'respiratory', 'chest', 'lungs', 'abdomen', 'abdominal',
  'genitourinary', 'musculoskeletal', 'skin', 'integumentary',
  'neurologic', 'neurological', 'psychiatric', 'lymphatic'
];

// ── Extract diagnoses from note text ─────────────────────────────

// Common pediatric diagnoses — avoids NLM lookup for known terms
var COMMON_ICD10 = {
  'fever': { code: 'R50.9', name: 'Fever, unspecified' },
  'cough': { code: 'R05.9', name: 'Cough, unspecified' },
  'vomiting': { code: 'R11.10', name: 'Vomiting, unspecified' },
  'diarrhea': { code: 'R19.7', name: 'Diarrhea, unspecified' },
  'constipation': { code: 'K59.00', name: 'Constipation, unspecified' },
  'headache': { code: 'R51.9', name: 'Headache, unspecified' },
  'abdominal pain': { code: 'R10.9', name: 'Unspecified abdominal pain' },
  'rash': { code: 'R21', name: 'Rash and other nonspecific skin eruption' },
  'sore throat': { code: 'J02.9', name: 'Acute pharyngitis, unspecified' },
  'pharyngitis': { code: 'J02.9', name: 'Acute pharyngitis, unspecified' },
  'strep pharyngitis': { code: 'J02.0', name: 'Streptococcal pharyngitis' },
  'otitis media': { code: 'H66.90', name: 'Otitis media, unspecified' },
  'acute otitis media': { code: 'H66.90', name: 'Otitis media, unspecified' },
  'uri': { code: 'J06.9', name: 'Acute upper respiratory infection, unspecified' },
  'upper respiratory infection': { code: 'J06.9', name: 'Acute upper respiratory infection, unspecified' },
  'bronchiolitis': { code: 'J21.9', name: 'Acute bronchiolitis, unspecified' },
  'asthma': { code: 'J45.20', name: 'Mild intermittent asthma, uncomplicated' },
  'asthma exacerbation': { code: 'J45.21', name: 'Mild intermittent asthma with acute exacerbation' },
  'pneumonia': { code: 'J18.9', name: 'Pneumonia, unspecified organism' },
  'croup': { code: 'J05.0', name: 'Acute obstructive laryngitis' },
  'bronchitis': { code: 'J20.9', name: 'Acute bronchitis, unspecified' },
  'eczema': { code: 'L30.9', name: 'Dermatitis, unspecified' },
  'atopic dermatitis': { code: 'L20.9', name: 'Atopic dermatitis, unspecified' },
  'urticaria': { code: 'L50.9', name: 'Urticaria, unspecified' },
  'conjunctivitis': { code: 'H10.9', name: 'Unspecified conjunctivitis' },
  'urinary tract infection': { code: 'N39.0', name: 'Urinary tract infection, site not specified' },
  'uti': { code: 'N39.0', name: 'Urinary tract infection, site not specified' },
  'gastroenteritis': { code: 'K52.9', name: 'Noninfective gastroenteritis and colitis, unspecified' },
  'dehydration': { code: 'E86.0', name: 'Dehydration' },
  'seizure': { code: 'R56.9', name: 'Unspecified convulsions' },
  'febrile seizure': { code: 'R56.00', name: 'Simple febrile convulsions' },
  'anemia': { code: 'D64.9', name: 'Anemia, unspecified' },
  'iron deficiency anemia': { code: 'D50.9', name: 'Iron deficiency anemia, unspecified' },
  'obesity': { code: 'E66.01', name: 'Morbid (severe) obesity due to excess calories' },
  'failure to thrive': { code: 'R62.51', name: 'Failure to thrive (child)' },
  'adhd': { code: 'F90.9', name: 'Attention-deficit hyperactivity disorder, unspecified type' },
  'anxiety': { code: 'F41.9', name: 'Anxiety disorder, unspecified' },
  'depression': { code: 'F32.9', name: 'Major depressive disorder, single episode, unspecified' },
  'well child': { code: 'Z00.129', name: 'Encounter for routine child health examination without abnormal findings' },
  'well child visit': { code: 'Z00.129', name: 'Encounter for routine child health examination without abnormal findings' },
  'newborn': { code: 'Z00.110', name: 'Health examination for newborn under 8 days old' },
  'jaundice': { code: 'P59.9', name: 'Neonatal jaundice, unspecified' },
  'neonatal jaundice': { code: 'P59.9', name: 'Neonatal jaundice, unspecified' },
  'hyperbilirubinemia': { code: 'P59.9', name: 'Neonatal jaundice, unspecified' }
};

function extractDiagnoses(noteText) {
  var diagnoses = [];

  // Look for Assessment section
  var assessmentMatch = noteText.match(/(?:assessment|diagnos[ie]s|impression|a\/p|assessment\s*(?:and|&)\s*plan)[:\s]*\n?([\s\S]*?)(?:\n\s*(?:plan|disposition|follow.up|return|recommendations)[:\s]|\n\s*\n\s*\n|$)/i);
  var assessmentText = assessmentMatch ? assessmentMatch[1] : noteText;

  // Extract ICD-10 codes already in the text (e.g., "J06.9" or "(J06.9)")
  var icdPattern = /\b([A-TV-Z]\d{2}(?:\.\d{1,4})?)\b/g;
  var icdMatch;
  while ((icdMatch = icdPattern.exec(noteText)) !== null) {
    diagnoses.push({ term: icdMatch[1], type: 'icd_extracted' });
  }

  // Extract numbered diagnoses from assessment
  var numberedLines = assessmentText.match(/^\s*\d+[\.\)]\s*(.+)$/gm);
  if (numberedLines) {
    numberedLines.forEach(function(line) {
      var term = line.replace(/^\s*\d+[\.\)]\s*/, '').replace(/\s*[\(\[].*$/, '').trim();
      if (term.length > 3 && term.length < 100) {
        diagnoses.push({ term: term, type: 'numbered' });
      }
    });
  }

  // Extract dash/bullet diagnoses
  var bulletLines = assessmentText.match(/^\s*[-•]\s*(.+)$/gm);
  if (bulletLines) {
    bulletLines.forEach(function(line) {
      var term = line.replace(/^\s*[-•]\s*/, '').replace(/\s*[\(\[].*$/, '').trim();
      if (term.length > 3 && term.length < 100) {
        diagnoses.push({ term: term, type: 'bullet' });
      }
    });
  }

  // If no structured diagnoses found, try to extract from inline text
  if (diagnoses.length === 0) {
    var commonPatterns = [
      /(?:diagnosed? with|assessment of|consistent with|suggestive of|likely|probable)\s+([^,\.\n]{3,60})/gi
    ];
    commonPatterns.forEach(function(pattern) {
      var m;
      while ((m = pattern.exec(assessmentText)) !== null) {
        diagnoses.push({ term: m[1].trim(), type: 'inline' });
      }
    });
  }

  // Deduplicate
  var seen = {};
  return diagnoses.filter(function(d) {
    var key = d.term.toLowerCase();
    if (seen[key]) return false;
    seen[key] = true;
    return true;
  });
}

// ── Count ROS and PE systems documented ──────────────────────────

function countDocumentedSystems(noteText) {
  var lower = noteText.toLowerCase();
  var rosCount = 0;
  var peCount = 0;

  ROS_SYSTEMS.forEach(function(sys) {
    if (lower.indexOf(sys) !== -1) rosCount++;
  });
  // Also check common abbreviations
  if (/\bENT\b/.test(noteText)) rosCount++;
  if (/\bGI\b/.test(noteText)) rosCount++;
  if (/\bGU\b/.test(noteText)) rosCount++;
  if (/\bMSK\b/.test(noteText)) rosCount++;

  PE_SYSTEMS.forEach(function(sys) {
    if (lower.indexOf(sys) !== -1) peCount++;
  });

  return { rosCount: Math.min(rosCount, 14), peCount: Math.min(peCount, 12) };
}

// ── Estimate E/M complexity level ────────────────────────────────

function estimateEMLevel(noteText, diagnosisCount) {
  var systems = countDocumentedSystems(noteText);
  var noteLength = noteText.length;

  // MDM complexity estimation (simplified 2021 E/M guidelines)
  // Based on: number of diagnoses, data reviewed, risk
  var mdmLevel = 2; // default straightforward

  if (diagnosisCount >= 4 || noteLength > 3000) mdmLevel = 4;
  else if (diagnosisCount >= 2 || noteLength > 1500) mdmLevel = 3;

  // Check for high-risk indicators
  var highRisk = /(?:admit|hospitali[sz]|intubat|seizure|sepsis|meningitis|fracture|surgery|ICU|PICU|NICU|emergent|critical|unstable)/i.test(noteText);
  if (highRisk) mdmLevel = Math.max(mdmLevel, 4);

  var moderateRisk = /(?:dehydrat|IV fluid|antibiotic|x-ray|CT scan|MRI|ultrasound|lab|CBC|CMP|urinalysis|blood culture|lumbar puncture)/i.test(noteText);
  if (moderateRisk) mdmLevel = Math.max(mdmLevel, 3);

  return {
    level: Math.min(mdmLevel, 5),
    rosCount: systems.rosCount,
    peCount: systems.peCount,
    diagnosisCount: diagnosisCount,
    complexity: mdmLevel >= 4 ? 'high' : mdmLevel >= 3 ? 'moderate' : 'low'
  };
}

// ── Get age category for well visit CPT ──────────────────────────

function getWellVisitCPT(patientAge) {
  var ageStr = (patientAge || '').toLowerCase();
  // Sum all units so "4 yr 11 mo" → 4.91 years, not 4. Prior behaviour
  // used only the first matched unit, which mis-bucketed patients like
  // 59 months (should be 5-11y bracket, was billed as 1-4y).
  var ageYears = 0;
  var yearMatch  = ageStr.match(/(\d+(?:\.\d+)?)\s*(?:years?|yrs?|y)\b/);
  var monthMatch = ageStr.match(/(\d+(?:\.\d+)?)\s*(?:months?|mos?|mo)\b/);
  var weekMatch  = ageStr.match(/(\d+(?:\.\d+)?)\s*(?:weeks?|wks?|wk)\b/);
  var dayMatch   = ageStr.match(/(\d+(?:\.\d+)?)\s*(?:days?|d)\b/);

  if (yearMatch)  ageYears += parseFloat(yearMatch[1]);
  if (monthMatch) ageYears += parseFloat(monthMatch[1]) / 12;
  if (weekMatch)  ageYears += parseFloat(weekMatch[1]) / 52;
  if (dayMatch)   ageYears += parseFloat(dayMatch[1]) / 365;

  // Sanity-bound: negative or absurdly large values default to the
  // lowest-level code. 0 is legitimate (newborn well visit).
  if (isNaN(ageYears) || ageYears < 0 || ageYears > 120) ageYears = 0;

  // Established patient codes (most common)
  if (ageYears < 1)  return CPT_EM.wellvisit_established[0];
  if (ageYears < 5)  return CPT_EM.wellvisit_established[1];
  if (ageYears < 12) return CPT_EM.wellvisit_established[2];
  if (ageYears < 18) return CPT_EM.wellvisit_established[3];
  return CPT_EM.wellvisit_established[4];
}

// ── NLM ICD-10 lookup ────────────────────────────────────────────

async function lookupICD10(term) {
  try {
    // Use maxList=1 for short/generic terms, 2 for longer specific terms
    var maxList = term.split(/\s+/).length >= 3 ? 2 : 1;
    var url = 'https://clinicaltables.nlm.nih.gov/api/icd10cm/v3/search?sf=code,name&terms=' + encodeURIComponent(term) + '&maxList=' + maxList;
    var resp = await fetch(url, { signal: AbortSignal.timeout(5000) });
    if (!resp.ok) return [];
    var data = await resp.json();
    // NLM response format: [totalCount, codeArray, extraDataArray, displayArray]
    if (!data || !data[1] || !data[3]) return [];
    return data[3].map(function(item) {
      return { code: item[0], name: item[1] };
    });
  } catch (e) {
    return [];
  }
}

// ── Main endpoint ────────────────────────────────────────────────

router.post('/suggest-codes', authMiddleware, async function(req, res) {
  try {
    var { noteText, noteType, patientAge, visitType } = req.body;
    if (!noteText) return res.status(400).json({ error: 'No note text provided' });
    // ReDoS guard — regex over note text can blow up on pathological inputs.
    if (typeof noteText !== 'string' || noteText.length > 20000) {
      return res.status(400).json({ error: 'Note too long for billing analysis (max 20,000 chars)' });
    }

    // 1. Extract diagnoses from note text
    var diagnoses = extractDiagnoses(noteText);

    // 2. Look up ICD-10 codes via NLM for each diagnosis
    var icd10Results = [];
    var seen = {};

    // First add any ICD-10 codes already extracted from the text
    diagnoses.filter(function(d) { return d.type === 'icd_extracted'; }).forEach(function(d) {
      if (!seen[d.term]) { icd10Results.push({ code: d.term, name: '', source: 'extracted' }); seen[d.term] = true; }
    });

    // Then look up non-ICD terms — check local common map first, then NLM
    var lookupTerms = diagnoses.filter(function(d) { return d.type !== 'icd_extracted'; }).slice(0, 8);
    for (var i = 0; i < lookupTerms.length; i++) {
      var termLower = lookupTerms[i].term.toLowerCase().trim();
      var localMatch = COMMON_ICD10[termLower];
      if (localMatch && !seen[localMatch.code]) {
        icd10Results.push({ code: localMatch.code, name: localMatch.name, source: 'local' });
        seen[localMatch.code] = true;
      } else if (!localMatch) {
        var results = await lookupICD10(lookupTerms[i].term);
        results.forEach(function(r) {
          if (!seen[r.code]) { icd10Results.push({ code: r.code, name: r.name, source: 'nlm' }); seen[r.code] = true; }
        });
      }
    }

    // 3. Suggest CPT codes based on note type and complexity
    var cptResults = [];
    var emLevel = estimateEMLevel(noteText, diagnoses.length);

    if (noteType === 'wellvisit') {
      var wvCode = getWellVisitCPT(patientAge);
      if (wvCode) cptResults.push(wvCode);
      // Well visits may also have a problem-oriented code if issues found
      if (diagnoses.length > 1) {
        cptResults.push({ code: '99213', desc: 'May also bill problem-oriented visit if significant issue addressed', note: 'modifier -25' });
      }
    } else if (noteType === 'hospital' || visitType === 'inpatient') {
      var isAdmission = /(?:admit|admission|initial hospital|H&P)/i.test(noteText);
      var isDischarge = /(?:discharge|d\/c summary)/i.test(noteText);
      if (isDischarge) {
        cptResults.push(CPT_EM.inpatient_discharge[noteText.length > 2000 ? 1 : 0]);
      } else if (isAdmission) {
        var admitIdx = Math.min(emLevel.level - 2, 2);
        if (admitIdx >= 0) cptResults.push(CPT_EM.inpatient_admit[admitIdx]);
      } else {
        var subIdx = Math.min(emLevel.level - 2, 2);
        if (subIdx >= 0) cptResults.push(CPT_EM.inpatient_subsequent[subIdx]);
      }
    } else if (visitType === 'ed') {
      var edIdx = Math.min(emLevel.level - 1, 4);
      cptResults.push(CPT_EM.ed[edIdx]);
    } else {
      // Outpatient — determine new vs established
      var isNew = /\bnew patient\b/i.test(noteText);
      var table = isNew ? CPT_EM.outpatient_new : CPT_EM.outpatient_established;
      var idx = Math.min(emLevel.level - 1, table.length - 1);
      cptResults.push(table[idx]);
    }

    logger.audit(req.user.id, 'suggest_billing_codes', 'Suggested ' + icd10Results.length + ' ICD-10 + ' + cptResults.length + ' CPT codes', req, { category: 'clinical' });

    res.json({
      success: true,
      icd10: icd10Results.slice(0, 10),
      cpt: cptResults,
      emLevel: emLevel
    });
  } catch (err) {
    console.error('[Billing] Error:', err.message);
    res.status(500).json({ error: 'Code suggestion failed' });
  }
});

module.exports = router;
