import _ from 'underscore'
import React from 'react'
import ReactDOM from 'react-dom'
import ReactTestUtils from 'react-addons-test-utils'
import ProposedTimePicker from '../lib/calendar/proposed-time-picker'
import TestProposalDataSource from './test-proposal-data-source'
import WeekView from '../../../src/components/nylas-calendar/week-view'
import ProposedTimeCalendarStore from '../lib/proposed-time-calendar-store'

import {NylasCalendar} from 'nylas-component-kit'

import {activate, deactivate} from '../lib/main'

const now = window.testNowMoment

/**
 * This tests the ProposedTimePicker as an integration test of the picker,
 * associated calendar object, the ProposedTimeCalendarStore, and stubbed
 * ProposedTimeCalendarDataSource
 *
 */
describe("ProposedTimePicker", () => {
  beforeEach(() => {
    spyOn(NylasEnv, "getWindowType").andReturn("calendar");
    spyOn(WeekView.prototype, "_now").andReturn(now());
    spyOn(NylasCalendar.prototype, "_now").andReturn(now());
    activate()

    this.testSrc = new TestProposalDataSource()
    spyOn(ProposedTimePicker.prototype, "_dataSource").andReturn(this.testSrc)
    this.picker = ReactTestUtils.renderIntoDocument(
      <ProposedTimePicker />
    )
    this.weekView = ReactTestUtils.findRenderedComponentWithType(this.picker, WeekView);
  });

  afterEach(() => {
    deactivate()
  })

  it("renders a proposed time picker in week view", () => {
    const picker = ReactTestUtils.findRenderedComponentWithType(this.picker, ProposedTimePicker);
    const weekView = ReactTestUtils.findRenderedComponentWithType(this.picker, WeekView);
    expect(picker instanceof ProposedTimePicker).toBe(true);
    expect(weekView instanceof WeekView).toBe(true);
  });

  // NOTE: We manually fire the SchedulerActions since we've tested the
  // mouse click to time conversion in the nylas-calendar

  it("creates a proposal on click", () => {
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    expect(ProposedTimeCalendarStore.proposals().length).toBe(1)
    expect(ProposedTimeCalendarStore.proposalsAsEvents().length).toBe(1)
    const proposals = $("proposal");
    const events = $("calendar-event");
    expect(events.length).toBe(1);
    expect(proposals.length).toBe(1);

    // It's not an availability block but a full blown proposal
    expect($("availability").length).toBe(0);
  });

  it("creates the time picker for the correct timespan", () => {
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    const title = $("title");
    expect(ReactDOM.findDOMNode(title[0]).innerHTML).toBe("March 13 - March 19 2016");
  });

  it("creates a block of proposals on drag down", () => {
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseMove({
      time: now().add(30, 'minutes'),
      mouseIsDown: true,
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseMove({
      time: now().add(60, 'minutes'),
      mouseIsDown: true,
      currentView: NylasCalendar.WEEK_VIEW,
    })
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);

    // Ensure that we don't see any proposals
    expect(ProposedTimeCalendarStore.proposals().length).toBe(0)
    let proposalEls = $("proposal");
    expect(proposalEls.length).toBe(0);

    // But we DO see the drag block event
    expect(ProposedTimeCalendarStore.proposalsAsEvents().length).toBe(1)
    let events = $("calendar-event");
    expect($("availability").length).toBe(1);
    expect(events.length).toBe(1);

    this.picker._onCalendarMouseUp({
      time: now().add(90, 'minutes'),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // Now that we've moused up, this should convert them into proposals
    const proposals = ProposedTimeCalendarStore.proposals()
    expect(proposals.length).toBe(3)
    expect(ProposedTimeCalendarStore.proposalsAsEvents().length).toBe(3)
    proposalEls = $("proposal");
    events = $("calendar-event");
    expect(events.length).toBe(3);
    expect(proposalEls.length).toBe(3);

    const times = proposals.map((p) =>
      [p.start, p.end]
    );

    expect(times).toEqual([
      [now().unix(), now().add(30, 'minutes').unix() - 1],
      [now().add(30, 'minutes').unix(),
       now().add(60, 'minutes').unix() - 1],
      [now().add(60, 'minutes').unix(),
       now().add(90, 'minutes').unix() - 1],
    ]);
  });

  it("creates a block of proposals on drag up", () => {
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseMove({
      time: now().subtract(30, 'minutes'),
      mouseIsDown: true,
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseMove({
      time: now().subtract(60, 'minutes'),
      mouseIsDown: true,
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now().subtract(90, 'minutes'),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    const proposals = ProposedTimeCalendarStore.proposals()
    const times = proposals.map((p) =>
      [p.start, p.end]
    );

    expect(times).toEqual([
      [now().subtract(90, 'minutes').unix(),
       now().subtract(60, 'minutes').unix() - 1],
      [now().subtract(60, 'minutes').unix(),
       now().subtract(30, 'minutes').unix() - 1],
      [now().subtract(30, 'minutes').unix(),
       now().unix() - 1],
    ]);
  });

  it("removes proposals when clicked on", () => {
    // This created a proposal
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // See the proposal is there
    expect(ProposedTimeCalendarStore.proposals().length).toBe(1)

    // Now let's find and click it.
    // This also tests to make sure it actually rendered
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    const removeBtn = $("rm-time proposal");
    expect(removeBtn.length).toBe(1)
    ReactTestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(removeBtn[0]))

    // Now see that it's gone!
    expect(ProposedTimeCalendarStore.proposals().length).toBe(0)
    // And gone from the DOM too.
    expect($("proposal").length).toBe(0);
    // And didn't turn into an availability block or something dumb
    expect($("availability").length).toBe(0);
  });

  it("can clear all of the proposals", () => {
    // This created a proposal
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // See the proposal is there
    expect(ProposedTimeCalendarStore.proposals().length).toBe(1)

    // Find the clear button
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    const clearBtns = $("clear-proposed-times");
    expect(clearBtns.length).toBe(1);

    // Click it
    ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(clearBtns[0]))

    // Ensure no more proposals
    expect(ProposedTimeCalendarStore.proposals().length).toBe(0)
    // And nothing still rendered
    expect($("proposal").length).toBe(0);
    expect($("availability").length).toBe(0);
  });

  it("can change the duration", () => {
    // Find the duration picker.
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    const pickerEls = $("duration-picker-select");
    expect(pickerEls.length).toBe(1);

    // Starts with default duration
    const d30Min = ProposedTimeCalendarStore.DURATIONS[0]
    expect(ProposedTimeCalendarStore._duration).toEqual(d30Min)

    const pickerEl = ReactDOM.findDOMNode(pickerEls[0]);
    pickerEl.value = "1.5|hours|1½ hr"
    ReactTestUtils.Simulate.change(pickerEl)

    const dHrHalf = ProposedTimeCalendarStore.DURATIONS[2]
    dHrHalf[0] = `${dHrHalf[0]}` // convert to string
    expect(ProposedTimeCalendarStore._duration).toEqual(dHrHalf)
  });

  it("creates a block of proposals with a longer duration", () => {
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);

    // Create a single proposal with the default 30 min duration.
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // It's 30 min long
    const proposals = ProposedTimeCalendarStore.proposals()
    const times = proposals.map((p) => [p.start, p.end]);
    expect(times).toEqual([
      [now().unix(),
       now().add(30, 'minutes').unix() - 1],
    ]);

    // Change duration to 2.5 hours
    const pickerEl = ReactDOM.findDOMNode($("duration-picker-select")[0]);
    pickerEl.value = "2.5|hours|2½ hr"
    ReactTestUtils.Simulate.change(pickerEl)

    // Click a new event
    this.picker._onCalendarMouseDown({
      time: now().add(2, 'hours'),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now().add(2, 'hours'),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // It should have added a 2.5 hour long event and left the original
    // event alone
    const p2 = ProposedTimeCalendarStore.proposals()
    const t2 = p2.map((p) => [p.start, p.end]);
    expect(t2).toEqual([
      [now().unix(),
       now().add(30, 'minutes').unix() - 1],
      [now().add(2, 'hours').unix(),
       now().add(4.5, 'hours').unix() - 1],
    ]);
  });

  it("overrides events so they don't overlap", () => {
    const $ = _.partial(ReactTestUtils.scryRenderedDOMComponentsWithClass, this.picker);
    this.picker._onCalendarMouseDown({
      time: now(),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now().add(1, 'hour'),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // Creates two proposals.
    const proposals = ProposedTimeCalendarStore.proposals()
    const times = proposals.map((p) => [p.start, p.end]);
    expect(times).toEqual([
      [now().unix(),
       now().add(30, 'minutes').unix() - 1],
      [now().add(30, 'minutes').unix(),
       now().add(60, 'minutes').unix() - 1],
    ]);

    // Change the duration to 2 hours
    const pickerEl = ReactDOM.findDOMNode($("duration-picker-select")[0]);
    pickerEl.value = "2|hours|2 hr"
    ReactTestUtils.Simulate.change(pickerEl)

    // Click and drag overlapping the first of the original events.
    this.picker._onCalendarMouseDown({
      time: now().subtract(1.5, 'hours'),
      currentView: NylasCalendar.WEEK_VIEW,
    })
    this.picker._onCalendarMouseUp({
      time: now().add(20, 'minutes'),
      currentView: NylasCalendar.WEEK_VIEW,
    })

    // See that there's only 1 new event with the correct time and it
    // exhchanged it with the old one.
    //
    // It left the non overlapping one alone.
    const p2 = ProposedTimeCalendarStore.proposals()
    const t2 = p2.map((p) => [p.start, p.end]);
    expect(t2).toEqual([
      [now().add(30, 'minutes').unix(),
       now().add(60, 'minutes').unix() - 1],
      [now().subtract(1.5, 'hours').unix(),
       now().add(30, 'minutes').unix() - 1],
    ]);
  });
});
