import Service from '../objects/Service'
import { ServiceType } from '../types'
import ApiUtil from 'utils/ApiUtil'

export const REQUEST_SERVICES = 'REQUEST_SERVICES'
export const RECEIVE_SERVICES = 'RECEIVE_SERVICES'
export const RECEIVE_SERVICES_ERROR = 'RECEIVE_SERVICES_ERROR'
export const RECEIVE_SERVICES_EXISTING = 'RECEIVE_SERVICES_EXISTING'

/**
 * Queries our API for our list of services and returns REQUEST_SERVICES and RECEIVE_SERVICES.
 */
export function fetchServices () {
  return function (dispatch, getState) {
    dispatch(requestServices())
    const existingServices = getState().api_data.services
    if (Object.keys(existingServices).length > 0) {
      dispatch(receiveServicesExisting())
      return Promise.resolve()
    }
    let promises = []
    promises.push(ApiUtil.getOrgs())
    promises.push(ApiUtil.getTeams())
    promises.push(ApiUtil.getProjects())
    return Promise.all(promises).then(
      (jsons) => {
        let services = {}
        let orgIds = getOrgs(jsons[0], services)
        let teamIds = getTeams(jsons[1], services)
        let projectIds = getProjects(jsons[2], services)
        if (services['undef_org'] !== undefined) {
          orgIds.push('undef_org')
        }
        // For each service, sort its children by their name, so whenever we need to display, they're already sorted.
        Object.keys(services).forEach((serviceId) => {
          services[serviceId].children.sort((serviceIdA, serviceIdB) => {
            return services[serviceIdA].compareNameTo(services[serviceIdB])
          })
        })
        dispatch(receiveServices(services, orgIds, teamIds, projectIds))
      }
    ).catch(
      (error) => {
        dispatch(receiveServicesError(error))
        return Promise.reject(error)
      }
    )
  }
}

function getOrgs (json, services) {
  let orgIds = []
  for (let orgData of json.orgs) {
    let org = new Service(orgData.org_name, orgData.org_name, ServiceType.ORG)
    services[org.id] = org
    orgIds.push(org.id)
    let teams = orgData.teams
    if (teams !== undefined) {
      for (let teamData of teams) {
        const team = new Service(teamData.team_name, teamData.team_name, ServiceType.TEAM)
        services[team.id] = team
        org.addChild(team)
      }
    }
  }
  return orgIds
}

function getTeams (json, services) {
  let teamIds = []
  for (let teamData of json.teams) {
    const teamId = teamData.team_name
    let team = services[teamId]
    if (team === undefined) {
      console.warn(`Got an undefined team: '${teamId}'`)
      team = new Service(teamData.team_name, teamData.team_name, ServiceType.TEAM)
      services[team.id] = team
    }
    teamIds.push(teamId)
    let projects = teamData.projects
    if (projects !== undefined) {
      for (let projectData of projects) {
        const project = new Service(projectData.project_id, projectData.project_name, ServiceType.PROJECT)
        services[project.id] = project
        team.addChild(project)
      }
    }
  }
  return teamIds
}

function getProjects (json, services) {
  let projectIds = []
  for (let projectData of json.projects) {
    const projectId = projectData.project_id
    let project = services[projectId]
    if (project === undefined) {
      console.warn(`Got an undefined project: '${projectId}'`)
      project = new Service(projectId, projectData.project_name, ServiceType.PROJECT)
      services[project.id] = project
      let id = 'undef_team'
      let team = services[id]
      if (team === undefined) {
        team = new Service(id, 'Miscellaneous', ServiceType.TEAM)
        team.hasMetrics = false
        let org = new Service('undef_org', 'Miscellaneous', ServiceType.ORG)
        org.addChild(team)
        services[id] = team
        services['undef_org'] = org
      }
      team.addChild(project)
    }
    projectIds.push(project.id)
  }
  return projectIds
}

const requestServices = () => ({
  type: REQUEST_SERVICES
})

function receiveServices (services, orgIds, teamIds, projectIds) {
  return {
    type: RECEIVE_SERVICES,
    services: services,
    orgIds: orgIds.sort(),
    teamIds: teamIds.sort(),
    projectIds: projectIds.sort()
  }
}

const receiveServicesError = (error) => ({
  type: RECEIVE_SERVICES_ERROR,
  error: error
})

const receiveServicesExisting = () => ({
  type: RECEIVE_SERVICES_EXISTING
})
