import {tolerant_selector} from 'selectors/base'
import {
  make_memoized, array_from_set, set_from_array, hash_with_key, filter_hash, property_getter, create_hash,
  empty_array, map_hash, array_from_hash, flatten_array, split_hash,
} from 'common/utilities'
import {
  missions_fields_options, users_fields_options, action_create_value, action_add_user_value,
  action_update_field, action_remove_field, action_select_entity_value, action_remove_entity_value,
  action_update_entity_comment, action_view_entity_template, action_upload_entity_file,
  action_fetch_entity_computed_value, action_remove_value, action_update_value, action_create_value_field,
  action_show_associated_values,
} from 'common/fields'
import {get_user_assigned_mission_ids} from './user_missions'
import store from 'common/store'

const get_raw_fields = (state) => state.fields.fields
const get_raw_values = (state) => state.fields.values
const get_raw_missions = (state) => state.missions.missions
const get_raw_deals = (state) => state.missions.deals
const get_raw_users = (state) => state.users.users
const get_raw_shifts = (state) => state.shifts.shifts

export const get_proposal_ids = tolerant_selector(
  [get_raw_missions],
  (missions) => flatten_array(
    array_from_hash(missions).map(({assigned_proposals}) => (assigned_proposals || []).map(property_getter('id')))
  )
)

export const get_values = tolerant_selector(
  [get_raw_values],
  (values) => map_hash(values, (value) => {
    const {field_id, id} = value
    return {
      ...value,
      update(new_value) {
        action_update_value({id, ...new_value})
      },
      remove() {
        action_remove_value(id)
      },
      create_value_field() {
        return action_create_value_field(field_id)
      },
      show_associated_values() {
        action_show_associated_values(id)
      },
    }
  })
)

export const get_values_by_field = tolerant_selector(
  [get_values, get_raw_fields],
  (values, fields) => split_hash(values, property_getter('field_id'), Object.keys(fields))
)

export const get_fields = tolerant_selector(
  [get_raw_fields, get_values_by_field],
  (fields, values_by_field) => array_from_hash(fields).map((field) => {
    const values = values_by_field[field.id]
    return {
      ...field,
      value_ids: Object.keys(values),
      values: array_from_hash(values),
      tolerant: true,
      get_value_props(value_id) {
        return values[value_id]
      },
      get_item_values({id}) {
        return [field.value_id_for[id]].filter(Boolean)
      },
      update(new_field) {
        return action_update_field({id: field.id, ...new_field})
      },
      remove() {
        return action_remove_field(field.id)
      },
      view_template(id) {
        return action_view_entity_template(id, field)
      },
      create_value(value_name) {
        return action_create_value(field.id, value_name)
      },
      select_value(entity_id, value_id, for_missions) {
        return action_select_entity_value(entity_id, value_id, for_missions)
      },
      update_comment(entity_id, comment) {
        return action_update_entity_comment(entity_id, field.id, comment)
      },
      fetch_value(entity_id) {
        return action_fetch_entity_computed_value(entity_id, field.id)
      },
    }
  })
)

function make_get_entity_ids(entity_type) {
  switch (entity_type) {
    case 'User':
      return (state) => Object.keys(state.users.users)
    case 'Mission':
      return (state) => Object.keys(state.missions.missions)
    case 'MissionProposal':
      return get_proposal_ids
    case 'Shift':
      return (state) => Object.keys(state.shifts.shifts)
    case 'Value':
      return (state) => Object.keys(state.fields.values)
    case 'Workplace':
      return (state) => Object.keys(state.missions.workplaces)
    case 'Deal':
      return (state) => Object.keys(state.missions.deals)
    default:
      return (state) => []
  }
}

const make_get_entity_values_by_entity = make_memoized((entity_type) => {
  if (entity_type == 'Mission') {
    return get_mission_values_by_mission
  }
  return tolerant_selector(
    [get_raw_values, make_get_entity_ids(entity_type), get_raw_fields],
    (values, entity_ids, fields) => create_hash(
      entity_ids,
      (entity_id) => Object.keys(values).filter((value_id) => {
        const {field_id, entity_ids} = values[value_id] || {}
        const {for_entity, category, value_id_for} = fields[field_id] || {}
        if (for_entity == entity_type) {
          switch (category) {
            case 'non_exclusive':
              return entity_ids[entity_id]
            case 'exclusive':
              return value_id == value_id_for[entity_id]
            default:
              return false
          }
        }
        return false
      })
    )
  )
})

export const get_mission_values_by_mission = tolerant_selector(
  [get_raw_values, get_raw_missions, get_raw_deals, get_raw_fields],
  (values, missions, deals, fields) => {
    return create_hash(
      Object.keys(missions),
      (mission_id) => {
        const {deal_id} = missions[mission_id]
        const {custom_field_ids, template_mission_id} = deals[deal_id] || {custom_field_ids: {}}
        const get_mission_id = make_memoized((field_id) => custom_field_ids[field_id] ? mission_id : template_mission_id)
        return Object.keys(values).filter((value_id) => {
          const {mission_ids, field_id} = values[value_id] || {mission_ids: {}}
          const {for_entity, recycled, category, value_id_for} = fields[field_id] || {}
          if (for_entity == 'Mission' || recycled) {
            if (category == 'exclusive' && for_entity == 'Mission') {
              return value_id == value_id_for[get_mission_id(field_id)]
            } else {
              return mission_ids[get_mission_id(field_id)]
            }
          }
          return false
        })
      }
    )
  }
)

const make_fields_with_selected_values = (item_type, item_id, fields, values, selected_values) => {
  let field_ids = fields.map(property_getter('id'))
  let selected_values_by_field = create_hash(field_ids, () => [])
  selected_values.forEach((id) => {
    const value = values[id]
    if (selected_values_by_field[value.field_id]) {
      selected_values_by_field[value.field_id].push(value)
    }
  })
  const for_missions = item_type == 'Mission'
  return hash_with_key(fields.map((field) => {
    const {
      value_id_for, id, recycled, files, select_value, update_comment, view_template,
      fetch_value, comments,
    } = field
    let {category} = field
    if (recycled && for_missions) {
      category = 'non_exclusive'
    }
    const selected_value_id = (value_id_for || {})[item_id]
    function is_empty() {
      switch (category) {
        case "non_exclusive":
          return empty_array(selected_values_by_field[id])
        case "comment":
          return !comments[item_id]
        case "file":
          return !files[item_id]
        case "exclusive":
          return !selected_value_id
      }
    }
    return {
      ...field,
      category,
      selected_values: selected_values_by_field[field.id],
      selected_value: values[selected_value_id],
      empty: is_empty(),
      comment: (comments || {})[item_id],
      file: (files || {})[item_id],
      selected: selected_value_id,
      allow_creating_values: !for_missions || !field.recycled,
      on_select_value(value_id) {
        return select_value(item_id, value_id, for_missions)
      },
      on_deselect_value(value_id) {
        return action_remove_entity_value(item_id, value_id, for_missions)
      },
      on_clear() {
        return action_remove_entity_value(item_id, selected_value_id)
      },
      on_submit_comment(comment) {
        return update_comment(item_id, comment)
      },
      view_template() {
        return view_template(item_id)
      },
      upload_file(file) {
        return action_upload_entity_file(item_id, id, file)
      },
      fetch_value() {
        return fetch_value(item_id)
      },
      user_id: item_type == 'User' && item_id,
    }
  }))
}

const make_get_fields_by_entity = (entity_type) => {
  function make_fields_filter(entity_id, values) {
    if (entity_type == 'Mission') {
      return ({for_entity, recycled}) => recycled || for_entity == 'Mission'
    }
    if (entity_type == 'Value') {
      const value = values[entity_id] || {}
      return ({for_entity, field_id}) => for_entity == 'Value' && value.field_id == field_id
    }
    return ({for_entity}) => for_entity == entity_type
  }
  return tolerant_selector(
    [
      get_fields, get_values, make_get_entity_ids(entity_type),
      make_get_entity_values_by_entity(entity_type),
    ],
    (fields, values, entity_ids, entity_values_by_entity) => create_hash(
      entity_ids,
      (entity_id) => make_fields_with_selected_values(
        entity_type,
        entity_id,
        fields.filter(make_fields_filter(entity_id, values)),
        values,
        entity_values_by_entity[entity_id]
      )
    )
  )
}

export const get_fields_by_proposal = make_get_fields_by_entity('MissionProposal')
export const get_fields_by_shift = make_get_fields_by_entity('Shift')
export const get_fields_by_user = make_get_fields_by_entity('User')
export const get_fields_by_mission = make_get_fields_by_entity('Mission')
export const get_fields_by_value = make_get_fields_by_entity('Value')
export const get_fields_by_workplace = make_get_fields_by_entity('Workplace')
export const get_fields_by_deal = make_get_fields_by_entity('Deal')

export const get_mission_fields = make_memoized((mission_id) => tolerant_selector(
  [get_fields_by_mission],
  (fields_by_mission) => fields_by_mission[mission_id]
))
export const get_user_fields = make_memoized((user_id) => tolerant_selector(
  [get_fields_by_user],
  (fields_by_user) => fields_by_user[user_id]
))

export const get_users_fields_with_locked_values = make_memoized((mission_id, status) => tolerant_selector(
  [get_mission_values_by_mission, get_users_filter_fields],
  (mission_values_by_mission, fields) => {
    if (status == 'available') {
      const locked_value_ids = set_from_array(mission_values_by_mission[mission_id] || [])
      return fields.map(({values, ...field}) => ({
        ...field,
        values: field.recycled ? values.map((value) => ({
          ...value,
          locked: locked_value_ids[value.id],
        })) : values,
      }))
    } else {
      return fields
    }
  }
))

const make_get_specific_fields = (entity_type, for_missions = false) => tolerant_selector(
  [get_fields],
  (fields) => fields.filter(
    ({for_entity, recycled}) => for_entity == entity_type || (for_missions && recycled)
  ).map((field) => {
    if (field.recycled && for_missions) {
      return {
        ...field,
        category: 'non_exclusive',
      }
    }
    return field
  })
)

export const get_users_fields = make_get_specific_fields('User')
export const get_missions_fields = make_get_specific_fields('Mission', true)
export const get_proposals_fields = make_get_specific_fields('MissionProposal')
export const get_shifts_fields = make_get_specific_fields('Shift')
export const get_workplaces_fields = make_get_specific_fields('Workplace')
export const get_exports_fields = make_get_specific_fields('Agency')
export const get_deals_fields = make_get_specific_fields('Deal')
export const get_missions_filter_fields = tolerant_selector(
  [get_missions_fields],
  (fields) => fields.map((field) => ({
    ...field,
    get_item_values({template_mission, custom_field_ids, missions}) {
      if (custom_field_ids[field.id]) {
        return flatten_array(missions.map(field.get_item_values))
      }
      return field.get_item_values(template_mission)
    },
  }))
)

export const get_users_filter_fields = tolerant_selector(
  [
    get_users_fields, get_missions_fields, get_user_assigned_mission_ids, get_fields_by_mission,
    get_raw_missions, get_raw_deals,
  ],
  (users_fields, missions_fields, user_assigned_mission_ids, fields_by_mission, missions, deals) => {
    return [
      ...users_fields,
      ...missions_fields.map((field) => {
        const {category, recycled} = field
        if (category == 'exclusive' && !recycled) {
          return {
            ...field,
            get_item_values({id}) {
              const mission_ids = user_assigned_mission_ids[id] || []
              return flatten_array(mission_ids.map((mission_id) => {
                const mission = missions[mission_id] || {}
                const {custom_field_ids, template_mission_id} = deals[mission.deal_id] || {}
                if ((custom_field_ids || {})[field.id]) {
                  return field.get_item_values(mission)
                } else {
                  return field.get_item_values(missions[template_mission_id] || {})
                }
              }))
            },
          }
        }
        return null
      }).filter(Boolean),
    ]
  }
)

export const get_missions_recyclable_fields = tolerant_selector(
  [get_raw_fields],
  (fields) => filter_hash(
    fields,
    ({for_entity, recycled, category}) => for_entity == 'User' && !recycled && (category == 'non_exclusive' || category == 'exclusive')
  )
)

export const get_mission_fields_by_deal = tolerant_selector(
  [get_fields_by_mission, get_raw_deals],
  (fields_by_mission, deals) => map_hash(deals, ({template_mission_id}) => array_from_hash(fields_by_mission[template_mission_id]))
)
