import React from 'react';
import { block } from 'bem-cn';
import { Button, TextInput, RadioButton } from 'lego-on-react';
import { i18n } from 'i18n2';
import directory from 'api2/directory';
import { SessionStore, ConfigStore, BalanceStore } from 'lib2/stores';
import getOrganization from 'lib2/getOrganization';
import getHelpUrl from 'lib2/getHelpUrl';
import hasPermission from 'lib2/hasPermission';
import storage from 'lib2/storage';

import Promocode from 'components2/Promocode';
import Form from 'components2/Form';
import PersonId from 'components2/PersonId';
import InlineError from 'components2/InlineError';

import naturalPersonValidator from './naturalPersonValidator';
import legalPersonValidator from './legalPersonValidator';

import './index.css';

const FormFieldsMap = {
    natural: [
        'last_name',
        'first_name',
        'middle_name',
        'phone',
        'email',
    ],
    legal: [
        'long_name',
        'phone',
        'email',
        'postal_code',
        'postal_address',
        'legal_address',
        'inn',
        'kpp',
        'bik',
        'account',
    ],
};

const ValidatorMap = {
    natural: naturalPersonValidator,
    legal: legalPersonValidator,
};

const NewPersonId = {
    person_id: 'new-person-id',
    first_name: i18n('subscription.new_person_id.name'),
};

function getFormKey(personType) {
    return `form.${personType}_person.${SessionStore.get('user.id')}`;
}

function getStorageKey(key) {
    return `${key}.${SessionStore.get('user.id')}`;
}

function getDefaultNaturalValues(storedValues) {
    const user = SessionStore.get('user');
    const phoneContact = user.contacts.find(({ type }) => type === 'phone');

    return Object.assign({
        first_name: user.name.first,
        last_name: user.name.last,
        middle_name: user.name.middle,
        phone: phoneContact && phoneContact.value,
        email: user.email,
    }, storedValues);
}
function getDefaultLegalValues(storedValues) {
    const org = getOrganization();

    return Object.assign({
        long_name: org.name,
        phone: org.phone_number,
        email: org.email,
    }, storedValues);
}

function getFormErrors(data, validator) {
    return Object
        .entries(validator)
        .reduce((acc, [key, value]) => {
            const error = value.method && value.method(data);

            if (error) {
                acc[key] = [error];
            }

            return acc;
        }, {});
}

function getDefaultDescription() {
    const { searchParams } = new URL(window.location.href);
    const source = searchParams.get('source');
    const reason = searchParams.get('reason');

    return reason === 'no_active_person_found' ?
        i18n('subscription.form.inactive.description') :
        i18n(`subscription.form.${source}.description`) || i18n('subscription.form.default.description');
}

const b = block('create-contract-form');

export default class CreateContractForm extends React.PureComponent {
    constructor(props) {
        super(props);

        const persons = props.persons;
        const personType = storage.session.getItem(getStorageKey('person_type')) || 'natural';
        const personId = storage.session.getItem(getStorageKey('person_id')) ||
            (persons.length ? String(persons[0].person_id) : NewPersonId.person_id);

        this.state = {
            personType,
            busy: false,
            natural: getDefaultNaturalValues(storage.session.getItem(getFormKey('natural'))),
            legal: getDefaultLegalValues(storage.session.getItem(getFormKey('legal'))),
            personId,
            errors: {},
        };

        this._handlePersonIdChange = this._handlePersonIdChange.bind(this);
        this._handlePersonTypeChange = this._handlePersonTypeChange.bind(this);
        this._renderFormField = this._renderFormField.bind(this);
        this._submit = this._submit.bind(this);
        this._storeFormData = this._storeFormData.bind(this);
    }

    componentWillUnmount() {
        this._unmounted = true;
        this._storeFormData();
        window.removeEventListener('beforeunload', this._storeFormData);
    }

    componentWillMount() {
        window.addEventListener('beforeunload', this._storeFormData);
    }

    _storeFormData() {
        const { legal, natural, personType, personId } = this.state;

        storage.session.setItem(getStorageKey('person_type'), personType);
        storage.session.setItem(getStorageKey('person_id'), personId);
        storage.session.setItem(getFormKey('legal'), legal);
        storage.session.setItem(getFormKey('natural'), natural);
    }

    _handlePersonTypeChange({ target: { value } }) {
        this.setState({
            personType: value,
            errors: {},
        });
    }

    _handlePersonIdChange({ target: { value } }) {
        this.setState({
            personId: value,
            errors: {},
        });
    }

    _isNewPersonId() {
        return this.state.personId === NewPersonId.person_id;
    }

    _onInputChange(key, text) {
        const { personType } = this.state;

        this.setState(prevState => ({ [personType]: { ...prevState[personType], [key]: text } }));
    }

    _submit() {
        const { personType, personId } = this.state;
        const { onSubmit, persons } = this.props;
        let data = this.state[personType];

        // Если создаем нового плательщика, то надо провалидировать поля
        if (this._isNewPersonId()) {
            const errors = getFormErrors(data, ValidatorMap[personType]);

            this.setState({
                errors,
            });

            if (errors && Object.keys(errors).length) {
                return;
            }

            data.person_type = personType;
        } else {
            const person = persons.find(details => String(details.person_id) === personId);

            data = {
                person_id: person.person_id,
                person_type: person.type,
            };
        }

        this.setState({ busy: true });

        directory
            .send('POST', '/v11/subscription/create-contract-info/', {
                body: JSON.stringify(data),
            })
            .then(({ ok, status, body = {} }) => {
                if (this._unmounted) {
                    return;
                }

                if (!ok && body.code !== 'organization_already_has_contract') {
                    this.setState({
                        errors: {
                            _common: i18n(`backend_errors.subscription.contract.${body.code}`) ||
                                i18n(`backend_errors.${body.code}`) ||
                                i18n('common.error.generic', { code: status }),
                        },
                    });

                    return;
                }

                return new Promise(resolve => {
                    let counter = 0;

                    const pollContract = () => directory.send('GET', '/v11/subscription/')
                        .then(({ body: balance }) => {
                            if (balance.has_contract) {
                                BalanceStore.mergeState(balance);

                                return resolve(true);
                            }

                            setTimeout(pollContract, 2000);
                        })
                        .finally(() => {
                            if (counter++ > 50) {
                                return resolve(false);
                            }
                        });

                    pollContract();
                })
                    .then(isContractCreated => {
                        if (onSubmit) {
                            onSubmit(isContractCreated);
                        }
                    });
            })
            .finally(() => {
                if (!this._unmounted) {
                    this.setState({ busy: false });
                }
            });
    }

    _renderFormField(key) {
        const { personType, errors } = this.state;
        const validator = ValidatorMap[personType][key];
        const required = validator && validator.required;
        const fieldErrors = errors && errors[key];

        return (
            <Form.Field
                key={key}
                name={key}
            >
                <Form.Label required={required}>
                    {i18n(`subscription.${personType}_person.${key}`)}
                </Form.Label>
                <Form.Value>
                    <TextInput
                        size="m"
                        autoComplete={false}
                        theme="normal"
                        hasClear
                        name={key}
                        onChange={this._onInputChange.bind(this, key)}
                        placeholder={i18n(`subscription.${personType}_person.placeholder.${key}`)}
                        text={this.state[personType][key]}
                    />
                    <Form.Error>
                        {Boolean(fieldErrors) && fieldErrors.map(err => <div key={String(err)}>{err}</div>)}
                    </Form.Error>
                </Form.Value>
            </Form.Field>
        );
    }

    render() {
        const { personType, busy, personId, errors } = this.state;
        const {
            persons,
            title = i18n('subscription.form.title'),
            description = getDefaultDescription(),
            helpUrl = getHelpUrl('ui.help.troubleshooting'),
            offerUrl = ConfigStore.get('ui.help.offer'),
            footnote,
        } = this.props;

        const hasPersons = persons && persons.length !== 0;

        return (
            <div className={b()}>
                <div className={b('content')}>
                    {Boolean(title) && (
                        <div className={b('title')}>
                            {title}
                        </div>
                    )}
                    {Boolean(description) && (
                        <div className={b('description')}>
                            {description}
                        </div>
                    )}
                    <Form
                        cls={b('form')}
                        onSubmit={this._submit}
                    >
                        {hasPersons && (
                            <React.Fragment>
                                <div className={b('section-title')}>
                                    {i18n('subscription.person_id.title')}
                                </div>
                                <PersonId
                                    persons={persons.concat(NewPersonId)}
                                    value={personId}
                                    onChange={this._handlePersonIdChange}
                                />
                            </React.Fragment>
                        )}
                        {this._isNewPersonId() && (
                            <React.Fragment>
                                <div className={b('section-title')}>
                                    {i18n('subscription.person_type.title')}
                                </div>
                                <div className={b('person-type')}>
                                    <RadioButton
                                        theme="normal"
                                        view="default"
                                        tone="default"
                                        size="m"
                                        value={personType}
                                        onChange={this._handlePersonTypeChange}
                                    >
                                        <RadioButton.Radio
                                            value="natural"
                                            cls={b('radio', { type: 'natural' })}
                                        >
                                            {i18n('subscription.entity_type.natural_person')}
                                        </RadioButton.Radio>
                                        <RadioButton.Radio
                                            value="legal"
                                            cls={b('radio', { type: 'legal' })}
                                        >
                                            {i18n('subscription.entity_type.legal_person')}
                                        </RadioButton.Radio>
                                    </RadioButton>
                                </div>
                            </React.Fragment>
                        )}
                        {this._isNewPersonId() && (
                            <React.Fragment>
                                <div className={b('section-title')}>
                                    {i18n('subscription.person_data.title')}
                                </div>
                                {FormFieldsMap[personType].map(this._renderFormField)}
                            </React.Fragment>
                        )}
                    </Form>
                    {hasPermission('activate_promocode') && (
                        <React.Fragment>
                            <div className={b('section-title')}>
                                {i18n('balance.promocode.subtitle')}
                            </div>
                            <Promocode cls={b('promo-code')} />
                        </React.Fragment>
                    )}
                    <div className={b('footer')}>
                        <div
                            className={b('terms')}
                            dangerouslySetInnerHTML={{
                                __html: i18n('subscription.payment.auto_agreement', {
                                    offer_url: offerUrl,
                                }),
                            }}
                        />
                        {errors._common && (
                            <InlineError cls={b('error')}>
                                {errors._common}
                            </InlineError>
                        )}
                        <div className={b('controls')}>
                            <Button
                                progress={busy}
                                onClick={this._submit}
                                text={i18n('common.action.save')}
                                theme="action"
                                size="m"
                                type="submit"
                            />
                        </div>
                    </div>
                </div>
                {Boolean(helpUrl) && (
                    <div
                        className={b('help')}
                        dangerouslySetInnerHTML={{ __html: i18n('subscription.form.help', {
                            url: helpUrl,
                        }) }}
                    />
                )}
                {Boolean(footnote) && (
                    <div
                        className={b('footnote')}
                        dangerouslySetInnerHTML={{ __html: footnote }}
                    />
                )}
            </div>
        );
    }
}
