import { parse } from 'date-fns'
import {
  IBlock,
  IExercise,
  IRating,
  TTraining,
  TExerciseInstance,
  TBlockInstance,
  TBlock,
  TExercise
} from './types'
import {
  TrainingQuery,
  SubBlockFragment,
  BlockContentFragment,
  Training,
  ExerciseContentFragment
} from '../gql'
import { isArray, transform, isEqual, isObject } from 'lodash'

/**
 * Takes a block of exercises and calculates the duration in seconds.
 * @param block
 */
export function calculateDuration(block: IBlock): number {
  if (block.duration) return block.duration
  const repetitions = block.repetitions || 1
  const rest = block.rest || 0
  if (block.blocks) {
    const subblockDuration = block.blocks.reduce(
      (accumulator, block) =>
        accumulator + (block.duration || calculateDuration(block)),
      0
    )
    return repetitions * (subblockDuration + rest)
  } else {
    return 0
  }
}

/**
 * Formats a time in seconds into 0:00
 * @param seconds
 */
export function formatTime(seconds: number) {
  return `${Math.floor(seconds / 60)}:${(seconds % 60)
    .toString()
    .padStart(2, '0')}`
}

/**
 * Takes an array of exercises and formats them into
 * 4x Exercise 1 - Exercise 2 - 2x Exercise 3
 * @param exercises
 */
export function printExercises(exercises: IExercise[]) {
  return exercises
    .map(exercise =>
      exercise.repetitions > 1
        ? `${exercise.repetitions}x ${exercise.name}`
        : exercise.name
    )
    .join(' - ')
}

/**
 * Takes an array of rating and calculates the average rating
 * @param ratings
 */
export function calculateRating(ratings: IRating[]) {
  const numberOfRatings = ratings.length
  const sumOfRatings = ratings.reduce(
    (accumulator, rating) => accumulator + rating.value,
    0
  )
  return numberOfRatings ? sumOfRatings / numberOfRatings : '-'
}

export function trainingDBToArray(DBTraining: TrainingQuery['training']) {
  console.log({ DBTraining })
  if (!DBTraining) return undefined
  const { blocks, ...data } = DBTraining
  return { ...data, blocks: blockDBToArray(blocks) }
}

export function trainingArrayToDB() {}

export function blockDBToArray(DBSubBlock?: SubBlockFragment[]) {
  console.log({ DBSubBlock })
  if (!DBSubBlock) return undefined
  return DBSubBlock
}

export function blockArrayToDB() {}

const Weekdays = {
  Monday: 1,
  Tuesday: 2,
  Wednesday: 3,
  Thursday: 4,
  Friday: 5,
  Saturday: 6,
  Sunday: 0
}
type TWeekdays = keyof typeof Weekdays
/**
 * Get the date of the next weekday.
 * @param weekDay Sunday=0, Monday=1, Tuesday=2
 * @param time Format: HH:mm
 */
export function nextTime(weekDay: TWeekdays, time: string) {
  const nextTime = parse(time, 'HH:mm', new Date())
  nextTime.setDate(
    nextTime.getDate() + ((Weekdays[weekDay] + 7 - nextTime.getDay()) % 7)
  )
  return nextTime.toISOString()
}

function randomID() {
  return `++${Math.random()
    .toString(36)
    .replace(/[^a-zA-Z0-9]+/g, '')
    .substr(0, 10)}`
}

export function emptyExercise(input?: TExercise) {
  const emptyExercise = {
    id: randomID(),
    name: '',
    description: '',
    videos: [],
    pictures: [],
    targets: [],
    baseExercise: []
  }
  return { ...emptyExercise, ...input }
}

export function emptyExerciseInstance(input?: TExerciseInstance) {
  const emptyExerciseInstance = {
    id: randomID(),
    order: 0,
    exercise: emptyExercise()
  }
  return { ...emptyExerciseInstance, ...input }
}

export function emptyBlock(input?: TBlock) {
  const emptyBlock = {
    id: randomID(),
    title: '',
    format: { id: '', name: '', description: '' },
    videos: [],
    pictures: [],
    blocks: [],
    exercises: []
  }
  return { ...emptyBlock, ...input }
}

export function emptyBlockInstance(input?: TBlockInstance) {
  const emptyBlockInstance = {
    id: randomID(),
    block: emptyBlock(),
    order: 0
  }
  return { ...emptyBlockInstance, ...input }
}

export function emptyTraining(input?: TTraining) {
  const emptyTraining = {
    id: randomID(),
    title: '',
    type: { id: '', name: '', description: '' },
    createdAt: '',
    trainingDate: nextTime('Tuesday', '11:45'),
    published: false,
    blocks: []
  }
  return { ...emptyTraining, ...input }
}

export function collectMutationCreateConnect(arr: any[]) {
  const create: any[] = []
  const connect: any[] = []
  const del: any[] = []
  const update: any[] = []
  arr.forEach(val => {
    if (typeof val === 'object' && val['connect']) connect.push(val['connect'])
    if (typeof val === 'object' && val['create']) create.push(val['create'])
    if (typeof val === 'object' && val['delete']) del.push(val['delete'])
    if (typeof val === 'object' && val['update']) update.push(val['update'])
  })
  const returnObject: {
    connect?: any
    delete?: any
    create?: any
    update?: any
  } = {}
  if (connect.length > 0) returnObject.connect = connect
  if (del.length > 0) returnObject.delete = del
  if (update.length > 0) returnObject.update = update
  if (create.length > 0) returnObject.create = create
  return returnObject
}

export function transformArrayToDB(acc: any, val: any, key: any, object: any) {
  if (key === '__typename') {
    // remove the typename from the database
    return
  } else if (key === 'id' && typeof val === 'string' && val.startsWith('++')) {
    // remove placeholder IDs
    return
  } else if (isArray(val)) {
    // collect 'create' and 'connect' statements
    acc[key] = collectMutationCreateConnect(transform(val, transformArrayToDB))
  } else if (typeof val === 'object' && !!val) {
    // we found an object!
    if (!!val['id'] && val['id'].startsWith('++')) {
      // values with placeholder IDs are preserved
      acc[key] = { create: transform(val, transformArrayToDB) }
    } else if (!!val['id'] && val['id'].startsWith('--')) {
      // IDs starting with -- are for deletion
      acc[key] = { delete: { id: val['id'].substr(2) } }
    } else {
      // values with real IDs are just connected
      const { id, ...data } = val
      if (id?.startsWith('@@')) {
        if (typeof key === 'string') {
          acc[key] = { update: transform(data, transformArrayToDB) }
        } else {
          acc[key] = {
            update: {
              where: { id: id.substr(2) },
              data: transform(data, transformArrayToDB)
            }
          }
        }
        console.log('update candidate', key, id, data, acc[key].update.data)
      } else {
        acc[key] = { connect: { id } }
      }
    }
  } else {
    // copy the value
    acc[key] = val
  }
}

export function diffDB(newObject: any = {}, oldObject: any = {}) {
  const transformResult = transform(
    newObject,
    (result: any, value: any, key: string) => {
      if (key === 'id') {
        if (isEqual(value, oldObject[key])) result[key] = `@@${value}`
      } else if (!isEqual(value, oldObject[key])) {
        const newValue =
          isObject(value) && isObject(oldObject[key])
            ? diffDB(value, oldObject[key])
            : value
        if (newValue !== undefined && newValue !== null) {
          result[key] = newValue
        }
      }
    }
  )
  return Object.keys(transformResult).length > 0 ? transformResult : undefined
}
