import { shallow, mount } from 'enzyme';
import sinon from 'sinon';
import React from 'react';
import ReactDOM from 'react-dom';
import { TextInput } from 'web-foundation';
import {
    defaultPlaceholders,
    defaultAltPlaceholders
} from 'web-foundation/atoms/text-input/text-input.jsx';

import FieldKit from 'field-kit/dist/field-kit';
import OnlyNumberFormatter from '../../etc/only-number-formatter';
import OnlyMoneyFormatter from '../../etc/only-money-formatter';
const TextFormatter = FieldKit.Formatter;
const PhoneFormatter = FieldKit.PhoneFormatter;
const SSNFormatter = FieldKit.SocialSecurityNumberFormatter;
const CardFormatter = FieldKit.AdaptiveCardFormatter;

describe('<TextInput />', function() {
    it('should make an element', function() {
        const wrapper = shallow(<TextInput />);

        expect(wrapper.hasClass('text-input')).to.be.true;
        expect(wrapper.find('input')).to.have.length(1);
        expect(wrapper.find('label')).to.have.length(0);

        wrapper.find('input').simulate('focus');
        wrapper.find('input').simulate('blur');
    });

    it('should allow for a label', function() {
        const wrapper = shallow(
            <TextInput
                label="This is a label" />
        );

        expect(wrapper.find('label')).to.have.length(1);
        expect(wrapper.find('label').text()).to.equal('This is a label');
    });

    it('should show the placeholder in the input', function() {
        const wrapper = shallow(
            <TextInput
                placeholder="This is a placeholder" />
        );

        expect(wrapper.find('input').prop('placeholder'))
            .to.equal('This is a placeholder');
    });

    it('should show the alt placeholder on focus', function() {
        const blurCallback = sinon.spy(),
            focusCallback = sinon.spy(),
            wrapper = shallow(
                <TextInput
                    format="text"
                    altPlaceholder="This is an alt placeholder"
                    focus={ focusCallback }
                    blur={ blurCallback } />
            );

        expect(wrapper.find('input').prop('placeholder'))
            .to.equal('');

        wrapper.find('input').simulate('focus');

        expect(wrapper.find('input').prop('placeholder'))
            .to.equal('This is an alt placeholder');

        wrapper.find('input').simulate('blur');

        expect(wrapper.find('input').prop('placeholder'))
            .to.equal('');
        expect(focusCallback.calledOnce).to.be.true;
        expect(blurCallback.calledOnce).to.be.true;
    });

    it('should have default placeholders for password', function() {
        const wrapper = shallow(<TextInput format="password"/>);

        expect(wrapper.find('input').prop('placeholder'))
            .to.equal('Password');
    });

    it('should have default placeholders & alt for some formats', function() {
        const formats = [ 'email', 'phone', 'card', 'ssn', 'website' ],
            assertions = [];
        let wrapper;

        formats.forEach((format) => {
            assertions.push({
                format,
                placeholder: defaultPlaceholders[format],
                alt: defaultAltPlaceholders[format]
            });
        });

        for (const assertion of assertions) {
            wrapper = shallow(<TextInput format={ assertion.format } />);

            expect(wrapper.find('input').prop('placeholder'))
                .to.equal(assertion.placeholder);

            wrapper.find('input').simulate('focus');

            expect(wrapper.find('input').prop('placeholder'))
                .to.equal(assertion.alt);
        }
    });

    it('should set appropriate type for some formats', function() {
        const wrapperPassword = shallow(<TextInput format="password" />),
            wrapperPhone = shallow(<TextInput format="phone" />),
            wrapperWebsite = shallow(<TextInput format="website" />);

        expect(wrapperPassword.find('input').prop('type'))
            .to.equal('password');

        expect(wrapperPhone.find('input').prop('type'))
            .to.equal('tel');

        expect(wrapperWebsite.find('input').prop('type'))
            .to.equal('url');
    });

    it('should set appropriate inputmode for some formats', function() {
        const wrapperCard = shallow(<TextInput format="card" />),
            wrapperEmail = shallow(<TextInput format="email" />),
            wrapperSSN = shallow(<TextInput format="ssn" />);

        expect(wrapperCard.find('input').prop('inputMode'))
            .to.equal('numeric');

        expect(wrapperEmail.find('input').prop('inputMode'))
            .to.equal('email');

        expect(wrapperSSN.find('input').prop('inputMode'))
            .to.equal('numeric');
    });

    it('should display a value', function() {
        const wrapper = mount(
                <TextInput value="test value" />
            ),
            node = ReactDOM.findDOMNode(wrapper.instance());

        expect(node.getElementsByTagName('input')[0].value)
            .to.equal('test value');
    });

    // currently this is untestable through dom manipulation
    it('should update the value', function() {
        const updateSpy = sinon.spy(),
            wrapperOne = mount(
                <TextInput value="test value"
                    update={ updateSpy } />
            ),
            wrapperTwo = mount(
                <TextInput value="test value" />
            );

        wrapperOne.instance().update({ value() { return 'beans'; } });
        wrapperTwo.instance().update({ value() { return 'beans'; } });
        expect(updateSpy.callCount).to.equal(1);
    });

    it('should add a class for special types', function() {
        const wrapperOne = mount(<TextInput format="money" />),
            wrapperTwo = mount(<TextInput format="number" />),
            nodeOne = ReactDOM.findDOMNode(wrapperOne.instance()),
            nodeTwo = ReactDOM.findDOMNode(wrapperTwo.instance());

        expect(nodeOne.classList.contains('money')).to.be.true;
        expect(nodeTwo.classList.contains('number')).to.be.true;

        wrapperOne.setProps({ format: 'number' });
        wrapperTwo.setProps({ format: 'money' });

        expect(nodeOne.classList.contains('money')).to.be.false;
        expect(nodeOne.classList.contains('number')).to.be.true;

        expect(nodeTwo.classList.contains('number')).to.be.false;
        expect(nodeTwo.classList.contains('money')).to.be.true;
    });

    it('should have a button for password format', function() {
        const wrapper = mount(
            <TextInput format="password" />
        );

        expect(wrapper.find('button')).to.have.length(1);
    });

    it('should toggle input type for passwords when clicked', function() {
        const wrapper = mount(
            <TextInput format="password" />
        );

        expect(wrapper.find('input').is('input[type="password"]')).to.be.true;

        wrapper.find('button').simulate('click');

        expect(wrapper.find('input').is('input[type="text"]')).to.be.true;
    });

    it('should allow a value rewrite through props', function() {
        const updateSpy = sinon.spy(),
            wrapper = mount(
                <TextInput value="test value"
                    update={ updateSpy } />
            ),
            node = ReactDOM.findDOMNode(wrapper.instance());

        wrapper.setProps({ value: 'beans' });

        expect(updateSpy.callCount).to.equal(0);
        expect(node.getElementsByTagName('input')[0].value)
            .to.equal('beans');
    });

    it('should ignore that if it has focus', function() {
        const updateSpy = sinon.spy(),
            wrapper = mount(
                <TextInput value="test value"
                    update={ updateSpy } />
            ),
            node = ReactDOM.findDOMNode(wrapper.instance());

        wrapper.find('input').simulate('focus');
        wrapper.setProps({ value: 'beans' });

        expect(updateSpy.callCount).to.equal(0);
        expect(node.getElementsByTagName('input')[0].value)
            .to.equal('test value');
    });

    it('should return all the things', function() {
        const wrapper = shallow(
            <TextInput value="test value" />
        );

        expect(wrapper.instance().getFormatter())
            .to.be.an.instanceOf(TextFormatter);
        expect(wrapper.instance().getFormatter({ format: 'testing' }))
            .to.be.an.instanceOf(TextFormatter);
        expect(wrapper.instance().getFormatter({ format: 'phone' }))
            .to.be.an.instanceOf(PhoneFormatter);
        expect(wrapper.instance().getFormatter({ format: 'ssn' }))
            .to.be.an.instanceOf(SSNFormatter);
        expect(wrapper.instance().getFormatter({ format: 'card' }))
            .to.be.an.instanceOf(CardFormatter);
        expect(wrapper.instance().getFormatter({ format: 'number' }))
            .to.be.an.instanceOf(OnlyNumberFormatter);
        expect(wrapper.instance().getFormatter({ format: 'float' }))
            .to.be.an.instanceOf(OnlyNumberFormatter);
        expect(wrapper.instance().getFormatter({ format: 'money' }))
            .to.be.an.instanceOf(OnlyMoneyFormatter);
    });

    it('should initialize with focus if told to', function() {
        let wrapper = mount(
            <TextInput value="test value" />
        );

        expect(wrapper.state('focused')).to.be.false;

        wrapper = mount(
            <TextInput value="test value"
                setFocus />
        );

        expect(wrapper.state('focused')).to.be.true;
    });

    it('should apply a \'disabled\' class', function() {
        const wrapper = shallow(<TextInput disabled />);

        expect(wrapper.hasClass('disabled')).to.be.true;
    });

    it('should acknowledge an error through props', function() {
        const wrapper = shallow(
                <TextInput
                    error="error message" />
            );

        expect(wrapper.hasClass('has-error')).to.be.true;
    });
});
