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, remove } from 'ramda';
import theme from '../theme';

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

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

  onSubmit = (values) => {
    if (!values || !values.rows.length) return;

    const { onSubmit } = this.props;
    onSubmit(values);
  };

  addWorker = (setFieldValue, rows, allJobs) => {
    const jobs = [{
      id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: []
    }];

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

  addTemplateWorker = (setFieldValue, allJobs) => (e) => {
    if (e.keyCode !== undefined && e.keyCode !== 13) return;

    e.preventDefault();

    const val = this.new.value.replace(/:/g, '');
    const updatedJobs = [...allJobs, val ];
    setFieldValue('allJobs', updatedJobs);

    this.new.value = null;
  };

  render() {
    return (
      <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows, allJobs, flowShop } }) => (
        <FormikForm>
          <h1>1/3 Vytvorenie úloh</h1>
          <Box flexWrap="wrap" flexDirection="row">
            {allJobs.map((_, key) => (
              <Box key={key} width={300} flexDirection="row" marginBottom={10}>
                <Box as={Field} name={`allJobs.${key}`} flex={1} marginRight={5} onChange={({ target: { value } }) => setFieldValue(`allJobs.${key}`, value)} />
                <Box cursor="pointer" onClick={() => setFieldValue(`allJobs`, remove(key, 1, allJobs))} color="red" marginRight={20}>✕</Box>
              </Box>
            ))}
          </Box>

          <Box width={300} flexDirection="row" marginBottom={10}>
            <Box as="input" placeholder="Nová úloha" onKeyDown={this.addTemplateWorker(setFieldValue, allJobs)} nativeRef={c => this.new = c} marginRight={5} flex={1} />
            <Box cursor="pointer" onClick={this.addTemplateWorker(setFieldValue, allJobs)} color="green" fontSize="1rem">+</Box>
          </Box>

          {allJobs.length ? (
            <div>
              <h1>2/3 Konfigurácia projektu</h1>

              {rows.length ?
                <Box flexDirection="row" marginBottom={10}>
                  <Box flexBasis="20%" flexShrink={0}><strong>Pracovníci</strong></Box>
                  <Box flexBasis="63%" flexShrink={0}><strong>Priradené úlohy</strong></Box>
                </Box> : null
              }

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

              <Box flexDirection="row" marginBottom={10}>
                <Box
                  flexShrink={0}
                  onClick={() => this.addWorker(setFieldValue, rows, allJobs)}
                  {...theme.buttons.primary}
                >
                  + Nový pracovník
                </Box>
              </Box>

              {rows.length ?
                <div>
                  <Box flexDirection="row" justifyContent="center" marginBottom={10}>
                    <Box as="label" justifyContent="center" flexDirection="row" marginRight={5}>
                      <Field type="radio" name="flowShop" value="1" checked={flowShop == '1'} disabled={rows.length === 1} />
                      <Box as="em" color="gray" textAlign="center" fontSize="0.9em">
                        Flow shop
                      </Box>
                    </Box>

                    <Box as="label" justifyContent="center" flexDirection="row">
                      <Field type="radio" name="flowShop" value="0" checked={flowShop == '0'} disabled={rows.length === 1} />
                      <Box as="em" color="gray" textAlign="center" fontSize="0.9em">
                        Pracovníci zvládnu každú úlohu
                      </Box>
                    </Box>
                  </Box>

                  <Box as="label" justifyContent="center" flexDirection="row">
                    <Field type="checkbox" name="preempt" disabled={flowShop == '1' || rows.length === 1}/>
                    <Box as="em" color="gray" textAlign="center" fontSize="0.9em">
                      Úlohy pracovníkov možno prerušiť
                    </Box>
                  </Box>

                  <Box as="button" margin="auto" marginTop={10} width="20rem" alignItems="center"
                       padding={5} {...theme.buttons.primary} backgroundColor="#C89C93" fontSize="1rem">
                    Vytvoriť rozvrh
                  </Box>
                </div>
                : null}
            </div>
          ) : null}
        </FormikForm>
      )}>
      </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 { prefix, job, existingJobs: _existingJobs } = this.props;
      const { formik: { values: { rows, allJobs, flowShop } } } = 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}
          className="sortable-inactive"
        >
          {flowShop == '1' ?
            <Box marginRight={10} fontSize="1.5rem" width={20}>
              <DragHandle/>
            </Box>
           : null}

          <Box flex={1} padding={1}>
            <Box width={200} marginBottom={10} flexDirection="row">
              <Field component="select" name={`${prefix}.name`}>
               {allJobs.map((job, key) => (
                  <option value={job} key={key}>{job}</option>
                ))}
              </Field>
              <Box cursor="pointer" onClick={this.deleteJob} marginLeft={5} color="red">✕</Box>
            </Box>

            <Box flexDirection="row" justifyContent="space-between" marginBottom={10}>
              <Box flexBasis="30%">
                <label>
                  Trvanie: <br />
                  <Field type="number" min={0} step={1} name={`${prefix}.t`} style={{ width: '100%' }} required />
                </label>
              </Box>
              <Box flexBasis="30%">
                <label>
                  Deadline: <br />
                  <Field type="number" min={0} step={1} name={`${prefix}.d`} style={{ width: '100%' }} disabled={rows.length > 1} />
                </label>
              </Box>
              <Box flexBasis="30%">
                <label>
                  Váha: (1-10) <br />
                  <Field type="number" min={1} max={10} step={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} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti*/}
              {/*</Box>*/}
              <Box marginBottom={5}>
                <Box as="label" flexDirection="row">
                  Predchodcovia
                </Box>
                <Box flex={1} marginTop={5}>
                  <Select
                    multi
                    onChange={e => this.handleSelectChange('anc', e)}
                    options={existingJobs}
                    removeSelected
                    value={job.anc}
                  />
                </Box>
              </Box>
            </Box>
          </Box>
        </Box>
      );
    }
  });

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

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

  state = {
    draggingIndex: -1
  };

  onSortStart = ({ index }) => {
    this.setState({ draggingIndex: index });
  };

  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);
    this.setState({ draggingIndex: -1 });
  };

  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, allJobs } } } = this.context;
    const newJob = {
      id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: []
    };

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

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

  deleteRow = () => {
    const { index } = this.props;
    const { formik: { setFieldValue, values: { rows } } } = this.context;

    let newRows = remove(index, 1, rows);

    setFieldValue('rows', newRows);
  };

  duplicate = () => {
    const { 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, jobs: jobsCopy };

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

    setFieldValue('rows', updatedRows);
  };

  render() {
    const { index, prefix, name, jobs } = this.props;
    const { draggingIndex } = this.state;

    const existingJobs = this.findExistingJobs();

    return (
      <Box flexDirection="row" marginBottom={30} padding={10} className="row">
        <Box flexBasis="20%" flexShrink={0} paddingRight={10} flexDirection="row">
          <strong>{name}</strong>
          <Box cursor="pointer" onClick={this.deleteRow} color="red" marginLeft={5}>✕</Box>
        </Box>
        <Box flexBasis="63%" flexShrink={0}>
          <FieldArray render={() => (
            <SortableList
              helperClass="sorting"
              onSortStart={this.onSortStart}
              onSortEnd={this.onSortEnd}
              jobs={jobs}
              useDragHandle={true}
              existingJobs={existingJobs}
              prefix={`${prefix}.jobs`}
              rowIndex={index}
              draggingIndex={draggingIndex}
            />
          )} />
          <Box onClick={this.addJob} {...theme.buttons.secondary} alignSelf="flex-start">
            + Nová úloha
          </Box>
        </Box>
        <Box alignItems="center" flex={1}>
          <Box
            onClick={this.duplicate}
            {...theme.buttons.tertiary}
            flexDirection="row"
            alignItems="center"
            flexWrap="wrap"
            justifyContent="center"
            paddingVertical={1}
          >
            <span style={{ fontSize: '1.8em', paddingRight: '0.2em' }}>⩇</span> Duplikovať
          </Box>
        </Box>
      </Box>
    )
  }
}

export default Form;