import MockedCollectionFactory from "../__mock__/MockedCollectionFactory";
import DependentAttribute from "../DependentAttribute";

import StringAttributeModel from "../../StringAttributeModel";
import { ConfigurationException } from "../../../../exceptions";

describe("depending attribute - visibility", () => {
  it("needs to check hint applicability", () => {
    expect(DependentAttribute.checkHintApplicability).toBe(true);
  });

  it("is applicable for every attribute", () => {
    expect(
      DependentAttribute.isApplicableForAttribute(new StringAttributeModel()),
    ).toBe(true);
  });

  it("checks applicability for hint", () => {
    expect(DependentAttribute.isApplicableForHint("bla")).toBe(false);
    expect(DependentAttribute.isApplicableForHint("dependent-attribute")).toBe(
      false,
    );
    expect(
      DependentAttribute.isApplicableForHint("dependent-attribute:show sss"),
    ).toBe(true);
    expect(
      DependentAttribute.isApplicableForHint("dependent-attribute: show sss"),
    ).toBe(true);
    expect(
      DependentAttribute.isApplicableForHint("dependent-attribute:hide sss"),
    ).toBe(true);
    expect(
      DependentAttribute.isApplicableForHint("dependent-attribute:enable sss"),
    ).toBe(false);
  });

  it("throws on invalid hint", () => {
    expect(() => {
      DependentAttribute.initHint("dependent-attribute:bla");
    }).toThrow(ConfigurationException);
  });

  it("parse layout hint on init", () => {
    const showEquals = DependentAttribute.initHint(
      "dependent-attribute:show when dependent-control:controlA equals code1",
    );

    expect(showEquals).toBeInstanceOf(DependentAttribute);
    expect(showEquals.action).toBe("show");
    expect(showEquals.control).toBe("controlA");
    expect(showEquals.operator).toBe("equals");
    expect(showEquals.options).toStrictEqual(["code1"]);

    const showEqualsExtraSpaces = DependentAttribute.initHint(
      "dependent-attribute: show when dependent-control: controlA equals code1",
    );

    expect(showEqualsExtraSpaces).toBeInstanceOf(DependentAttribute);
    expect(showEqualsExtraSpaces.action).toBe("show");
    expect(showEqualsExtraSpaces.control).toBe("controlA");
    expect(showEqualsExtraSpaces.operator).toBe("equals");
    expect(showEqualsExtraSpaces.options).toStrictEqual(["code1"]);

    const hideTwoOptions = DependentAttribute.initHint(
      "dependent-attribute:hide when dependent-control:controlA includes code1|code2",
    );

    expect(hideTwoOptions).toBeInstanceOf(DependentAttribute);
    expect(hideTwoOptions.action).toBe("hide");
    expect(hideTwoOptions.control).toBe("controlA");
    expect(hideTwoOptions.operator).toBe("includes");
    expect(hideTwoOptions.options).toStrictEqual(["code1", "code2"]);

    const hideTwoOptionsSpaces = DependentAttribute.initHint(
      "dependent-attribute:hide when dependent-control:controlA includes code1 | code2",
    );

    expect(hideTwoOptionsSpaces).toBeInstanceOf(DependentAttribute);
    expect(hideTwoOptionsSpaces.action).toBe("hide");
    expect(hideTwoOptionsSpaces.control).toBe("controlA");
    expect(hideTwoOptionsSpaces.operator).toBe("includes");
    expect(hideTwoOptionsSpaces.options).toStrictEqual(["code1", "code2"]);
  });

  const expectVisible = (mockedCollection, key) => {
    expect(mockedCollection.getAttributeByKey(key).isVisible).toBe(true);
    expect(mockedCollection.getAttributeByKey(key).isHidden).toBe(false);
  };

  const expectHidden = (mockedCollection, key) => {
    expect(mockedCollection.getAttributeByKey(key).isVisible).toBe(false);
    expect(mockedCollection.getAttributeByKey(key).isHidden).toBe(true);
  };

  it("returns visible when no visibility specific hint is set", () => {
    const mockedCollectionFactory = new MockedCollectionFactory();

    mockedCollectionFactory.addControlAttribute(
      "attributeControl",
      ["code1", "code2", "code3", "code4", "code5", "code6"],
      ["code2", "code4", "code6"],
      ["dependent-control: Control A"],
    );
    mockedCollectionFactory.addDependentAttribute("attributeUnknownHint", [
      "unknown-hint",
    ]);
    mockedCollectionFactory.addDependentAttribute(
      "attributeHasNonExistingControl",
      [
        "dependent-attribute: show when dependent-control: Control B equals code2",
      ],
    );

    const mockedCollection = mockedCollectionFactory.createCollection();

    expectVisible(mockedCollection, "attributeUnknownHint");
    expectVisible(mockedCollection, "attributeHasNonExistingControl");
  });

  it("returns visible", () => {
    const mockedCollectionFactory = new MockedCollectionFactory();

    mockedCollectionFactory.addControlAttribute(
      "attributeControl",
      ["code1", "code2"],
      ["code2"],
      ["dependent-control: Control A"],
    );
    mockedCollectionFactory.addDependentAttribute("showEqualsSelected", [
      "dependent-attribute:show when dependent-control: Control A equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("hideEqualsNotSelected", [
      "dependent-attribute:hide when dependent-control:Control A equals code1",
    ]);
    mockedCollectionFactory.addDependentAttribute("showIncludesSelected", [
      "dependent-attribute:show when dependent-control: Control A includes code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("hideIncludesNotSelected", [
      "dependent-attribute:hide when dependent-control: Control A includes code1",
    ]);

    mockedCollectionFactory.addDependentAttribute("showNotEqualsSelected", [
      "dependent-attribute:show when dependent-control: Control A notEquals code1",
    ]);
    mockedCollectionFactory.addDependentAttribute("hideNotEqualsNotSelected", [
      "dependent-attribute:hide when dependent-control: Control A notEquals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("showNotIncludesSelected", [
      "dependent-attribute:show when dependent-control: Control A notIncludes code1",
    ]);
    mockedCollectionFactory.addDependentAttribute(
      "hideNotIncludesNotSelected",
      [
        "dependent-attribute: hide when dependent-control: Control A notIncludes code2",
      ],
    );

    const mockedCollection = mockedCollectionFactory.createCollection();

    expectVisible(mockedCollection, "showEqualsSelected");
    expectVisible(mockedCollection, "hideEqualsNotSelected");
    expectVisible(mockedCollection, "showIncludesSelected");
    expectVisible(mockedCollection, "hideIncludesNotSelected");
    expectVisible(mockedCollection, "showNotEqualsSelected");
    expectVisible(mockedCollection, "hideNotEqualsNotSelected");
    expectVisible(mockedCollection, "showNotIncludesSelected");
    expectVisible(mockedCollection, "hideNotIncludesNotSelected");
  });

  it("returns hidden", () => {
    const mockedCollectionFactory = new MockedCollectionFactory();

    mockedCollectionFactory.addControlAttribute(
      "attributeControl",
      ["code1", "code2"],
      ["code2"],
      ["dependent-control: Control A"],
    );
    mockedCollectionFactory.addDependentAttribute("showEqualsNotSelected", [
      "dependent-attribute:show when dependent-control: Control A equals code1",
    ]);
    mockedCollectionFactory.addDependentAttribute("hideEqualsSelected", [
      "dependent-attribute:hide when dependent-control: Control A equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("showIncludesNotSelected", [
      "dependent-attribute:show when dependent-control: Control A includes code1",
    ]);
    mockedCollectionFactory.addDependentAttribute("hideIncludesSelected", [
      "dependent-attribute:hide when dependent-control: Control A includes code2",
    ]);

    const mockedCollection = mockedCollectionFactory.createCollection();

    expectHidden(mockedCollection, "showEqualsNotSelected");
    expectHidden(mockedCollection, "hideEqualsSelected");
    expectHidden(mockedCollection, "showIncludesNotSelected");
    expectHidden(mockedCollection, "hideIncludesSelected");
  });

  it("can handle an attribute that is dependend on an other dependent attribute", () => {
    const mockedCollectionFactory = new MockedCollectionFactory();

    mockedCollectionFactory.addControlAttribute(
      "attributeControlA",
      ["code1", "code2"],
      ["code2"],
      ["dependent-control: Control A"],
    );
    mockedCollectionFactory.addControlAttribute(
      "attributeControlB",
      ["code1", "code2"],
      ["code2"],
      [
        "dependent-control: Control B",
        "dependent-attribute:show when dependent-control: Control A equals code2",
      ],
    );
    mockedCollectionFactory.addControlAttribute(
      "attributeControlC",
      ["code1", "code2"],
      ["code2"],
      [
        "dependent-control: Control C",
        "dependent-attribute:show when dependent-control: Control B equals code2",
      ],
    );
    mockedCollectionFactory.addControlAttribute(
      "attributeControlD",
      ["code1", "code2"],
      ["code2"],
      [
        "dependent-control: Control D",
        "dependent-attribute:show when dependent-control: Control C equals code1",
      ],
    );
    mockedCollectionFactory.addControlAttribute(
      "attributeControlE",
      ["code1", "code2"],
      ["code2"],
      [
        "dependent-control: Control E",
        "dependent-attribute:show when dependent-control: Control D equals code2",
      ],
    );

    mockedCollectionFactory.addDependentAttribute("show1", [
      "dependent-attribute:show when dependent-control: Control A equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("show2", [
      "dependent-attribute:show when dependent-control: Control B equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("show3", [
      "dependent-attribute:show when dependent-control: Control C equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("show4", [
      "dependent-attribute:show when dependent-control: Control D equals code2",
    ]);
    mockedCollectionFactory.addDependentAttribute("show5", [
      "dependent-attribute:show when dependent-control: Control E equals code2",
    ]);

    const mockedCollection = mockedCollectionFactory.createCollection();

    expectVisible(mockedCollection, "show1");
    expectVisible(mockedCollection, "show2");
    expectVisible(mockedCollection, "show3");
    expectVisible(mockedCollection, "attributeControlB");
    expectVisible(mockedCollection, "attributeControlC");

    expectHidden(mockedCollection, "show4");
    expectHidden(mockedCollection, "show5");
  });

  it("can handle an attribute with multiple dependent attributes", () => {
    const mockedCollectionFactory = new MockedCollectionFactory();

    mockedCollectionFactory.addControlAttribute(
      "attributeControlA",
      ["code1", "code2"],
      [],
      ["dependent-control: Control A"],
    );

    mockedCollectionFactory.addControlAttribute(
      "attributeControlB",
      ["code1", "code2"],
      [],
      ["dependent-control: Control B"],
    );

    mockedCollectionFactory.addDependentAttribute("depending", [
      "dependent-attribute:show when dependent-control: Control A equals code1",
      "dependent-attribute:show when dependent-control: Control B equals code1",
    ]);

    const mockedCollection = mockedCollectionFactory.createCollection();

    expectHidden(mockedCollection, "depending");

    mockedCollection.getAttributeByKey("attributeControlA").update("code1");
    mockedCollection.processLayoutHintRules();

    expectHidden(mockedCollection, "depending");

    mockedCollection.getAttributeByKey("attributeControlB").update("code1");
    mockedCollection.processLayoutHintRules();

    expectVisible(mockedCollection, "depending");
  });
});
