'use strict';

/**
 * Loop SGV Entry and DeviceStatus Upload Tests
 * 
 * Tests Loop-specific entry and deviceStatus upload patterns.
 * 
 * TEST MATRIX REFERENCE: docs/backlogs/loop-nightscout-upload-testing.md
 * - TEST-SGV-001 to TEST-SGV-005: Glucose entry tests
 * - TEST-DS-001 to TEST-DS-005: DeviceStatus tests
 */

const request = require('supertest');
const should = require('should');
const language = require('../lib/language')();

describe('Loop SGV Entry Upload Tests', function() {
  this.timeout(30000);
  const self = this;
  
  const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1';

  before(function(done) {
    process.env.API_SECRET = 'this is my long pass phrase';
    self.env = require('../lib/server/env')();
    self.env.settings.authDefaultRoles = 'readable';
    self.env.settings.enable = ['careportal', 'api'];
    const wares = require('../lib/middleware/')(self.env);
    self.app = require('express')();
    self.app.enable('api');
    require('../lib/server/bootevent')(self.env, language).boot(function booted(ctx) {
      self.ctx = ctx;
      self.ctx.wares = wares;
      self.ctx.ddata = require('../lib/data/ddata')();
      self.app.use('/api', require('../lib/api/')(self.env, ctx));
      done();
    });
  });

  beforeEach(function(done) {
    self.ctx.entries().deleteMany({}, done);
  });

  describe('TEST-SGV-001: Single SGV entry', function() {

    it('creates SGV with required fields', function(done) {
      const now = Date.now();
      
      const sgv = {
        type: 'sgv',
        sgv: 120,
        date: now,
        dateString: new Date(now).toISOString(),
        direction: 'Flat'
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([sgv])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          res.body.length.should.equal(1);
          
          const created = res.body[0];
          created.sgv.should.equal(120);
          created.direction.should.equal('Flat');
          created._id.should.match(/^[0-9a-f]{24}$/);
          
          console.log('      ✓ Single SGV entry created');
          done();
        });
    });

    it('handles all direction values', function(done) {
      const directions = ['DoubleUp', 'SingleUp', 'FortyFiveUp', 'Flat', 
                          'FortyFiveDown', 'SingleDown', 'DoubleDown', 'NOT COMPUTABLE'];
      
      const entries = directions.map((dir, idx) => ({
        type: 'sgv',
        sgv: 100 + idx * 10,
        date: Date.now() + idx * 300000,
        dateString: new Date(Date.now() + idx * 300000).toISOString(),
        direction: dir
      }));
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send(entries)
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          res.body.length.should.equal(directions.length);
          
          console.log('      ✓ All direction values accepted');
          done();
        });
    });
  });

  describe('TEST-SGV-004: SGV with device field', function() {

    it('preserves Loop device identifier', function(done) {
      const sgv = {
        type: 'sgv',
        sgv: 115,
        date: Date.now(),
        dateString: new Date().toISOString(),
        direction: 'FortyFiveUp',
        device: 'loop://iPhone'
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([sgv])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          res.body[0].device.should.equal('loop://iPhone');
          
          console.log('      ✓ Loop device field preserved');
          done();
        });
    });

    it('preserves Dexcom device identifier', function(done) {
      const sgv = {
        type: 'sgv',
        sgv: 125,
        date: Date.now(),
        dateString: new Date().toISOString(),
        direction: 'Flat',
        device: 'share2',
        filtered: 150000,
        unfiltered: 155000,
        noise: 1
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([sgv])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          res.body[0].device.should.equal('share2');
          res.body[0].noise.should.equal(1);
          
          console.log('      ✓ Dexcom device with filtered/unfiltered/noise');
          done();
        });
    });
  });

  describe('TEST-SGV-005: SGV deduplication', function() {

    it('duplicate date+device does not create second entry', function(done) {
      const timestamp = Date.now();
      const device = 'loop://iPhone';
      
      const first = {
        type: 'sgv',
        sgv: 120,
        date: timestamp,
        dateString: new Date(timestamp).toISOString(),
        direction: 'Flat',
        device: device
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([first])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          // Post duplicate
          const duplicate = {
            type: 'sgv',
            sgv: 125,  // Different value
            date: timestamp,  // Same timestamp
            dateString: new Date(timestamp).toISOString(),
            direction: 'FortyFiveUp',
            device: device  // Same device
          };
          
          request(self.app)
            .post('/api/entries/')
            .set('api-secret', api_secret_hash)
            .set('Accept', 'application/json')
            .send([duplicate])
            .end(function(err) {
              should.not.exist(err);
              
              // Check database
              self.ctx.entries.list({}, function(err, list) {
                should.not.exist(err);
                list.length.should.equal(1, 'Duplicate should not create second entry');
                
                console.log('      ✓ SGV deduplication by date+device');
                done();
              });
            });
        });
    });

    it('different devices create separate entries', function(done) {
      const timestamp = Date.now();
      
      const entry1 = {
        type: 'sgv',
        sgv: 120,
        date: timestamp,
        dateString: new Date(timestamp).toISOString(),
        direction: 'Flat',
        device: 'loop://iPhone'
      };
      
      const entry2 = {
        type: 'sgv',
        sgv: 122,
        date: timestamp + 1,  // Slightly different timestamp to avoid collision
        dateString: new Date(timestamp + 1).toISOString(),
        direction: 'Flat',
        device: 'xDrip+'
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([entry1])
        .expect(200)
        .end(function(err) {
          should.not.exist(err);
          
          request(self.app)
            .post('/api/entries/')
            .set('api-secret', api_secret_hash)
            .set('Accept', 'application/json')
            .send([entry2])
            .expect(200)
            .end(function(err, res) {
              should.not.exist(err);
              res.body.length.should.equal(1);
              res.body[0].device.should.equal('xDrip+');
              
              console.log('      ✓ Different devices create separate entries');
              done();
            });
        });
    });
  });

  describe('MBG (Manual BG Check) Entries', function() {

    it('creates manual BG check entry', function(done) {
      const mbg = {
        type: 'mbg',
        mbg: 110,
        date: Date.now(),
        dateString: new Date().toISOString(),
        device: 'loop://iPhone'
      };
      
      request(self.app)
        .post('/api/entries/')
        .set('api-secret', api_secret_hash)
        .set('Accept', 'application/json')
        .send([mbg])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          res.body[0].type.should.equal('mbg');
          res.body[0].mbg.should.equal(110);
          
          console.log('      ✓ MBG entry created');
          done();
        });
    });
  });
});

describe('Loop DeviceStatus Upload Tests', function() {
  this.timeout(30000);
  const self = this;
  
  const api_secret_hash = 'b723e97aa97846eb92d5264f084b2823f57c4aa1';

  before(function(done) {
    process.env.API_SECRET = 'this is my long pass phrase';
    self.env = require('../lib/server/env')();
    self.env.settings.authDefaultRoles = 'readable';
    self.env.settings.enable = ['careportal', 'api'];
    const wares = require('../lib/middleware/')(self.env);
    self.app = require('express')();
    self.app.enable('api');
    require('../lib/server/bootevent')(self.env, language).boot(function booted(ctx) {
      self.ctx = ctx;
      self.ctx.wares = wares;
      self.ctx.ddata = require('../lib/data/ddata')();
      self.app.use('/api', require('../lib/api/')(self.env, ctx));
      done();
    });
  });

  beforeEach(function(done) {
    self.ctx.devicestatus.remove({
      find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } }
    }, done);
  });

  describe('TEST-DS-001: Loop status with IOB/COB', function() {

    it('creates deviceStatus with loop IOB and COB', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          iob: {
            iob: 2.5,
            timestamp: new Date().toISOString()
          },
          cob: {
            cob: 15,
            timestamp: new Date().toISOString()
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          res.body.length.should.equal(1);
          
          const created = res.body[0];
          created.loop.iob.iob.should.equal(2.5);
          created.loop.cob.cob.should.equal(15);
          
          console.log('      ✓ Loop deviceStatus with IOB/COB');
          done();
        });
    });
  });

  describe('TEST-DS-002: Loop status with predicted', function() {

    it('creates deviceStatus with prediction values array', function(done) {
      const predictions = [120, 118, 115, 112, 110, 108, 105, 102, 100];
      
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          predicted: {
            startDate: new Date().toISOString(),
            values: predictions
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.loop.predicted.values.should.be.an.Array();
          created.loop.predicted.values.length.should.equal(predictions.length);
          
          console.log('      ✓ Loop predicted values array preserved');
          done();
        });
    });
  });

  describe('TEST-DS-003: Loop status with enacted', function() {

    it('creates deviceStatus with enacted temp basal', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          enacted: {
            timestamp: new Date().toISOString(),
            rate: 1.5,
            duration: 30,
            received: true
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.loop.enacted.rate.should.equal(1.5);
          created.loop.enacted.duration.should.equal(30);
          created.loop.enacted.received.should.equal(true);
          
          console.log('      ✓ Loop enacted temp basal');
          done();
        });
    });
  });

  describe('TEST-DS-004: Pump status', function() {

    it('creates deviceStatus with pump reservoir and battery', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        pump: {
          clock: new Date().toISOString(),
          reservoir: 150.5,
          battery: {
            percent: 75
          },
          status: {
            status: 'normal',
            timestamp: new Date().toISOString()
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.pump.reservoir.should.equal(150.5);
          created.pump.battery.percent.should.equal(75);
          
          console.log('      ✓ Pump reservoir and battery');
          done();
        });
    });

    it('handles Omnipod specific fields', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        pump: {
          clock: new Date().toISOString(),
          reservoir: 50,
          reservoir_display_override: '50+ U',
          pumpID: 'pod-12345',
          manufacturer: 'Insulet',
          model: 'Eros'
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.pump.manufacturer.should.equal('Insulet');
          created.pump.model.should.equal('Eros');
          
          console.log('      ✓ Omnipod specific fields');
          done();
        });
    });
  });

  describe('TEST-DS-005: Override in deviceStatus', function() {

    it('creates deviceStatus with active override', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          override: {
            active: true,
            timestamp: new Date().toISOString(),
            name: 'Pre-Meal',
            currentCorrectionRange: {
              minValue: 80,
              maxValue: 80
            },
            multiplier: 1.0
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.loop.override.active.should.equal(true);
          created.loop.override.name.should.equal('Pre-Meal');
          created.loop.override.currentCorrectionRange.minValue.should.equal(80);
          
          console.log('      ✓ Override in deviceStatus');
          done();
        });
    });

    it('handles override with insulinNeedsScaleFactor', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          override: {
            active: true,
            timestamp: new Date().toISOString(),
            name: 'Workout',
            currentCorrectionRange: {
              minValue: 140,
              maxValue: 160
            },
            multiplier: 0.5  // 50% basal
          }
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          created.loop.override.multiplier.should.equal(0.5);
          
          console.log('      ✓ Override with insulinNeedsScaleFactor');
          done();
        });
    });
  });

  describe('Full Loop DeviceStatus', function() {

    it('creates complete Loop deviceStatus with all fields', function(done) {
      const ds = {
        device: 'loop://iPhone',
        created_at: new Date().toISOString(),
        uploaderBattery: 85,
        loop: {
          version: '3.0.0',
          timestamp: new Date().toISOString(),
          name: 'Loop',
          iob: {
            iob: 2.5,
            timestamp: new Date().toISOString()
          },
          cob: {
            cob: 15,
            timestamp: new Date().toISOString()
          },
          predicted: {
            startDate: new Date().toISOString(),
            values: [120, 118, 115, 112, 110]
          },
          enacted: {
            timestamp: new Date().toISOString(),
            rate: 1.2,
            duration: 30,
            received: true
          },
          recommendedBolus: 0,
          failureReason: null
        },
        pump: {
          clock: new Date().toISOString(),
          reservoir: 150,
          battery: { percent: 75 }
        },
        uploader: {
          battery: 85
        }
      };
      
      request(self.app)
        .post('/api/devicestatus/')
        .set('api-secret', api_secret_hash)
        .send([ds])
        .expect(200)
        .end(function(err, res) {
          should.not.exist(err);
          
          const created = res.body[0];
          
          // Verify all major sections
          should.exist(created.loop);
          should.exist(created.pump);
          should.exist(created.loop.iob);
          should.exist(created.loop.cob);
          should.exist(created.loop.predicted);
          should.exist(created.loop.enacted);
          
          console.log('      ✓ Complete Loop deviceStatus');
          done();
        });
    });
  });
});
