import React from 'react';
import Box from './Box';
import uuid from 'uuid/v4';
import {
  SortableContainer,
  SortableElement,
  arrayMove,
} from 'react-sortable-hoc';
import DragHandle from './DragHandle';
import { FieldArray, Formik, Form as FormikForm, Field } from 'formik';
import PropTypes from 'proptypes';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import {
  filter, find, flatten, identity, map, prop, propEq, reject, uniq, update
} from 'rambda';
import { insert } from 'ramda';

const allJobs = {
  frontend: 'Frontend',
  backend: 'Backend',
};

const nextWorkerName = (rows) =>
  `${rows.length + 1}. pracovník`;

class Form extends React.PureComponent {
  state = {
    rows: [
      {
        id: uuid(),
        name: 'pracovnik 1',
        preempt: false,
        linkedTo: null,
        jobs: [
          { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
          { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
        ]
      },
      {
        id: uuid(),
        name: 'pracovnik 2',
        preempt: false,
        linkedTo: null,
        jobs: [
          { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
          { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
        ]
      }
    ]
  };

  addWorker = (setFieldValue, rows) => {
    const updatedRows = [...rows, { id: uuid(), name: nextWorkerName(rows), jobs: [] } ];
    setFieldValue('rows', updatedRows);
  };

  render() {
    return (
      <Formik initialValues={this.state} render={({ setFieldValue, values: { rows } }) => (
        <React.Fragment>
          <h1>Konfigurácia projektu</h1>

          <Box flexDirection="row" marginBottom={10}>
            <Box flexBasis="20%" flexShrink={0}><strong>Pracovník</strong></Box>
            <Box flexBasis="60%" flexShrink={0}><strong>Úlohy</strong></Box>
            <Box flexBasis="10%" flexShrink={0} align="center"><strong>Prerušenie</strong></Box>
          </Box>

          <FieldArray name="rows" render={arrayHelpers => (
            <FormikForm>
              {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} prefix={`rows.${i}`} index={i} />)}
            </FormikForm>
          )} />

          <Box flexDirection="row" marginBottom={10}>
            <Box flexBasis="20%" flexShrink={0} onClick={() => this.addWorker(setFieldValue, rows)} style={{ cursor: 'pointer' }}>
              + Nový pracovník
            </Box>
          </Box>
        </React.Fragment>
      )}>
      </Formik>
    )
  }
}

const SortableJob = SortableElement(
  class extends React.PureComponent {
    static contextTypes = {
      formik: PropTypes.object
    };

    handleSelectChange = (type, selectedPairs) => {
      const { prefix } = this.props;
      const { formik: { setFieldValue } } = this.context;

      const selected = map(prop('value'), selectedPairs);

      setFieldValue(`${prefix}.${type}`, selected);
    };

    deleteJob = () => {
      const { rowIndex, myIndex } = this.props;
      const { formik: { setFieldValue, values: { rows } } } = this.context;

      const jobs = filter(identity, update(myIndex, null, rows[rowIndex].jobs));

      if (jobs.length) {
        setFieldValue(`rows.${rowIndex}.jobs`, jobs);
      } else {
        const updatedRows = filter(identity, update(rowIndex, null, rows));
        setFieldValue(`rows`, updatedRows);
      }
    };

    render() {
      const { myIndex, rowIndex, prefix, job, existingJobs: _existingJobs } = this.props;
      const { formik: { values: { rows } } } = this.context;

      const existingJobs = map(
        value => {
          const [idRow, idJob] = value.split(':');
          const row = find(propEq('id', idRow), rows);
          const jobName = find(propEq('id', idJob), row.jobs).name;

          return ({ value, label: `${row.name} – ${jobName}` });
        },
        reject(
          value => {
            const [, idJob] = value.split(':');
            return idJob === job.id;
          },
          _existingJobs
        )
      );

      return (
        <Box flexDirection="row" marginBottom={10} paddingBottom={10} backgroundColor="white" borderBottomColor="#1B1B3A" borderBottomWidth={1} borderStyle="solid">
          <Box marginRight={10} style={{ fontSize: '1.5rem' }}>
            <DragHandle />
          </Box>

          <Box flex={1}>
            <Box width={200} marginBottom={10} flexDirection="row">
              <Field component="select" name={`${prefix}.name`}>
               {Object.keys(allJobs).map(key => (
                  <option value={key} key={key}>{allJobs[key]}</option>
                ))}
              </Field>
              <Box style={{ cursor: 'pointer' }} onClick={this.deleteJob} marginLeft={5}>✕</Box>
            </Box>

            <Box flexDirection="row" justifyContent="space-between" marginBottom={10}>
              <Box flexBasis="30%">
                <label>
                  Trvanie: <br />
                  <Field type="number" step={1} name={`${prefix}.t`} style={{ width: '100%' }} />
                </label>
              </Box>
              <Box flexBasis="30%">
                <label>
                  Deadline: <br />
                  <Field type="number" step={1} name={`${prefix}.d`} style={{ width: '100%' }} />
                </label>
              </Box>
              <Box flexBasis="30%">
                <label>
                  Váha: <br />
                  <Field type="number" min={0} max={1} step={0.1} name={`${prefix}.w`} style={{ width: '100%' }} />
                </label>
              </Box>
            </Box>

            <Box>
              <Box as="label" flexDirection="row" marginBottom={5}>
                <Field type="radio" name={`${prefix}.succOrAnc`} value={0} /> Bez nadväznosti
              </Box>
              <Box marginBottom={5}>
                <Box as="label" flexDirection="row">
                  <Field type="radio" name={`${prefix}.succOrAnc`} value={1}/> Predchodcovia
                </Box>
                <Box flex={1} marginLeft={10} marginTop={5}>
                  <Select
                    disabled={false}
                    multi
                    onChange={e => this.handleSelectChange('anc', e)}
                    options={existingJobs}
                    removeSelected
                    value={job.anc}
                  />
                </Box>
              </Box>
              <Box>
                <Box as="label" flexDirection="row">
                  <Field type="radio" name={`${prefix}.succOrAnc`} value={2} /> Nasledovníci
                </Box>
                <Box flex={1} marginLeft={10} marginTop={5}>
                  <Select
                    disabled={false}
                    multi
                    onChange={e => this.handleSelectChange('succ', e)}
                    options={existingJobs}
                    removeSelected
                    value={job.succ}
                  />
                </Box>
              </Box>
            </Box>
          </Box>
        </Box>
      );
    }
  });

const SortableList = SortableContainer(({ jobs, existingJobs, prefix, rowIndex }) => (
  <div>
    {jobs.map((job, i) => (
      <SortableJob
        key={job.id}
        index={i}
        myIndex={i}
        rowIndex={rowIndex}
        job={job}
        prefix={`${prefix}.${i}`}
        existingJobs={existingJobs}
      />
    ))}
  </div>
));

class Row extends React.PureComponent {
  static contextTypes = {
    formik: PropTypes.object
  };

  onSortEnd = ({ oldIndex, newIndex }) => {
    const { prefix, index } = this.props;
    const { formik: { setFieldValue, values: { rows }} } = this.context;

    const updatedJobs = arrayMove(rows[index].jobs, oldIndex, newIndex);

    setFieldValue(`${prefix}.jobs`, updatedJobs);
  };

  findExistingJobs = () => {
    const { formik: { values: { rows } } } = this.context;

    const existingJobs = uniq(
      flatten(
        map(({ id: idRow, jobs }) =>
          map(({ id: idJob }) => `${idRow}:${idJob}`, jobs),
        rows)
      )
    );

    return existingJobs;
  };

  addJob = () => {
    const { index } = this.props;
    const { formik: { setFieldValue, values: { rows } } } = this.context;
    const newJob = {
      id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0
    };

    const jobs = [...rows[index].jobs, newJob];

    setFieldValue(`rows.${index}.jobs`, jobs);
  };

  duplicate = () => {
    const { id, index, name, preempt, jobs } = this.props;
    const { formik: { setFieldValue, values: { rows } } } = this.context;

    const jobsCopy = map(job => ({ ...job }), jobs);
    const newRow = { id: uuid(), name: `${name} - kópia`, preempt, linkedTo: id, jobs: jobsCopy };

    const updatedRows = insert(index + 1, newRow, rows);

    setFieldValue('rows', updatedRows);
  };

  render() {
    const { index, prefix, name, preempt, linkedTo, jobs } = this.props;
    const existingJobs = this.findExistingJobs();

    return (
      <Box flexDirection="row" marginBottom={30}>
        <Box flexBasis="20%" flexShrink={0}>{name}</Box>
        <Box flexBasis="60%" flexShrink={0}>
          <FieldArray render={() => (
            <SortableList jobs={jobs} onSortStart={this.onSortStart} onSortEnd={this.onSortEnd} useDragHandle={true} existingJobs={existingJobs} prefix={`${prefix}.jobs`} rowIndex={index} />
          )} />
          <Box onClick={this.addJob} style={{ cursor: 'pointer' }}>+ Nová úloha</Box>
        </Box>
        <Box flexBasis="10%" flexShrink={0} alignItems="center">
          <Field type="checkbox" name={`${prefix}.preempt`} />
        </Box>
        <Box alignItems="flex-end"><button type="button" onClick={this.duplicate}>Duplikovať a spojiť pracovníka</button></Box>
      </Box>
    )
  }
}

export default Form;