defmodule WraftDoc.ContentTypes.ContentTypeTest do
  use WraftDoc.ModelCase, async: true
  import WraftDoc.Factory
  @moduletag :document
  alias WraftDoc.ContentTypes.ContentType

  @valid_attrs %{
    name: "Offer letter",
    description: "A letter issued by Employer to employee in the time of joinig",
    prefix: "OFFR",
    organisation_id: Faker.UUID.v4(),
    flow_id: Faker.UUID.v4(),
    theme_id: Faker.UUID.v4(),
    layout_id: Faker.UUID.v4(),
    creator_id: Faker.UUID.v4()
  }
  @invalid_attrs %{name: "ofer letter"}

  describe "changeset/2" do
    test "returns a valid changeset with valid attributes" do
      organisation = insert(:organisation)
      valid_attrs = Map.merge(@valid_attrs, %{organisation_id: organisation.id})
      changeset = ContentType.changeset(%ContentType{}, valid_attrs)
      assert changeset.valid?
    end

    test "returns an invalid changeset with invalid attributes" do
      changeset = ContentType.changeset(%ContentType{}, @invalid_attrs)
      refute changeset.valid?
    end

    test "all field EXCEPT :descirption, :color and other autogenerated are required" do
      changeset = ContentType.changeset(%ContentType{}, %{})
      refute changeset.valid?

      for field <-
            ContentType.__schema__(:fields) --
              [:id, :description, :color, :inserted_at, :updated_at] do
        assert "can't be blank" in errors_on(changeset, field)
      end
    end

    test "checks content type name is unique" do
      %{id: user_id} = insert(:user)
      %{id: organisation_id} = organisation = insert(:organisation)
      %{id: theme_id} = insert(:theme, organisation: organisation)
      %{id: flow_id} = insert(:flow, organisation: organisation)
      %{id: layout_id} = insert(:layout, organisation: organisation)

      attrs =
        Map.merge(
          @valid_attrs,
          %{
            organisation_id: organisation_id,
            theme_id: theme_id,
            flow_id: flow_id,
            layout_id: layout_id,
            creator_id: user_id
          }
        )

      {:ok, _content_type} =
        %ContentType{}
        |> ContentType.changeset(attrs)
        |> Repo.insert()

      {:error, changeset} =
        %ContentType{}
        |> ContentType.changeset(attrs)
        |> Repo.insert()

      assert "Content type with the same name under your organisation exists.!" in errors_on(
               changeset,
               :name
             )
    end

    test "returns an valid changeset with valid color format" do
      organisation = insert(:organisation)
      params_a = Map.merge(@valid_attrs, %{color: "#FF3323", organisation_id: organisation.id})
      changeset_a = ContentType.changeset(%ContentType{}, params_a)
      assert changeset_a.valid?
      params_b = Map.merge(@valid_attrs, %{color: "#ff3", organisation_id: organisation.id})
      changeset_b = ContentType.changeset(%ContentType{}, params_b)
      assert changeset_b.valid?
    end

    test "returns an invalid changeset with invalid color format" do
      params1 = Map.put(@valid_attrs, :color, "#SF3323")
      changeset1 = ContentType.changeset(%ContentType{}, params1)
      refute changeset1.valid?
      assert "has invalid format" in errors_on(changeset1, :color)

      params2 = Map.put(@valid_attrs, :color, "FF3323")
      changeset2 = ContentType.changeset(%ContentType{}, params2)
      refute changeset2.valid?
      assert "has invalid format" in errors_on(changeset2, :color)
    end

    test "returns invalid changeset with invalid length for prefix field" do
      params1 = Map.put(@valid_attrs, :prefix, "D")
      changeset1 = ContentType.changeset(%ContentType{}, params1)
      refute changeset1.valid?

      assert "should be at least 2 character(s)" in errors_on(changeset1, :prefix)

      params2 = Map.put(@valid_attrs, :prefix, "ABCDEFG")
      changeset2 = ContentType.changeset(%ContentType{}, params2)
      refute changeset2.valid?
      assert "should be at most 6 character(s)" in errors_on(changeset2, :prefix)
    end
  end

  describe "update_changeset/2" do
    test "returns a valid changeset with valid attributes" do
      organisation = insert(:organisation)
      %{id: theme_id} = insert(:theme, organisation: organisation)
      %{id: flow_id} = insert(:flow, organisation: organisation)
      %{id: layout_id} = insert(:layout, organisation: organisation)
      content_type = insert(:content_type, organisation: organisation)

      attrs =
        @valid_attrs
        |> Map.merge(%{theme_id: theme_id, flow_id: flow_id, layout_id: layout_id})
        |> Map.drop([:organisation_id, :creator_id])

      changeset = ContentType.update_changeset(content_type, attrs)
      assert changeset.valid?
    end

    test "returns an invalid changeset with invalid attributes" do
      content_type = insert(:content_type)
      attrs = Map.put(@invalid_attrs, :theme_id, nil)

      changeset = ContentType.update_changeset(content_type, attrs)
      refute changeset.valid?
    end

    test "all field EXCEPT :descirption, :color, :organisation_id, :creator_id, and other autogenerated are required" do
      content_type = insert(:content_type)

      attrs =
        Map.merge(
          @invalid_attrs,
          %{name: nil, prefix: nil, flow_id: nil, theme_id: nil, layout_id: nil, creator_id: nil}
        )

      changeset = ContentType.update_changeset(content_type, attrs)
      refute changeset.valid?

      for field <-
            ContentType.__schema__(:fields) --
              [
                :id,
                :description,
                :color,
                :organisation_id,
                :creator_id,
                :inserted_at,
                :updated_at
              ] do
        assert "can't be blank" in errors_on(changeset, field)
      end
    end

    test "checks content type name is unique" do
      organisation = insert(:organisation)
      insert(:content_type, name: "Offer letter", organisation: organisation)
      content_type = insert(:content_type, name: "New Offer letter", organisation: organisation)

      {:error, changeset} =
        content_type
        |> ContentType.update_changeset(%{name: "Offer letter"})
        |> Repo.update()

      assert "Content type with the same name under your organisation exists.!" in errors_on(
               changeset,
               :name
             )
    end

    test "returns a valid changeset with valid color format" do
      content_type = insert(:content_type)

      changeset1 = ContentType.update_changeset(content_type, %{color: "#FF3323"})
      assert changeset1.valid?

      changeset2 = ContentType.update_changeset(content_type, %{color: "#ff3"})
      assert changeset2.valid?
    end

    test "returns an invalid changeset with invalid color format" do
      content_type = insert(:content_type)

      changeset1 = ContentType.update_changeset(content_type, %{color: "#SF3323"})
      refute changeset1.valid?
      assert "has invalid format" in errors_on(changeset1, :color)

      changeset2 = ContentType.update_changeset(content_type, %{color: "FF3323"})
      refute changeset2.valid?
      assert "has invalid format" in errors_on(changeset2, :color)
    end

    test "returns invalid changeset with invalid length for prefix field" do
      content_type = insert(:content_type)

      params1 = Map.put(@valid_attrs, :prefix, "D")
      changeset1 = ContentType.changeset(content_type, params1)
      refute changeset1.valid?

      assert "should be at least 2 character(s)" in errors_on(changeset1, :prefix)

      params2 = Map.put(@valid_attrs, :prefix, "ABCDEFG")
      changeset2 = ContentType.changeset(content_type, params2)
      refute changeset2.valid?
      assert "should be at most 6 character(s)" in errors_on(changeset2, :prefix)
    end

    test "returns invalid changeset if theme, flow, and/or layout doesn't belong to the same organisation as content_type" do
      content_type = insert(:content_type)
      %{id: theme_id} = insert(:theme)
      %{id: flow_id} = insert(:flow)
      %{id: layout_id} = insert(:layout)

      attrs =
        @valid_attrs
        |> Map.merge(%{theme_id: theme_id, flow_id: flow_id, layout_id: layout_id})
        |> Map.drop([:organisation_id, :creator_id])

      changeset = ContentType.update_changeset(content_type, attrs)
      refute changeset.valid?

      for field <- [:theme_id, :layout_id, :flow_id] do
        assert "is invalid" in errors_on(changeset, field)
      end
    end
  end
end
