import { mount } from "enzyme";
import React from "react";
import { FluentBundle, FluentResource } from "@fluent/bundle";
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
import { RichText } from "content-src/asrouter/components/RichText/RichText.jsx";
import schema from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
import { SubmitFormSnippet } from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx";

const DEFAULT_CONTENT = {
  scene1_text: "foo",
  scene2_text: "bar",
  scene1_button_label: "Sign Up",
  retry_button_label: "Try again",
  form_action: "foo.com",
  hidden_inputs: { foo: "foo" },
  error_text: "error",
  success_text: "success",
};

describe("SubmitFormSnippet", () => {
  let sandbox;
  let onBlockStub;

  function mockL10nWrapper(content) {
    const bundle = new FluentBundle("en-US");
    for (const [id, value] of Object.entries(content)) {
      if (typeof value === "string") {
        bundle.addResource(new FluentResource(`${id} = ${value}`));
      }
    }
    const l10n = new ReactLocalization([bundle]);
    return {
      wrappingComponent: LocalizationProvider,
      wrappingComponentProps: { l10n },
    };
  }

  /**
   * mountAndCheckProps - Mounts a SubmitFormSnippet with DEFAULT_CONTENT extended with any props
   *                      passed in the content param and validates props against the schema.
   * @param {obj} content Object containing custom message content (e.g. {text, icon, title})
   * @returns enzyme wrapper for SubmitFormSnippet
   */
  function mountAndCheckProps(content = {}) {
    const props = {
      content: Object.assign({}, DEFAULT_CONTENT, content),
      onBlock: onBlockStub,
      onDismiss: sandbox.stub(),
      sendUserActionTelemetry: sandbox.stub(),
      onAction: sandbox.stub(),
      form_method: "POST",
    };
    assert.jsonSchema(props.content, schema);
    return mount(
      <SubmitFormSnippet {...props} />,
      mockL10nWrapper(props.content)
    );
  }

  beforeEach(() => {
    sandbox = sinon.createSandbox();
    onBlockStub = sandbox.stub();
  });

  afterEach(() => {
    sandbox.restore();
  });

  it("should render .text", () => {
    const wrapper = mountAndCheckProps({ scene1_text: "bar" });
    assert.equal(wrapper.find(".body").text(), "bar");
  });
  it("should not render title element if no .title prop is supplied", () => {
    const wrapper = mountAndCheckProps();
    assert.lengthOf(wrapper.find(".title"), 0);
  });
  it("should render .title", () => {
    const wrapper = mountAndCheckProps({ scene1_title: "Foo" });
    assert.equal(wrapper.find(".title").text().trim(), "Foo");
  });
  it("should render light-theme .icon", () => {
    const wrapper = mountAndCheckProps({
      scene1_icon: "data:image/gif;base64,R0lGODl",
    });
    assert.equal(
      wrapper.find(".icon-light-theme").prop("src"),
      "data:image/gif;base64,R0lGODl"
    );
  });
  it("should render dark-theme .icon", () => {
    const wrapper = mountAndCheckProps({
      scene1_icon_dark_theme: "data:image/gif;base64,R0lGODl",
    });
    assert.equal(
      wrapper.find(".icon-dark-theme").prop("src"),
      "data:image/gif;base64,R0lGODl"
    );
  });
  it("should render .button_label and default className", () => {
    const wrapper = mountAndCheckProps({ scene1_button_label: "Click here" });

    const button = wrapper.find("button.ASRouterButton");
    assert.equal(button.text(), "Click here");
    assert.equal(button.prop("className"), "ASRouterButton secondary");
  });

  describe("#SignupView", () => {
    let wrapper;
    const fetchOk = { json: () => Promise.resolve({ status: "ok" }) };
    const fetchFail = { json: () => Promise.resolve({ status: "fail" }) };

    beforeEach(() => {
      wrapper = mountAndCheckProps({
        scene1_text: "bar",
        scene2_email_placeholder_text: "Email",
        scene2_text: "signup",
      });
    });

    it("should set the input type if provided through props.inputType", () => {
      wrapper.setProps({ inputType: "number" });
      wrapper.setState({ expanded: true });
      assert.equal(wrapper.find(".mainInput").instance().type, "number");
    });

    it("should validate via props.validateInput if provided", () => {
      function validateInput(value, content) {
        if (content.country === "CA" && value === "poutine") {
          return "";
        }
        return "Must be poutine";
      }
      const setCustomValidity = sandbox.stub();
      wrapper.setProps({
        validateInput,
        content: { ...DEFAULT_CONTENT, country: "CA" },
      });
      wrapper.setState({ expanded: true });
      const input = wrapper.find(".mainInput");
      input.instance().value = "poutine";
      input.simulate("change", {
        target: { value: "poutine", setCustomValidity },
      });
      assert.calledWith(setCustomValidity, "");

      input.instance().value = "fried chicken";
      input.simulate("change", {
        target: { value: "fried chicken", setCustomValidity },
      });
      assert.calledWith(setCustomValidity, "Must be poutine");
    });

    it("should show the signup form if state.expanded is true", () => {
      wrapper.setState({ expanded: true });

      assert.isTrue(wrapper.find("form").exists());
    });
    it("should dismiss the snippet", () => {
      wrapper.setState({ expanded: true });

      wrapper.find(".ASRouterButton.secondary").simulate("click");

      assert.calledOnce(wrapper.props().onDismiss);
    });
    it("should send a DISMISS event ping", () => {
      wrapper.setState({ expanded: true });

      wrapper.find(".ASRouterButton.secondary").simulate("click");

      assert.equal(
        wrapper.props().sendUserActionTelemetry.firstCall.args[0].event,
        "DISMISS"
      );
    });
    it("should render hidden inputs + email input", () => {
      wrapper.setState({ expanded: true });

      assert.lengthOf(wrapper.find("input[type='hidden']"), 1);
    });
    it("should open the SignupView when the action button is clicked", () => {
      assert.isFalse(wrapper.find("form").exists());

      wrapper.find(".ASRouterButton").simulate("click");

      assert.isTrue(wrapper.state().expanded);
      assert.isTrue(wrapper.find("form").exists());
    });
    it("should submit telemetry when the action button is clicked", () => {
      assert.isFalse(wrapper.find("form").exists());

      wrapper.find(".ASRouterButton").simulate("click");

      assert.equal(
        wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
        "scene1-button-learn-more"
      );
    });
    it("should submit form data when submitted", () => {
      sandbox.stub(window, "fetch").resolves(fetchOk);
      wrapper.setState({ expanded: true });

      wrapper.find("form").simulate("submit");
      assert.calledOnce(window.fetch);
    });
    it("should send user telemetry when submitted", () => {
      wrapper.setState({ expanded: true });

      wrapper.find("form").simulate("submit");

      assert.equal(
        wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
        "conversion-subscribe-activation"
      );
    });
    it("should set signupSuccess when submission status is ok", async () => {
      sandbox.stub(window, "fetch").resolves(fetchOk);
      wrapper.setState({ expanded: true });
      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.equal(wrapper.state().signupSuccess, true);
      assert.equal(wrapper.state().signupSubmitted, true);
      assert.calledOnce(onBlockStub);
      assert.calledWithExactly(onBlockStub, { preventDismiss: true });
    });
    it("should send user telemetry when submission status is ok", async () => {
      sandbox.stub(window, "fetch").resolves(fetchOk);
      wrapper.setState({ expanded: true });
      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.equal(
        wrapper.props().sendUserActionTelemetry.secondCall.args[0]
          .event_context,
        "subscribe-success"
      );
    });
    it("should not block the snippet if submission failed", async () => {
      sandbox.stub(window, "fetch").resolves(fetchFail);
      wrapper.setState({ expanded: true });
      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.equal(wrapper.state().signupSuccess, false);
      assert.equal(wrapper.state().signupSubmitted, true);
      assert.notCalled(onBlockStub);
    });
    it("should not block if do_not_autoblock is true", async () => {
      sandbox.stub(window, "fetch").resolves(fetchOk);
      wrapper = mountAndCheckProps({
        scene1_text: "bar",
        scene2_email_placeholder_text: "Email",
        scene2_text: "signup",
        do_not_autoblock: true,
      });
      wrapper.setState({ expanded: true });
      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.equal(wrapper.state().signupSuccess, true);
      assert.equal(wrapper.state().signupSubmitted, true);
      assert.notCalled(onBlockStub);
    });
    it("should send user telemetry if submission failed", async () => {
      sandbox.stub(window, "fetch").resolves(fetchFail);
      wrapper.setState({ expanded: true });
      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.equal(
        wrapper.props().sendUserActionTelemetry.secondCall.args[0]
          .event_context,
        "subscribe-error"
      );
    });
    it("should render the signup success message", () => {
      wrapper.setProps({ content: { success_text: "success" } });
      wrapper.setState({ signupSuccess: true, signupSubmitted: true });

      assert.isTrue(wrapper.find(".submissionStatus").exists());
      assert.propertyVal(
        wrapper.find(RichText).props(),
        "localization_id",
        "success_text"
      );
      assert.propertyVal(
        wrapper.find(RichText).props(),
        "success_text",
        "success"
      );
      assert.isFalse(wrapper.find(".ASRouterButton").exists());
    });
    it("should render the signup error message", () => {
      wrapper.setProps({ content: { error_text: "trouble" } });
      wrapper.setState({ signupSuccess: false, signupSubmitted: true });

      assert.isTrue(wrapper.find(".submissionStatus").exists());
      assert.propertyVal(
        wrapper.find(RichText).props(),
        "localization_id",
        "error_text"
      );
      assert.propertyVal(
        wrapper.find(RichText).props(),
        "error_text",
        "trouble"
      );
      assert.isTrue(wrapper.find(".ASRouterButton").exists());
    });
    it("should render the button to return to the signup form if there was an error", () => {
      wrapper.setState({ signupSubmitted: true, signupSuccess: false });

      const button = wrapper.find("button.ASRouterButton");
      assert.equal(button.text(), "Try again");
      wrapper.find(".ASRouterButton").simulate("click");

      assert.equal(wrapper.state().signupSubmitted, false);
    });
    it("should not render the privacy notice checkbox if prop is missing", () => {
      wrapper.setState({ expanded: true });

      assert.isFalse(wrapper.find(".privacyNotice").exists());
    });
    it("should render the privacy notice checkbox if prop is provided", () => {
      wrapper.setProps({
        content: { ...DEFAULT_CONTENT, scene2_privacy_html: "privacy notice" },
      });
      wrapper.setState({ expanded: true });

      assert.isTrue(wrapper.find(".privacyNotice").exists());
    });
    it("should not call fetch if form_method is GET", async () => {
      sandbox.stub(window, "fetch").resolves(fetchOk);
      wrapper.setProps({ form_method: "GET" });
      wrapper.setState({ expanded: true });

      await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.notCalled(window.fetch);
    });
    it("should block the snippet when form_method is GET", () => {
      wrapper.setProps({ form_method: "GET" });
      wrapper.setState({ expanded: true });

      wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });

      assert.calledOnce(onBlockStub);
      assert.calledWithExactly(onBlockStub, { preventDismiss: true });
    });
    it("should return to scene 2 alt when clicking the retry button", async () => {
      wrapper.setState({ signupSubmitted: true });
      wrapper.setProps({ expandedAlt: true });

      wrapper.find(".ASRouterButton").simulate("click");

      assert.isTrue(wrapper.find(".scene2Alt").exists());
    });
  });
});
