import moment from 'common/moment'
import {
  array_from_hash, for_all, find_optimum_in_sorted_array, flatten_array, split_array,
} from 'common/utilities'
import {mission_period} from 'common/missions'
import store from 'common/store'
import {show_popover} from 'actions/display'
import AgencyEditUserCalendar from 'components/input_calendar/agency_edit_user_calendar'

export function event_intersects(event, date, duration, unit) {
  return moment(event.start).isBefore(moment(date).add(duration, unit)) && moment(event.end).isAfter(date)
}

export function event_intersects_or_consecutive(event1, event2, unit) {
  return !(moment(event1.end).add(1, unit).isSameOrBefore(event2.start, unit) || moment(event2.end).add(1, unit).isSameOrBefore(event1.start, unit))
}

const make_period_from_event = (e) => ({is_period: true, start: moment(e.start), end: moment(e.end), events: [e], full_days: e.full_days})

export function intersecting_periods_interval(periods, start, end) {
  let lower = find_optimum_in_sorted_array(periods, (period) => period.end.isAfter(start)) + 1
  let upper = periods.length - 1 - find_optimum_in_sorted_array(periods.reverse(), (period) => period.start.isBefore(end))
  periods.reverse()
  return {lower, upper}
}

export const make_periods = (events) => {
  let events_array = array_from_hash(events)
  if (events_array.length == 0) {
    return []
  } else {
    return events_array.slice(1).reduce((periods, e) => {
      let extended_start = moment(e.start).subtract(1, "minute")
      let extended_end = moment(e.end).add(1, "minute")
      let {lower, upper} = intersecting_periods_interval(periods, extended_start, extended_end)
      let merging_periods = periods.slice(lower, upper)
      return [
        ...periods.slice(0, lower),
        {
          is_period: true,
          start: merging_periods.length == 0 || merging_periods[0].start.isAfter(e.start) ? moment(e.start) : merging_periods[0].start,
          end: merging_periods.length == 0 || merging_periods.slice(-1)[0].end.isBefore(e.end) ? moment(e.end) : merging_periods.slice(-1)[0].end,
          full_days: e.full_days && for_all(merging_periods, (period) => period.full_days),
          events: [...flatten_array(merging_periods.map((period) => period.events)), e],
        },
        ...periods.slice(upper)
      ]
    }, [make_period_from_event(events_array[0])])
  }
}

export const make_slots = (periods) => flatten_array(
  periods.map(({start, end, ...period}) => {
    let current = moment(start).add(1, 'day').startOf('day'), previous = start
    const periods = []
    while (current.isBefore(end)) {
      periods.push({start: previous, end: current, ...period})
      previous = current
      current = moment(current).add(1, 'day')
    }
    periods.push({start: previous, end, ...period})
    return periods
  })
)

function event_is_included_in_periods(event, periods) {
  let {lower, upper} = intersecting_periods_interval(periods, event.start, event.end)
  return lower == upper - 1 && periods[lower] && periods[lower].start.isSameOrBefore(event.start) && periods[lower].end.isSameOrAfter(event.end)
}

export function events_difference(old_events, new_events) {
  let new_periods = make_periods(new_events)
  let {remove, keep} = split_array(old_events, (event) => {
    if (event_is_included_in_periods(event, new_periods)) {
      return "keep"
    } else {
      return "remove"
    }
  })
  let old_periods = make_periods(keep)
  return {remove, add: new_events.filter((event) => {
    return !event_is_included_in_periods(event, old_periods)
  })}
}

export function action_agency_edit_user_events({id: user_id}) {
  store.dispatch(show_popover(
    AgencyEditUserCalendar,
    {user_id}
  ))
}
