var bpCache = null;

export function loadBpReferenceData() {
  if (bpCache) return Promise.resolve(bpCache);
  return fetch('/data/calculators/bp-reference.json', { credentials: 'same-origin' })
    .then(function(response) {
      if (!response.ok) throw new Error('Failed to load BP reference data');
      return response.json();
    })
    .then(function(data) {
      bpCache = data;
      return bpCache;
    });
}

function normalCDF(z) {
  var a1=0.254829592, a2=-0.284496736, a3=1.421413741, a4=-1.453152027, a5=1.061405429, p=0.3275911;
  var sign = z < 0 ? -1 : 1;
  z = Math.abs(z) / Math.sqrt(2);
  var t = 1 / (1 + p * z);
  var y = 1 - (((((a5*t+a4)*t)+a3)*t+a2)*t+a1)*t*Math.exp(-z*z);
  return 0.5 * (1 + sign * y);
}

export function calcHeightPercentile(reference, age, sex, heightCm) {
  var idx = Math.max(0, Math.min(217, Math.round(age * 12) - 24));
  var table = reference.heightLms[sex === 'female' ? 'female' : 'male'];
  var L = table.L[idx], M = table.M[idx], S = table.S[idx];
  var z = (Math.pow(heightCm / M, L) - 1) / (L * S);
  return { z: z, percentile: normalCDF(z) * 100 };
}

export function computeBPPercentile(reference, age, sex, heightCm, bpType, bpValue) {
  var t1, t2, t3, t4, t5, ta1, ta2, ta3, ta4, ta5, tb1, tb2, tb3, tb4, tb5, w;
  if (sex === 'female') {
    t1=106.7; t2=140.7; t3=154.0; t4=160.5; t5=168.9;
    ta1=5.00; ta2=10.70; ta3=13.16; ta4=14.51; ta5=17.33;
    tb1=6.701; tb2=16.438; tb3=46.80; tb4=84.46; tb5=203.608;
    w = (age - 10) * (heightCm - 147);
  } else {
    t1=107.8; t2=140.0; t3=154.5; t4=166.4; t5=179.1;
    ta1=5.06; ta2=10.79; ta3=13.22; ta4=14.51; ta5=17.30;
    tb1=-15; tb2=8.9; tb3=50.375; tb4=112.684; tb5=250.04;
    w = (age - 10) * (heightCm - 150);
  }

  var x = heightCm, y = age;

  var x2a = Math.max(0, x - t1); var x2b = Math.max(0, x - t4); var x2c = Math.max(0, x - t5);
  var x2 = Math.pow(x2a,3) - Math.pow(x2b,3)*(t5-t1)/(t5-t4) + Math.pow(x2c,3)*(t4-t1)/(t5-t4);
  var x3a = Math.max(0, x - t2);
  var x3 = Math.pow(x3a,3) - Math.pow(x2b,3)*(t5-t2)/(t5-t4) + Math.pow(x2c,3)*(t4-t2)/(t5-t4);
  var x4a = Math.max(0, x - t3);
  var x4 = Math.pow(x4a,3) - Math.pow(x2b,3)*(t5-t3)/(t5-t4) + Math.pow(x2c,3)*(t4-t3)/(t5-t4);
  var x2s = x2/100, x3s = x3/100, x4s = x4/100;

  var y2a = Math.max(0, y - ta1); var y2b = Math.max(0, y - ta4); var y2c = Math.max(0, y - ta5);
  var y2 = Math.pow(y2a,3) - Math.pow(y2b,3)*(ta5-ta1)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta1)/(ta5-ta4);
  var y3a = Math.max(0, y - ta2);
  var y3 = Math.pow(y3a,3) - Math.pow(y2b,3)*(ta5-ta2)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta2)/(ta5-ta4);
  var y4a = Math.max(0, y - ta3);
  var y4 = Math.pow(y4a,3) - Math.pow(y2b,3)*(ta5-ta3)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta3)/(ta5-ta4);
  var y2s = y2/100, y3s = y3/100, y4s = y4/100;

  var w2a = Math.max(0, w - tb1); var w2b = Math.max(0, w - tb4); var w2c = Math.max(0, w - tb5);
  var w2 = Math.pow(w2a,3) - Math.pow(w2b,3)*(tb5-tb1)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb1)/(tb5-tb4);
  var w3a = Math.max(0, w - tb2);
  var w3 = Math.pow(w3a,3) - Math.pow(w2b,3)*(tb5-tb2)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb2)/(tb5-tb4);
  var w4a = Math.max(0, w - tb3);
  var w4 = Math.pow(w4a,3) - Math.pow(w2b,3)*(tb5-tb3)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb3)/(tb5-tb4);
  var w2s = w2/10000, w3s = w3/10000, w4s = w4/10000;

  var coeff = reference.coefficients[sex === 'female' ? 'female' : 'male'][bpType === 'sys' ? 'sys' : 'dia'];
  var predicted = new Array(99);
  for (var i = 0; i < 99; i++) {
    predicted[i] = coeff[i][0] + coeff[i][5]*x + coeff[i][6]*x2s + coeff[i][7]*x3s + coeff[i][8]*x4s
      + coeff[i][1]*y + coeff[i][2]*y2s + coeff[i][3]*y3s + coeff[i][4]*y4s
      + coeff[i][9]*w + coeff[i][10]*w2s + coeff[i][11]*w3s + coeff[i][12]*w4s;
  }

  var minDiff = Infinity, percentile = 50;
  for (var j = 0; j < 99; j++) {
    var diff = Math.abs(bpValue - predicted[j]);
    if (diff < minDiff) { minDiff = diff; percentile = j + 1; }
  }
  return { percentile: percentile, predicted: predicted };
}

export function classifyBPFromPercentiles(age, sysPctile, diaPctile, sys, dia) {
  var sysClass, diaClass;
  if (age >= 13) {
    if (sys >= 140) sysClass = 'stage2';
    else if (sys >= 130) sysClass = 'stage1';
    else if (sys >= 120) sysClass = 'elevated';
    else sysClass = 'normal';

    if (dia >= 90) diaClass = 'stage2';
    else if (dia >= 80) diaClass = 'stage1';
    else diaClass = 'normal';
  } else {
    if (sysPctile >= 95 + 12 || sys >= 140) sysClass = 'stage2';
    else if (sysPctile >= 95 || sys >= 130) sysClass = 'stage1';
    else if (sysPctile >= 90 || sys >= 120) sysClass = 'elevated';
    else sysClass = 'normal';

    if (diaPctile >= 95 + 12 || dia >= 90) diaClass = 'stage2';
    else if (diaPctile >= 95 || dia >= 80) diaClass = 'stage1';
    else if (diaPctile >= 90) diaClass = 'elevated';
    else diaClass = 'normal';
  }

  var levels = { normal: 0, elevated: 1, stage1: 2, stage2: 3 };
  var overall = levels[sysClass] >= levels[diaClass] ? sysClass : diaClass;
  return { sysClass: sysClass, diaClass: diaClass, classification: overall };
}

export function calculateBPAssessment(reference, age, sex, heightCm, sys, dia) {
  var height = calcHeightPercentile(reference, age, sex, heightCm);
  var systolic = computeBPPercentile(reference, age, sex, heightCm, 'sys', sys);
  var diastolic = computeBPPercentile(reference, age, sex, heightCm, 'dia', dia);
  var classification = classifyBPFromPercentiles(age, systolic.percentile, diastolic.percentile, sys, dia);
  return { height: height, systolic: systolic, diastolic: diastolic, classification: classification };
}
