import { DateTime } from 'luxon'
import { FluidType } from 'enum/fluid.enum'
import { ITimePeriod, TimeStep, TimePeriod } from './dataConsumptionContracts'
import Config from '../../config.json'
import IFluidConfig from './IFluidConfig'

export default class ConsumptionPeriodSelector {
  private readonly _dataDelayOffsetConfig: IFluidConfig[]

  constructor() {
    this._dataDelayOffsetConfig = Config.fluidConfig
  }

  public getTimePeriods(
    date: DateTime,
    fluidTypes: FluidType[],
    timeStep: TimeStep
  ): any {
    const endOfOffsetDate = this.applyOffsetToDate(
      date,
      this.calculateFluidTypesOffset(fluidTypes)
    ).endOf('day')

    let lastDayOfCompletePeriod

    if (this.isLastDayOfTimePeriod(endOfOffsetDate, timeStep))
      lastDayOfCompletePeriod = endOfOffsetDate
    else
      lastDayOfCompletePeriod = this.getLastDayOfCompletePeriod(
        endOfOffsetDate,
        timeStep
      ).endOf('day')

    const timePeriod: ITimePeriod = this.getLastCompletePeriod(
      lastDayOfCompletePeriod,
      timeStep
    )
    const comparisonTimePeriod: ITimePeriod = this.getComparisonTimePeriod(
      timePeriod,
      timeStep
    )

    return {
      timePeriod: timePeriod,
      comparisonTimePeriod: comparisonTimePeriod,
    }
  }

  public calculateFluidTypesOffset(fluidTypes: FluidType[]): number {
    const allOffsets: number[] = []
    fluidTypes.forEach(fluidType =>
      allOffsets.push(this._dataDelayOffsetConfig[fluidType].dataDelayOffset)
    )

    // eslint-disable-next-line prefer-spread
    const maxOffset = Math.max.apply(Math, allOffsets)
    return maxOffset
  }

  public applyOffsetToDate(date: DateTime, offset: number): DateTime {
    return date.minus({ days: offset })
  }

  public isLastDayOfTimePeriod(date: DateTime, timeStep: TimeStep) {
    const lastDayOfTimePeriod = this.getLastDayOfTimePeriod(date, timeStep)

    return (
      date.year === lastDayOfTimePeriod.year &&
      date.month === lastDayOfTimePeriod.month &&
      date.day === lastDayOfTimePeriod.day
    )
  }

  public getLastDayOfTimePeriod(date: DateTime, timeStep: TimeStep): DateTime {
    switch (timeStep) {
      case TimeStep.HALF_AN_HOUR || TimeStep.HOUR:
        return DateTime.local(date.year, date.month, date.day)

      case TimeStep.DAY:
        return DateTime.local(date.year, date.month, date.day)
          .setLocale('fr-CA')
          .endOf('week')

      case TimeStep.MONTH:
        return DateTime.local(date.year, date.month, 1)
          .plus({ months: 1 })
          .minus({ days: 1 })

      case TimeStep.YEAR:
        return DateTime.local(date.year, 1, 1)
          .plus({ years: 1 })
          .minus({ days: 1 })

      default:
        return date
    }
  }

  public getLastDayOfCompletePeriod(date: DateTime, timeStep: TimeStep) {
    switch (timeStep) {
      case TimeStep.HALF_AN_HOUR || TimeStep.HOUR:
        return DateTime.local(date.year, date.month, date.day)

      case TimeStep.DAY:
        return DateTime.local(date.year, date.month, date.day)
          .setLocale('fr-CA')
          .startOf('week')
          .minus({ days: 1 })

      case TimeStep.MONTH:
        return DateTime.local(date.year, date.month, 1).minus({ days: 1 })

      case TimeStep.YEAR:
        return DateTime.local(date.year, 1, 1).minus({ days: 1 })

      default:
        return date
    }
  }

  public getStartDateFromEndDateByTimeStep(
    startDate: DateTime,
    timeStep: TimeStep
  ) {
    switch (timeStep) {
      case TimeStep.HALF_AN_HOUR || TimeStep.HOUR:
        return DateTime.local(startDate.year, startDate.month, startDate.day)

      case TimeStep.DAY:
        return DateTime.local(startDate.year, startDate.month, startDate.day)
          .setLocale('fr-CA')
          .startOf('week')

      case TimeStep.MONTH:
        return DateTime.local(startDate.year, startDate.month, 1)

      case TimeStep.YEAR:
        return DateTime.local(startDate.year, 1, 1)

      default:
        return startDate
    }
  }

  public getLastCompletePeriod(
    lastDay: DateTime,
    timeStep: TimeStep
  ): ITimePeriod {
    // calculate last day of the tobe coimpleted period

    const lastCompleteTimePeriod = {
      startDate: this.getStartDateFromEndDateByTimeStep(lastDay, timeStep),
      endDate: lastDay,
    }

    return lastCompleteTimePeriod
  }

  public getComparisonTimePeriod(
    timePeriod: ITimePeriod,
    timeStep: TimeStep
  ): ITimePeriod {
    let comparisonTimePeriodStartDate: DateTime
    let comparisonTimePeriodEndDate: DateTime

    switch (timeStep) {
      case TimeStep.HALF_AN_HOUR || TimeStep.HOUR:
        comparisonTimePeriodStartDate = timePeriod.startDate.minus({ days: 1 })
        comparisonTimePeriodEndDate = timePeriod.endDate.minus({ days: 1 })
        break

      case TimeStep.DAY:
        comparisonTimePeriodStartDate = timePeriod.startDate
          .minus({ days: 1 })
          .setLocale('fr-CA')
          .startOf('week')
        comparisonTimePeriodEndDate = timePeriod.startDate.minus({ days: 1 })
        break

      case TimeStep.MONTH:
        comparisonTimePeriodStartDate = DateTime.local(
          timePeriod.startDate.year - 1,
          timePeriod.startDate.month,
          timePeriod.startDate.day
        )
        comparisonTimePeriodEndDate = DateTime.local(
          timePeriod.endDate.year - 1,
          timePeriod.endDate.month,
          1
        ).endOf('month')
        break

      case TimeStep.YEAR:
        comparisonTimePeriodStartDate = DateTime.local(
          timePeriod.startDate.year - 1,
          timePeriod.startDate.month,
          timePeriod.startDate.day
        )
        comparisonTimePeriodEndDate = DateTime.local(
          timePeriod.endDate.year - 1,
          timePeriod.endDate.month,
          timePeriod.endDate.day
        ).endOf('year')
        break

      default:
        comparisonTimePeriodStartDate = timePeriod.startDate
        comparisonTimePeriodEndDate = timePeriod.endDate
    }

    const comparisonTimePeriod: ITimePeriod = {
      startDate: comparisonTimePeriodStartDate,
      endDate: comparisonTimePeriodEndDate,
    }
    return comparisonTimePeriod
  }
}