import React from 'react'
import DefaultView from 'views/DefaultView'
import { Grid, Row, Col, Well, PageHeader, Button } from 'react-bootstrap'
import { fetchServices } from 'modules/api_data/actions'
import Service from 'modules/api_data/objects/Service'
import { ServiceType } from 'modules/api_data/types'
import {
  FormText,
  FormTextList,
  FormValidationType,
  FormValidation,
  FormDropdown
} from 'components/Forms'

import { enrollProjectJson } from '../actions'

class CodeRepo {
  constructor (name = '', branch = '', reviewers = []) {
    this.name = name
    this.branch = branch
    this.reviewers = reviewers
  }
}

class JiraProject {
  constructor (name = '', componentStr = '', assignees = []) {
    this.name = name
    this.components = componentStr
    this.assignees = assignees
  }
}

export default class EnrollForm extends DefaultView {

  constructor (props) {
    super(props)
    this.state = {
      projectId: '',
      projectName: '',
      emailAddress: '',
      org: null,
      team: null,
      techLeads: [],
      curTechLead: '',
      developers: [],
      curDeveloper: '',
      jiraProjects: [],
      curJiraProjectName: '',
      curJiraProjectComponentStr: '',
      repos: [],
      curRepoName: '',
      curRepoBranch: '',
      buildJobs: [],
      curBuildJob: '',
      showOrgFormText: false,
      showTeamFormText: false,
      validations: {}
    }

    // Our validators that we must bind.
    this.validateString = this.validateString.bind(this)
    this.validateServiceId = this.validateServiceId.bind(this)
    this.validateServiceName = this.validateServiceName.bind(this)
    this.validateEmailAddress = this.validateEmailAddress.bind(this)
    this.validateJiraKey = this.validateJiraKey.bind(this)

    // Other functions we need to bind.
    this.submit = this.submit.bind(this)
    this.isSubmitDisabled = this.isSubmitDisabled.bind(this)
    this.showOrgFormText = this.showOrgFormText.bind(this)
    this.showTeamFormText = this.showTeamFormText.bind(this)
  }

  documentTitle () {
    return 'Project Enrollment'
  }

  componentDidMount () {
    this.props.dispatch(fetchServices())
  }

  getValidation (key) {
    let validation = this.state.validations[key]
    if (validation === undefined) {
      validation = new FormValidation()
    }
    return validation
  }

  setValidation (key, validation) {
    let validations = Object.assign({}, this.state.validations)
    validations[key] = validation
    return validations
  }

  showOrgFormText (doShow) {
    const isShowing = this.state.showOrgFormText
    if (!isShowing && doShow) {
      this.setState({ showOrgFormText: true })
    } else if (isShowing && !doShow) {
      this.setState({ showOrgFormText: false })
    }
  }

  showTeamFormText (doShow) {
    const isShowing = this.state.showTeamFormText
    if (!isShowing && doShow) {
      this.setState({ showTeamFormText: true })
    } else if (isShowing && !doShow) {
      this.setState({ showTeamFormText: false })
    }
  }

  submit () {
    // Add our tech leads.
    let techLeads = this.state.techLeads
    if (this.state.curTechLead !== '') {
      techLeads.push(this.state.curTechLead)
    }

    // Add our developers.
    let developers = this.state.developers
    if (this.state.curDeveloper !== '') {
      developers.push(this.state.curDeveloper)
    }

    // Handle the repos the user entered.
    let repos = []
    let codeReviews = []
    let formRepos = this.state.repos
    if (this.state.curRepoName !== '') {
      const repo = new CodeRepo(this.state.curRepoName, this.state.curRepoBranch)
      formRepos.push(repo)
    }
    formRepos.forEach((repo, idx) => {
      repo.reviewers = techLeads
      repos.push({
        github_repository: {
          name: repo.name
        }
      })
      codeReviews.push({
        reviewer: repo.reviewers,
        github_pull_request: {
          repository: {
            name: repo.name
          },
          upstream_branch: repo.branch
        }
      })
    })

    // Handle our Jira Projects.
    let jiraProjects = []
    let formJiraProjects = this.state.jiraProjects
    if (this.state.curJiraProjectName !== '') {
      const jiraProject = new JiraProject(this.state.curJiraProjectName, this.state.curJiraProjectComponentStr)
      formJiraProjects.push(jiraProject)
    }
    formJiraProjects.forEach((jiraProject, idx) => {
      let components = []
      jiraProject.components.split(',').forEach((component) => {
        component = component.trim()
        if (component !== '') {
          components.push(component)
        }
      })
      jiraProject.assignees = techLeads
      jiraProjects.push({
        jira_project: {
          project: jiraProject.name,
          component: components
        },
        assignee: jiraProject.assignees
      })
    })

    // Add our jenkins builds.
    let builds = []
    let formBuildJobs = this.state.buildJobs
    if (this.state.curBuildJob !== '') {
      formBuildJobs.push(this.state.curBuildJob)
    }
    formBuildJobs.forEach((jenkinsJob) => {
      builds.push({ jenkins_job: { name: jenkinsJob } })
    })

    let teamName = ''
    if (this.state.team !== null) {
      teamName = this.state.team.id
    }

    let orgName = ''
    if (this.state.org !== null) {
      orgName = this.state.org.id
    }

    let submitData = {
      project_metadata: {
        project_id: this.state.projectId,
        project_name: this.state.projectName,
        team_name: teamName,
        org_name: orgName,
        source_repositories: repos,
        tech_lead: techLeads,
        developer: developers,
        developer_email_list: this.state.emailAddress,
        issue_tracker: jiraProjects,
        code_review: codeReviews,
        build_service: builds
      }
    }
    submitData = JSON.stringify(submitData)
    this.props.dispatch(enrollProjectJson(submitData, this.state.projectId, this.state.projectName))
  }

  isSubmitDisabled () {
    // Make sure the user has provided an org and team.  If not, then submit is disabled.
    if (this.state.org === null || this.state.org.id.trim() === '') {
      return true
    }
    if (this.state.team === null || this.state.team.id.trim() === '') {
      return true
    }

    // List of keys of forms that must be valid and present in order for the form to be submitable.
    let requiredKeys = [
      'formProjectId',
      'formProjectName',
      'formTechLeads',
      'formEmailAddress',
      'formJira1',
      'formJenkins',
      'formGitHub1',
      'formGitHub2'
    ]

    // Iterate through the keys of our form validations and make sure they're all valid.  If one isn't then we
    // disable the submit button.
    const validations = this.state.validations
    const keys = Object.keys(validations)
    for (let key of keys) {
      let validation = validations[key]
      let keyIsValid = true
      if (validation.type !== FormValidationType.SUCCESS) {
        // In the special cases of list forms, its ok if the form has nothing and the list has at least 1 entry already.
        let existingEntries
        let curFormEntry

        if (key === 'formTechLeads') {
          existingEntries = this.state.techLeads
          curFormEntry = this.state.curTechLead
        } else if (key === 'formDevelopers') {
          existingEntries = this.state.developers
          curFormEntry = this.state.curDeveloper
        } else if (key === 'formJira1') {
          existingEntries = this.state.jiraProjects
          curFormEntry = this.state.curJiraProjectName
        } else if (key === 'formJira2') {
          existingEntries = this.state.jiraProjects
          curFormEntry = this.state.curJiraProjectComponentStr
        } else if (key === 'formJenkins') {
          existingEntries = this.state.buildJobs
          curFormEntry = this.state.curBuildJob
        } else if (key === 'formGitHub1') {
          existingEntries = this.state.repos
          curFormEntry = this.state.curRepoName
        } else if (key === 'formGitHub2') {
          existingEntries = this.state.repos
          curFormEntry = this.state.curRepoBranch
        }

        if (existingEntries !== undefined) {
          if (existingEntries.length === 0 || curFormEntry !== '') {
            keyIsValid = false
          }
        } else {
          keyIsValid = false
        }
      }

      // If this form is a requiredKey, then note that it has been accounted for.
      const keyIdx = requiredKeys.indexOf(key)
      if (keyIdx !== -1) {
        if (keyIsValid) {
          requiredKeys.splice(keyIdx, 1)
        }
      }
    }

    // Verify that all of our required keys have been validated a'ok.
    if (requiredKeys.length === 0) {
      return false
    }
    return true
  }

  validateMaxLength (value, maxTextLength) {
    const valueLength = value.length
    if (maxTextLength > 0) {
      if (valueLength > maxTextLength) {
        return new FormValidation(FormValidationType.ERROR, `Must be less than ${maxTextLength} characters in length`)
      }
    }
    return new FormValidation()
  }

  validateMinLength (value, minTextLength) {
    const valueLength = value.length
    if (minTextLength > 0 && valueLength < minTextLength) {
      return new FormValidation(FormValidationType.ERROR, `Text must be at least ${minTextLength} characters in length`)
    }
    return new FormValidation()
  }

  validateString (value) {
    if (value.length === 0) {
      return new FormValidation()
    }
    return new FormValidation(FormValidationType.SUCCESS)
  }

  validateServiceId (value) {
    if (value.length === 0) {
      return new FormValidation()
    }
    // Verify length.
    let validation = this.validateMinLength(value, 3)
    if (validation.type !== FormValidationType.NULL) {
      return validation
    }
    validation = this.validateMaxLength(value, 64)
    if (validation.type !== FormValidationType.NULL) {
      return validation
    }
    // Verify that the chars are valid.
    if (value.match(/^[a-z-]*$/) === null) {
      return new FormValidation(FormValidationType.ERROR, 'Please only use a-z and hyphens')
    }
    // Verify that this id isn't already in use.
    for (let serviceId of Object.keys(this.props.services)) {
      if (serviceId.toLowerCase() === value.toLowerCase()) {
        return new FormValidation(FormValidationType.ERROR, 'ID is already in use!')
      }
    }
    return new FormValidation(FormValidationType.SUCCESS)
  }

  validateServiceName (value) {
    if (value.length === 0) {
      return new FormValidation()
    }
    // Verfiy length.
    let validation = this.validateMinLength(value, 3)
    if (validation.type !== FormValidationType.NULL) {
      return validation
    }
    validation = this.validateMaxLength(value, 64)
    if (validation.type !== FormValidationType.NULL) {
      return validation
    }
    // Verify that the chars are valid.
    if (value.match(/^[A-Za-z0-9 ]*$/) === null) {
      return new FormValidation(FormValidationType.ERROR, 'Please don\'t use special characters')
    }
    // Verify name isn't already in use.
    for (let serviceId of Object.keys(this.props.services)) {
      const service = this.props.services[serviceId]
      if (service.name.toLowerCase() === value.toLowerCase()) {
        return new FormValidation(FormValidationType.ERROR, 'Name is already in use!')
      }
    }
    return new FormValidation(FormValidationType.SUCCESS)
  }

  validateJiraKey (value) {
    if (value.length === 0) {
      return new FormValidation()
    }
    // Verify that the chars are valid.
    if (value.match(/^[A-Z]*$/) === null) {
      return new FormValidation(FormValidationType.ERROR)
    }
    return new FormValidation(FormValidationType.SUCCESS)
  }

  validateEmailAddress (value) {
    if (value.length === 0) {
      return new FormValidation()
    }
    // Verify that the chars are valid.
    // eslint-disable-next-line
    const re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
    if (value.match(re) === null) {
      return new FormValidation(FormValidationType.ERROR, 'Invalid email address')
    }
    return new FormValidation(FormValidationType.SUCCESS)
  }

  renderContent () {
    let orgNames = this.props.orgs.map((org) => {
      return org.name
    })

    let teamNames = []
    let enableTeamDropdown = false
    let showTeamFormText = this.state.showTeamFormText
    let teamPlaceholderText = 'Select an org first'
    if (this.state.showOrgFormText) {
      enableTeamDropdown = false
      teamPlaceholderText = 'Other'
      showTeamFormText = true
    } else if (this.state.org !== null) {
      enableTeamDropdown = true
      teamPlaceholderText = 'Select an option'
      teamNames = this.state.org.children.map((teamId) => {
        const service = this.props.services[teamId]
        return service.name
      })
      if (teamNames.length === 0) {
        enableTeamDropdown = false
        teamPlaceholderText = 'Other'
        showTeamFormText = true
      }
    }

    let orgFormText = null
    if (this.state.showOrgFormText) {
      orgFormText = (
        <Row>
          <Col>
            <FormText
              controlId='formOrg'
              valuePlaceholder='Organization Name'
              value={(this.state.org !== null) ? this.state.org.id : ''}
              validation={this.getValidation('formOrg')}
              valueHandler={(value) => {
                const org = new Service(value, value, ServiceType.ORG)
                this.setState({
                  org: org,
                  validations: this.setValidation('formOrg', this.validateServiceName(org.name))
                })
              }}
              width='400px'
            />
          </Col>
        </Row>
      )
    }

    let teamFormText = null
    if (showTeamFormText) {
      teamFormText = (
        <Row>
          <Col>
            <FormText
              controlId='formTeam'
              valuePlaceholder='Team Name'
              value={(this.state.team !== null) ? this.state.team.id : ''}
              validation={this.getValidation('formTeam')}
              valueHandler={(value) => {
                const team = new Service(value, value, ServiceType.TEAM)
                this.setState({
                  team: team,
                  validations: this.setValidation('formTeam', this.validateServiceName(team.name))
                })
              }}
              width='400px'
            />
          </Col>
        </Row>
      )
    }

    return (
      <Grid>
        <PageHeader>
          New Project Enrollment
          <br />
          <small>
            Enrolling your project allows RPS to collect metrics for your project
          </small>
        </PageHeader>
        <Well>
          <Grid fluid>
            <Row>
              <Col>
                <FormText
                  controlId='formProjectId'
                  controlLabel='Project ID:'
                  controlHelp='Short project name used as an identifier.'
                  valuePlaceholder='Enter text...'
                  value={this.state.projectId}
                  validation={this.getValidation('formProjectId')}
                  valueHandler={(value) => {
                    this.setState({
                      projectId: value,
                      validations: this.setValidation('formProjectId', this.validateServiceId(value))
                    })
                  }}
                  width='510px'
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormText
                  controlId='formProjectName'
                  controlLabel='Project Name:'
                  controlHelp='Nice human-friendly project name.  This will be displayed throughout RPS.'
                  valuePlaceholder='Enter text...'
                  value={this.state.projectName}
                  validation={this.getValidation('formProjectName')}
                  valueHandler={(value) => {
                    this.setState({
                      projectName: value,
                      validations: this.setValidation('formProjectName', this.validateServiceName(value))
                    })
                  }}
                  width='510px'
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormDropdown
                  controlId='formOrg'
                  controlLabel='Organization:'
                  controlHelp='Organization that this project belongs to.'
                  valuePlaceholder='Select an option'
                  options={orgNames}
                  onSelect={(orgId) => {
                    if (orgId === 'other_selected') {
                      this.showOrgFormText(true)
                      this.setState({ org: null })
                      return
                    }
                    this.showOrgFormText(false)
                    const org = this.props.services[orgId]
                    this.setState({ org: org })
                  }}
                />
              </Col>
            </Row>
            {orgFormText}
            <Row>
              <Col>
                <FormDropdown
                  controlId='formTeam'
                  controlLabel='Team:'
                  controlHelp='Team that this project belongs to.'
                  valuePlaceholder={teamPlaceholderText}
                  disabled={!enableTeamDropdown}
                  options={teamNames}
                  onSelect={(teamId) => {
                    if (teamId === 'other_selected') {
                      this.showTeamFormText(true)
                      this.setState({ team: null })
                      return
                    }
                    this.showTeamFormText(false)
                    const team = this.props.services[teamId]
                    this.setState({ team: team })
                  }}
                />
              </Col>
            </Row>
            {teamFormText}
            <Row>
              <Col>
                <FormTextList
                  controlId='formTechLeads'
                  controlLabel='Tech Leads:'
                  controlHelp='Owner(s) of this project and main point of contact.'
                  valuePlaceholders={['LDAP Username']}
                  valueWidths={['510px']}
                  values={[this.state.curTechLead]}
                  valueValidations={[ this.getValidation('formTechLeads') ]}
                  valueHandlers={[(value) => {
                    this.setState({
                      curTechLead: value,
                      validations: this.setValidation('formTechLeads', this.validateString(value))
                    })
                  }]}
                  listHandler={(entries) => {
                    let leads = []
                    entries.forEach((values) => {
                      leads.push(values[0])
                    })
                    this.setState({ techLeads: leads })
                  }}
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormTextList
                  controlId='formDevelopers'
                  controlLabel='Developers:'
                  controlHelp='Other folk whom also contribute to this project.'
                  valuePlaceholders={['LDAP Username']}
                  valueWidths={['510px']}
                  values={[this.state.curDeveloper]}
                  valueValidations={[ this.getValidation('formDevelopers') ]}
                  valueHandlers={[(value) => {
                    this.setState({
                      curDeveloper: value,
                      validations: this.setValidation('formDevelopers', this.validateString(value))
                    })
                  }]}
                  listHandler={(entries) => {
                    let developers = []
                    entries.forEach((values) => {
                      developers.push(values[0])
                    })
                    this.setState({ developers: developers })
                  }}
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormText
                  controlId='formEmailAddress'
                  controlLabel='Email List:'
                  controlHelp='Email address for the email-list for this project.  Leave blank if there is none.'
                  valuePlaceholder='Email Address'
                  value={this.state.emailAddress}
                  validation={this.getValidation('formEmailAddress')}
                  valueHandler={(value) => {
                    this.setState({
                      emailAddress: value,
                      validations: this.setValidation('formEmailAddress', this.validateEmailAddress(value))
                    })
                  }}
                  width='510px'
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormTextList
                  controlId='formJira'
                  controlLabel='Jira Projects:'
                  controlHelp='Project(s) in Jira and their component(s).  Comma seperate list of components.'
                  valuePlaceholders={['Jira Project Key (i.e. SYS)', 'Components (optional)']}
                  valueWidths={['250px', '250px']}
                  values={[this.state.curJiraProjectName, this.state.curJiraProjectComponentStr]}
                  valueValidations={[
                    this.getValidation('formJira1'),
                    this.getValidation('formJira2')]
                  }
                  valueHandlers={[
                    (value) => {
                      value = value.toUpperCase()
                      this.setState({
                        curJiraProjectName: value,
                        validations: this.setValidation('formJira1', this.validateJiraKey(value))
                      })
                    },
                    (value) => {
                      this.setState({
                        curJiraProjectComponentStr: value,
                        validations: this.setValidation('formJira2', this.validateString(value))
                      })
                    }
                  ]}
                  listHandler={(entries) => {
                    let jiraProjects = []
                    entries.forEach((values) => {
                      jiraProjects.push(new JiraProject(values[0], values[1]))
                    })
                    this.setState({ jiraProjects: jiraProjects })
                  }}
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormTextList
                  controlId='formJenkins'
                  controlLabel='Jenkins Jobs:'
                  controlHelp='Jobs in Jenkins that are associated with this project.'
                  valuePlaceholders={['Jenkins Job Name']}
                  valuePrefixes={['jenkins.internal.justin.tv/job/']}
                  valueWidths={['310px']}
                  values={[this.state.curBuildJob]}
                  valueValidations={[this.getValidation('formJenkins')]}
                  valueHandlers={[
                    (value) => {
                      this.setState({
                        curBuildJob: value,
                        validations: this.setValidation('formJenkins', this.validateString(value))
                      })
                    }
                  ]}
                  listHandler={
                    (entries) => {
                      let jobs = []
                      entries.forEach((values) => {
                        jobs.push(values[0])
                      })
                      this.setState({ buildJobs: jobs })
                    }
                  }
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <FormTextList
                  controlId='formGitHub'
                  controlLabel='GitHub Repositories:'
                  controlHelp='Repositorie(s) associated with this project and their upstream branch.'
                  valuePlaceholders={['org/repo', 'branch']}
                  valuePrefixes={['git-aws.internal.justin.tv/']}
                  valueWidths={['250px', '250px']}
                  values={[this.state.curRepoName, this.state.curRepoBranch]}
                  valueValidations={[this.getValidation('formGitHub1'), this.getValidation('formGitHub2')]}
                  valueHandlers={[
                    (value) => {
                      this.setState({
                        curRepoName: value,
                        validations: this.setValidation('formGitHub1', this.validateString(value))
                      })
                    },
                    (value) => {
                      this.setState({
                        curRepoBranch: value,
                        validations: this.setValidation('formGitHub2', this.validateString(value))
                      })
                    }
                  ]}
                  listHandler={(entries) => {
                    let repos = []
                    entries.forEach((values) => {
                      repos.push(new CodeRepo(values[0], values[1]))
                    })
                    this.setState({ repos: repos })
                  }}
                />
              </Col>
            </Row>
            <Row>
              <Col>
                <table style={{ width: '100%' }}>
                  <tbody>
                    <tr>
                      <td style={{ textAlign: 'right' }}>
                        <Button onClick={this.submit} disabled={this.isSubmitDisabled()}>Submit</Button>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </Col>
            </Row>
          </Grid>
        </Well>
      </Grid>
    )
  }
}

EnrollForm.propTypes = {
  dispatch: React.PropTypes.func,
  services: React.PropTypes.object,
  orgs: React.PropTypes.array
}
