import moment from 'moment-timezone'
import { Domain, getDataSource } from '@things-factory/shell'
import { Appliance, User } from '@things-factory/auth-base'
import { DataSensor, createDataSample } from '@things-factory/dataset'
import { InverterStatus } from '@esome-dev/inverter'

const DATE_INFO_FORMAT = 'YYYY-MM-DD HH:mm:ss'

process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
  /*
   * can add global public routes to application (auth not required, tenancy not required)
   *
   * ex) routes.get('/path', async(context, next) => {})
   * ex) routes.post('/path', async(context, next) => {})
   */
  globalPublicRouter.post('/monitoring/rtu/sensor/setup', async (context, next) => {
    const data = context.request.body
    const { MFT_RTU_CD, DATE_INFO, RTU_IP } = data

    if (!MFT_RTU_CD) {
      throw new Error(`MFT_RTU_CD(${MFT_RTU_CD}) properties are mandatory`)
    }

    await getDataSource().transaction(async tx => {
      const appliance: Appliance = await tx.getRepository(Appliance).findOne({
        where: { name: MFT_RTU_CD },
        relations: ['domain']
      })

      if (!appliance) {
        throw new Error(`RTU having given MFT_RTU_CD(${MFT_RTU_CD}) not found`)
      }

      const domain = appliance.domain
      const updatedAt = moment.tz(DATE_INFO, DATE_INFO_FORMAT, domain.timezone || 'Asia/Seoul').toDate()

      if (appliance) {
        await tx.getRepository(Appliance).save({
          ...appliance,
          netmask: RTU_IP,
          updatedAt
        })
      }
    })

    context.status = 200
  })

  globalPublicRouter.post('/monitoring/rtu/sensor/inverter', async (context, next) => {
    const data = context.request.body
    const { MFT_RTU_CD, RTU_IVT_CD, DATE_INFO } = data
    const sensorId = `${MFT_RTU_CD || ''}-${RTU_IVT_CD || ''}`

    if (!MFT_RTU_CD || !RTU_IVT_CD) {
      throw new Error(`MFT_RTU_CD(${MFT_RTU_CD}) and INV-ID(${RTU_IVT_CD}) properties are mandatory`)
    }

    await getDataSource().transaction(async tx => {
      const sensor = await tx.getRepository(DataSensor).findOne({
        where: { deviceId: sensorId },
        relations: ['domain', 'appliance', 'dataSet']
      })

      if (!sensor) {
        throw new Error(`Sensor having given sensorId(${sensorId}) not found`)
      }

      if (!sensor.active) {
        throw new Error(`State of the sensor given sensorId(${sensorId}) is not active`)
      }

      if (!sensor.appliance) {
        throw new Error(`Appliance of the sensor given sensorId(${sensorId}) is not set up`)
      }

      if (!sensor.dataSet) {
        throw new Error(`DataSet of the sensor given sensorId(${sensorId}) is not set up`)
      }

      const domain = sensor.domain as Domain
      const dataSet = sensor.dataSet
      const user: User = await tx.getRepository(User).findOne({
        where: {
          reference: sensor.appliance.id,
          userType: 'appliance'
        }
      })

      context.state = {
        ...context.state,
        domain,
        tx
      }

      const collectedAt = moment.tz(DATE_INFO, DATE_INFO_FORMAT, domain.timezone || 'Asia/Seoul').toDate()

      await createDataSample(
        {
          dataSet,
          data,
          rawData: data,
          source: sensorId,
          collectedAt
        },
        context
      )

      const inverterStatus = await tx.getRepository(InverterStatus).findOne({
        where: {
          domain: { id: domain.id },
          rtuId: MFT_RTU_CD,
          ivtId: RTU_IVT_CD
        }
      })

      if (inverterStatus && inverterStatus.collectedAt > collectedAt) {
        return
      }

      const { ACC_P, AC_P, DC_P, DS, COM, RUN } = data

      await tx.getRepository('InverterStatus').save({
        ...inverterStatus,
        domain,
        rtuId: MFT_RTU_CD,
        ivtId: RTU_IVT_CD,
        acc: Number(ACC_P) || null,
        ac: Number(AC_P) || null,
        dc: Number(DC_P) || null,
        ds: Number(DS) || null,
        com: COM,
        run: RUN,
        collectedAt,
        updater: user
      })
    })

    context.status = 200
  })

  globalPublicRouter.post('/monitoring/rtu/sensor/weather', async (context, next) => {
    const data = context.request.body
    const { MFT_RTU_CD, RTU_CDS_CD, DATE_INFO } = data
    const sensorId = `${MFT_RTU_CD || ''}-${RTU_CDS_CD || ''}`

    if (!MFT_RTU_CD || !RTU_CDS_CD) {
      throw new Error(`MFT_RTU_CD(${MFT_RTU_CD}) and RTU_CDS_CD(${RTU_CDS_CD}) properties are mandatory`)
    }

    await getDataSource().transaction(async tx => {
      const sensor = await tx.getRepository(DataSensor).findOne({
        where: { deviceId: sensorId },
        relations: ['domain', 'appliance', 'dataSet']
      })

      if (!sensor) {
        throw new Error(`Sensor having given sensorId(${sensorId}) not found`)
      }

      if (!sensor.active) {
        throw new Error(`State of the sensor given sensorId(${sensorId}) is not active`)
      }

      if (!sensor.appliance) {
        throw new Error(`Appliance of the sensor given sensorId(${sensorId}) is not set up`)
      }

      if (!sensor.dataSet) {
        throw new Error(`DataSet of the sensor given sensorId(${sensorId}) is not set up`)
      }

      const domain = sensor.domain
      const dataSet = sensor.dataSet
      const user: User = await tx.getRepository(User).findOne({
        where: {
          reference: sensor.appliance.id,
          userType: 'appliance'
        }
      })

      context.state = {
        ...context.state,
        domain,
        tx
      }

      const collectedAt = moment.tz(DATE_INFO, DATE_INFO_FORMAT, domain.timezone || 'Asia/Seoul').toDate()

      return await createDataSample(
        {
          dataSet,
          data,
          rawData: data,
          source: sensorId,
          collectedAt
        },
        context
      )
    })

    context.status = 200
  })
})

process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
  /*
   * can add global private routes to application (auth required, tenancy not required)
   */
})

process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
  /*
   * can add domain public routes to application (auth not required, tenancy required)
   */
})

process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
  /*
   * can add domain private routes to application (auth required, tenancy required)
   */
})
