import {tolerant_selector} from 'selectors/base'
import {date_to_id} from 'selectors/calendar'
import {get_users} from 'selectors/users'
import {get_recruiters} from 'selectors/recruiters'
import {
  get_fields, get_fields_by_mission, get_mission_fields_by_deal, get_fields_by_proposal,
  get_mission_values_by_mission, get_values_by_field, get_fields_by_deal,
} from 'selectors/fields'
import moment from 'common/moment'
import {
  make_memoized, array_from_hash, array_from_set, split_hash, filter_hash, map_hash,
  group_hash_by_value, for_all, flatten_array, split_array, set_from_array, property_getter,
  set_from_hash, array_sum, array_minimum, array_maximum, set_intersection, filter_set,
  create_hash, exist,
} from 'common/utilities'
import {
  action_update_mission, action_cancel_mission, action_cancel_deal,
  action_send_mission, action_assign_mission, action_unassign_mission, action_duplicate_deal,
  action_add_mission, action_customize_deal_fields, deal_has_custom_fields, action_export_users,
  action_toggle_publish_mission, action_notify_updated_mission, action_cancel_notify_updated_mission,
  action_update_mission_events,
} from 'common/missions'
import {action_create_workplace} from 'common/workplaces'
import {make_periods} from 'common/calendar'

const get_raw_deals = (state) => state.missions.deals
const get_raw_missions = (state) => state.missions.missions
const get_raw_events = (state) => state.missions.events
const get_raw_mission_users = (state) => state.missions.users
const get_raw_workplaces = (state) => state.missions.workplaces
const get_raw_users = (state) => state.users.users
const get_profile_id = (state) => state.profile.id
const get_raw_agency = (state) => state.profile.agency
const get_raw_values = (state) => state.fields.values
const get_raw_fields = (state) => state.fields.fields

export const get_missions = tolerant_selector(
  [
    get_raw_missions, get_raw_mission_users, get_raw_events, get_raw_workplaces,
    get_users, get_fields, get_fields_by_mission, get_raw_agency,
  ],
  (missions, mission_users, events, workplaces, users, fields, fields_by_mission, {workplace_shared_with_user}) => {
    let day_events_by_mission = {}
    let hour_events_by_mission = {}
    let not_full_days = {}
    let start = {}
    let end = {}
    array_from_hash(events).forEach((e) => {
      if (!e.deleted_at) {
        let events_by_mission
        if (e.full_days) {
          events_by_mission = day_events_by_mission
        } else {
          not_full_days[e.calenderable_id] = true
          events_by_mission = hour_events_by_mission
        }
        events_by_mission[e.calenderable_id] = [
          ...(events_by_mission[e.calenderable_id] || []),
          e
        ]
        if (!start[e.calenderable_id] || moment(e.start).isBefore(start[e.calenderable_id])) {
          start[e.calenderable_id] = e.start
        }
        if (!end[e.calenderable_id] || moment(e.end).isAfter(end[e.calenderable_id])) {
          end[e.calenderable_id] = e.end
        }
      }
    })
    const allowed_users_statuses = set_from_array(["active", "mail", "pending"])
    return map_hash(missions, (mission) => {
      const {id, users_count, workplace_id, recruiter_id} = mission
      const users_status = mission_users[id] || {}
      const grouped_users_status = group_hash_by_value(
        users_status,
        ['new', 'accepted', 'rejected', 'assigned', 'impossible']
      )
      grouped_users_status.available = Object.keys(users)
      .filter((user_id) => !users_status[user_id])
      .filter((user_id) => users[user_id] && allowed_users_statuses[users[user_id].status.status])
      const status = mission_status({...mission, grouped_users_status})
      const assigned_count = grouped_users_status.assigned.length
      const accepted_count = grouped_users_status.accepted.length
      const day_events = day_events_by_mission[id] || []
      const hour_events = hour_events_by_mission[id] || []
      const fields = array_from_hash(fields_by_mission[id] || {}).map((field) => {
        const {shared_with_user, } = field
        if (shared_with_user) {
          return {
            ...field,
            ...create_hash([
              "on_select_value",
              "on_deselect_value",
              "on_clear",
              "on_submit_comment",
            ], (action) => (...args) => {
              action_notify_updated_mission(mission.id)
              return field[action](...args)
            })
          }
        }
        return field
      })
      mission = {
        ...mission,
        status,
        sent: status != 'draft',
        day_events,
        hour_events,
        events: [...day_events, ...hour_events],
        grouped_users_status,
        users_status,
        workplace: workplaces[workplace_id] || {},
        full_days: !not_full_days[id],
        start: start[id],
        end: end[id],
        assigned_count,
        accepted_count,
        empty_count: mission.users_count - assigned_count,
        waiting_count: grouped_users_status.new.length,
        fields,
      }
      return {
        ...mission,
        update(new_mission) {
          action_update_mission(mission, {id, ...new_mission})
        },
        cancel() {
          action_cancel_mission(mission)
        },
        create_workplace(workplace_name) {
          return action_create_workplace(workplace_name)
        },
        set_workplace(workplace_id) {
          action_notify_updated_mission(mission.id)
          action_update_mission(mission, {id, workplace_id})
        },
        update_events(events) {
          action_notify_updated_mission(mission.id)
          return action_update_mission_events(mission.id, events)
        },
        send(user_ids) {
          return action_send_mission(mission, user_ids)
        },
        assign(user_ids) {
          return action_assign_mission(mission, user_ids)
        },
        unassign(user_ids) {
          return action_unassign_mission(mission, user_ids)
        },
        toggle_publish() {
          return action_toggle_publish_mission(mission)
        },
        cancel_update_notification() {
          return action_cancel_notify_updated_mission(mission)
        },
      }
    })
  }
)

function mission_status({grouped_users_status, users_count, published}) {
  if (grouped_users_status.assigned.length >= users_count) {
    return "assigned"
  } else if (for_all(["new", "rejected", "assigned", "accepted"], (status) => grouped_users_status[status].length == 0) && !published) {
    return "draft"
  } else if (grouped_users_status.new.length + grouped_users_status.accepted.length + grouped_users_status.assigned.length < users_count && !published) {
    return "pending_selection"
  } else if (grouped_users_status.accepted.length > 0) {
    return "pending_assignement"
  } else {
    return "sent"
  }
}

const make_mission_periods = (mission) => {
  return make_periods(mission.events).map((period) => {
    return ({
      ...period,
      mission_start: mission.start,
      mission_end: mission.end,
      mission_status: mission.status,
      mission_workplace: mission.workplace,
      mission_id: mission.id,
    })
  })
}

export const get_user_missions = make_memoized((user_id) => tolerant_selector(
  [get_mission_proposals],
  (missions) => {
    let user_missions = filter_hash(missions, (mission) => mission.users_status[user_id])
    return array_from_hash(user_missions).map((mission) => {
      return {
        ...mission,
        user_status: mission.users_status[user_id],
        periods: make_mission_periods(mission), // deprecated, do not use
      }
    })
    .filter(({user_status}) => set_from_array(["new", "accepted", "rejected", "assigned"])[user_status])
  }
))

export const get_user_missions_events = make_memoized((user_id) => tolerant_selector(
  [get_user_missions(user_id)],
  (missions) => flatten_array(missions.map(property_getter('events')))
))

export const get_mission_users = make_memoized((mission_id, status) => tolerant_selector(
  [get_missions, get_users, get_mission_values_by_mission, get_values_by_field, get_raw_values, get_raw_fields],
  (missions, users, mission_values_by_mission, values_by_field, values, fields) => {
    let {grouped_users_status} = missions[mission_id] || {}
    if (grouped_users_status) {
      let user_ids_set = set_from_array(grouped_users_status[status])
      if (status == "available") {
        const accepted_values = {}
        mission_values_by_mission[mission_id].forEach((value_id) => {
          const {field_id} = values[value_id]
          const {for_entity, category, recycled} = fields[field_id]
          if (for_entity == 'User' && recycled) {
            if (category == 'non_exclusive') {
              user_ids_set = set_intersection(user_ids_set, values[value_id].entity_ids)
            } else if (category == 'exclusive') {
              accepted_values[field_id] = {
                ...(accepted_values[field_id] || {}),
                [value_id]: true,
              }
            }
          }
        })
        array_from_hash(fields).forEach(({id, category, for_entity, recycled, value_id_for}) => {
          if (for_entity == 'User' && recycled && category == 'exclusive') {
            const values = accepted_values[id]
            if (values) {
              user_ids_set = filter_set(user_ids_set, (user_id) => {
                return values[value_id_for[user_id]]
              })
            }
          }
        })
      }
      return filter_hash(users, ({id, status: {status}}) => user_ids_set[id] && status != 'applied')
    } else {
      return {}
    }
  }
))

export const get_initially_selected_recruiters = tolerant_selector(
  [get_recruiters, get_profile_id],
  (recruiters, profile_id) => {
    if (profile_id) {
      const {team_id, administrator} = recruiters[profile_id] || {}
      if (administrator) {
        return []
      } else if (team_id) {
        return array_from_hash(recruiters).filter(({team_id: id}) => id == team_id).map(property_getter('id'))
      } else {
        return [profile_id]
      }
    } else {
      return []
    }
  }
)

export const get_deals = tolerant_selector(
  [get_raw_deals, get_missions, get_raw_workplaces, get_recruiters, get_mission_fields_by_deal, get_fields_by_deal],
  (deals, all_missions, workplaces, recruiters, mission_fields_by_deal, fields_by_deal) => {
    const template_mission_ids = set_from_hash(map_hash(deals, property_getter('template_mission_id')))
    const missions_by_deal = split_array(
      array_from_hash(filter_hash(all_missions, ({id, deleted_at}) => !deleted_at && !template_mission_ids[id])),
      property_getter('deal_id'),
      Object.keys(deals),
    )
    return map_hash(
      deals,
      (deal) => {
        const {
          id, template_mission_id, custom_field_ids, has_custom_events, has_custom_workplace,
          recruiter_id,
        } = deal
        function filter_fields({fields, ...mission}, keep_custom = true) {
          return {
            ...mission,
            fields: (fields || []).filter(({id, category}) =>
              Boolean(keep_custom) == Boolean(custom_field_ids[id] || category == 'computed')
            ),
          }
        }
        function add_deals_fields({fields, ...mission}) {
          return {
            ...mission,
            fields: [...fields, ...array_from_hash(fields_by_deal[id] || {})]
          }
        }
        const template_mission = add_deals_fields(filter_fields(all_missions[template_mission_id] || {}, false))
        const missions = missions_by_deal[id]
          .map((mission) => filter_fields(mission, true))
          .filter(({deleted_at}) => !deleted_at)
        const counts = {};
        (new Array('users_count', 'accepted_count', 'assigned_count', 'empty_count', 'waiting_count')).forEach((count) => {
          counts[count] = array_sum(missions.map(property_getter(count, 0)))
        })
        const start = has_custom_events ? array_minimum(missions.map(property_getter('start')), (a, b) => moment(a).isBefore(b)) : template_mission.start
        const end = has_custom_events ? array_maximum(missions.map(property_getter('end')), (a, b) => moment(b).isBefore(b)) : template_mission.end
        const events = has_custom_events ? flatten_array(missions.map(property_getter('events'))) : template_mission.events
        const workplace_ids = has_custom_workplace ? missions.map(property_getter('workplace_id')) : [template_mission.workplace_id]
        const own_workplaces = workplace_ids.map((workplace_id) => workplaces[workplace_id] || {})
        deal = {
          ...deal,
          ...counts,
          missions,
          sent: exist(missions, property_getter('sent')),
          template_mission,
          status: deal_status(missions),
          workplace_ids,
          workplaces: own_workplaces,
          workplace: own_workplaces[0],
          recruiter: recruiters[recruiter_id] || {},
          start,
          end,
          events,
          fields: mission_fields_by_deal[id],
          has_custom_fields: deal_has_custom_fields(deal),
        }
        return {
          ...deal,
          cancel() {
            action_cancel_deal(deal)
          },
          duplicate() {
            action_duplicate_deal(deal)
          },
          add_mission() {
            action_add_mission(deal)
          },
          customize_fields() {
            action_customize_deal_fields(deal)
          },
          export_users() {
            return action_export_users(deal)
          }
        }
      }
    )
  }
)

export function deal_status(missions) {
  const statuses = split_array(missions, property_getter('status'), ['pending_selection', 'pending_assignement', 'assigned', 'sent', 'draft'])
  if (statuses['assigned'].length == missions.length) {
    return 'assigned'
  } else if (statuses['pending_selection'].length) {
    return 'pending_selection'
  } else if (statuses['pending_assignement'].length) {
    return 'pending_assignement'
  } else if (statuses['draft'].length) {
    return 'draft'
  } else {
    return 'sent'
  }
}

export const get_displayed_deals = tolerant_selector(
  [get_deals, get_recruiters],
  (deals, recruiters) => {
    let default_deal_ids = set_from_array(array_from_hash(recruiters).map(property_getter('default_deal_id')))
    return array_from_hash(deals).filter(({id, deleted_at}) => !deleted_at && !default_deal_ids[id])
  }
)

export const get_default_deal = tolerant_selector(
  [get_profile_id, get_recruiters, get_deals],
  (id, recruiters, deals) => {
    const {default_deal_id} = recruiters[id] || {}
    return deals[default_deal_id]
  }
)

export const get_mission_proposals = tolerant_selector(
  [get_missions, get_deals, get_raw_agency, get_recruiters],
  (missions, deals, agency, recruiters) => map_hash(missions, (mission) => {
    const deal = deals[mission.deal_id]
    const {
      has_custom_events, has_custom_workplace, template_mission_id, custom_field_ids,
      recruiter_id,
    } = deal || {custom_field_ids: {}}
    const template_mission = missions[template_mission_id] || {}
    let status = mission.status
    let sent = mission.sent
    if (template_mission_id == mission.id) {
      status = deal.status
      sent = deal.sent
    }
    const {fields, workplace} = template_mission
    const {workplace_shared_with_user} = agency
    function get_event_properties({start, end, events}) {
      return {start, end, events}
    }
    const real_workplace = has_custom_workplace ? mission.workplace : workplace
    return {
      ...mission,
      deal,
      status,
      sent,
      real_workplace,
      workplace: workplace_shared_with_user ? real_workplace : null,
      agency,
      recruiter: recruiters[recruiter_id] || {},
      ...get_event_properties(has_custom_events ? mission : template_mission),
      fields: (fields || []).filter(property_getter('shared_with_user')).map((field, index) => {
        if (custom_field_ids[field.id]) {
          return mission.fields[index]
        } else {
          return field
        }
      })
    }
  })
)

export const get_user_deals = make_memoized((user_id) => tolerant_selector(
  [get_user_missions(user_id), get_deals],
  (missions, deals) => {
    const deal_ids = {}
    missions.forEach(({deal_id}) => deal_ids[deal_id] = true)
    return array_from_hash(deals).filter(({id}) => deal_ids[id])
  }
))

export const get_proposals = make_memoized((mission_id) => tolerant_selector(
  [get_missions, get_users, get_fields_by_proposal],
  (missions, users, fields_by_proposal) => {
    const {assigned_proposals} = missions[mission_id] || {assigned_proposals: []}
    return assigned_proposals.map(({user_id, ...proposal}) => ({
      ...proposal,
      mission_id,
      user: users[user_id] || {},
      fields: array_from_hash(fields_by_proposal[proposal.id]) || [],
    }))
  }
))
