import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { createRefetchContainer, graphql } from 'react-relay';
import get from 'lodash.get';

import CirclePlusIcon from 'dibs-vg/dist/react/circle-plus';
import { Input } from 'dibs-elements/exports/Input';
import { Button } from 'dibs-elements/exports/Button';
import { Select } from 'dibs-elements/exports/Select';
import { Spinner } from 'dibs-elements/exports/Spinner';

import CreateProjectMutation from '../mutations/createProjectMutation';
import { postProjectToFA } from './faPoster';

import css from '../styles.scss';

export const STATUSES = {
    CREATE_PROJECT: 'CREATE_PROJECT',
    DEFAULT: 'DEFAULT',
    LOADING: 'LOADING',
};

const messages = defineMessages({
    createProjectPlaceholder: {
        id: 'dc.requestShippingModal.RequestModalSelectProject.select__project_placeholder',
        defaultMessage: 'Enter Project Name',
    },
});

const projectToProjectOption = project => ({
    value: project.serviceId,
    label: project.name,
});

export class RequestModalSelectProject extends Component {
    constructor(props) {
        super(props);

        this.createProject = this.createProject.bind(this);
        this.exitProjectCreation = this.exitProjectCreation.bind(this);
        this.getProjects = this.getProjects.bind(this);
        this.handleProjectCreationError = this.handleProjectCreationError.bind(this);
        this.handleProjectCreationSuccess = this.handleProjectCreationSuccess.bind(this);
        this.handleSelection = this.handleSelection.bind(this);
        this.renderInput = this.renderInput.bind(this);

        this.state = {
            errorMessage: '',
            newProjectName: '',
            status: STATUSES.DEFAULT,
        };
    }

    createProject(event) {
        event.preventDefault();
        const { newProjectName } = this.state;
        const { relay, user } = this.props;

        if (newProjectName) {
            this.setState({ status: STATUSES.LOADING });
            CreateProjectMutation.commit(relay.environment, {
                name: newProjectName,
                userId: user.serviceId,
                onCompleted: this.handleProjectCreationSuccess,
                onError: this.handleProjectCreationError,
            });
        }
    }

    exitProjectCreation() {
        this.setState({
            errorMessage: '',
            newProjectName: '',
            status: STATUSES.DEFAULT,
        });
    }

    getProjects() {
        const { viewer } = this.props;
        const projectsEdges = get(viewer, 'projects.edges') || [];
        return projectsEdges.map(e => e.node);
    }

    handleProjectCreationSuccess(response, errors = []) {
        if (errors.length) {
            this.handleProjectCreationError(errors);
            return;
        }

        const { onProjectSelection, relay, user } = this.props;
        const newProject = get(response, 'createProject.project');
        const refetchVars = { userIds: [user.serviceId] };

        postProjectToFA({
            action: 'CREATE',
            firmId: get(user, 'tradeFirm.serviceId'),
            project: newProject,
            source: 'bulk_shipping_modal',
            userId: get(user, 'serviceId'),
        });

        relay.refetch(
            refetchVars,
            null,
            () => {
                onProjectSelection(newProject);
                this.setState({
                    errorMessage: '',
                    newProjectName: '',
                    status: STATUSES.DEFAULT,
                });
            },
            { force: true }
        );
    }

    handleProjectCreationError(error) {
        const { newProjectName } = this.state;

        let errors;
        if (Array.isArray(error)) {
            errors = error;
        } else {
            errors = get(error, 'source.errors') || [];
        }

        const isProjectNameCollision = !!errors.find(err => get(err, 'requestInfo.status') === 409);

        const errorMessage = isProjectNameCollision
            ? `You already have a project called "${newProjectName}"`
            : 'Error: could not create project';

        this.setState({
            errorMessage,
            status: STATUSES.CREATE_PROJECT,
        });

        console.error('Project creation failed', error);
    }

    handleSelection(selectedProjectOption) {
        let selectedProject = null;
        if (selectedProjectOption) {
            const projects = this.getProjects();
            selectedProject = projects.find(p => p.serviceId === selectedProjectOption.value);
        }
        this.props.onProjectSelection(selectedProject);
    }

    renderInput() {
        const { intl, selectedProject } = this.props;
        const { errorMessage, newProjectName, status } = this.state;

        const selectedProjectOption = selectedProject
            ? projectToProjectOption(selectedProject)
            : null;
        const projectOptions = this.getProjects().map(projectToProjectOption);

        const clearIcon = (
            <div
                className={css.clearIcon}
                onClick={this.exitProjectCreation}
                data-tn="clear-new-project-input"
            >
                <CirclePlusIcon />
            </div>
        );

        switch (status) {
            case STATUSES.LOADING:
                return (
                    <div className={css.loadingProjectContainer}>
                        <Spinner dark />
                        <span className={css.loadingProjectLabel}>
                            <FormattedMessage
                                id="dc.requestShippingModal.RequestModalSelectProject.loading_new_project"
                                defaultMessage="Creating Project"
                            />
                        </span>
                    </div>
                );
            case STATUSES.CREATE_PROJECT:
                return (
                    <form className={css.createProjectForm} onSubmit={this.createProject}>
                        <Input
                            dataTn="project-name-input"
                            errorMessage={errorMessage || null}
                            onChange={e => this.setState({ newProjectName: e.target.value })}
                            leftDecorator={clearIcon}
                            maxLength={31}
                            placeholder={intl.formatMessage(messages.createProjectPlaceholder)}
                            value={newProjectName}
                        />
                        <Button
                            className={css.submitNewProject}
                            dataTn="create-project-submit-button"
                            type="submit"
                        >
                            <FormattedMessage
                                id="dc.requestShippingModal.RequestModalSelectProject.save_new_project"
                                defaultMessage="Save"
                            />
                        </Button>
                    </form>
                );
            default:
                return (
                    <div>
                        <Select
                            clearable={!!selectedProject}
                            dataTn="select-project-shipping-modal"
                            name="select-project-shipping-modal"
                            onChange={this.handleSelection}
                            options={projectOptions}
                            placeholder={
                                <FormattedMessage
                                    id="dc.requestShippingModal.RequestModalSelectProject.select_placeholder"
                                    defaultMessage="Select project"
                                />
                            }
                            searchable
                            value={selectedProjectOption}
                        />
                        <div
                            className={css.createProjectCta}
                            onClick={() => this.setState({ status: STATUSES.CREATE_PROJECT })}
                        >
                            <FormattedMessage
                                id="dc.requestShippingModal.RequestModalSelectProject.create_project_cta"
                                defaultMessage="+ Create New Project"
                            />
                        </div>
                    </div>
                );
        }
    }

    render() {
        return (
            <div className={css.selectProjectWrapper}>
                <div className={css.bodyHeaderWrapper}>
                    <span className={css.bodyHeader}>
                        <FormattedMessage
                            id="dc.requestShippingModal.RequestModalSelectProject.header"
                            defaultMessage="Which projects are you considering this piece for?"
                        />
                    </span>
                    &nbsp;
                    <FormattedMessage
                        id="dc.requestShippingModal.RequestModalSelectProject.optional_text"
                        defaultMessage="(optional)"
                    />
                </div>
                {this.renderInput()}
            </div>
        );
    }
}

RequestModalSelectProject.propTypes = {
    intl: PropTypes.object.isRequired,
    onProjectSelection: PropTypes.func.isRequired,
    relay: PropTypes.object.isRequired,
    selectedProject: PropTypes.shape({
        serviceId: PropTypes.string,
        name: PropTypes.string,
    }),
    user: PropTypes.object,
    viewer: PropTypes.object,
};

RequestModalSelectProject = injectIntl(RequestModalSelectProject);
export default createRefetchContainer(
    RequestModalSelectProject,
    {
        viewer: graphql`
            fragment RequestModalSelectProject_viewer on Viewer {
                projects(userIds: $userIds, first: 200) @include(if: $hasUserId) {
                    edges {
                        node {
                            serviceId
                            name
                            portfolio {
                                serviceId
                            }
                        }
                    }
                }
            }
        `,
        user: graphql`
            fragment RequestModalSelectProject_user on User {
                serviceId
                tradeFirm {
                    serviceId
                }
            }
        `,
    },
    graphql`
        query RequestModalSelectProjectRefetchQuery(
            $userIds: [String]
            $hasUserId: Boolean = true
        ) {
            viewer {
                ...RequestModalSelectProject_viewer
            }
        }
    `
);
