import { isDefined, join, fieldDefinitions, $ } from "../src/common"
import { getMemoryTemplateApi } from "./specHelpers"
import { fieldErrors } from "../src/templateApi/fields"

const getRecordTemplate = templateApi =>
  $(templateApi.getNewRootLevel(), [templateApi.getNewRecordTemplate])

const getValidField = templateApi => {
  const field = templateApi.getNewField("string")
  field.name = "forename"
  field.label = "forename"
  return field
}

const testMemberIsNotSet = membername => async () => {
  const { templateApi } = await getMemoryTemplateApi()
  const field = getValidField(templateApi)
  field[membername] = ""
  const errorsNotSet = templateApi.validateField([field])(field)
  expect(errorsNotSet.length).toBe(1)
  expect(errorsNotSet[0].error.includes("is not set")).toBeTruthy()
}

const testMemberIsNotDefined = membername => async () => {
  const { templateApi } = await getMemoryTemplateApi()
  const field = getValidField(templateApi)
  delete field[membername]
  const errorsNotSet = templateApi.validateField([field])(field)
  expect(errorsNotSet.length).toBe(1)
  expect(errorsNotSet[0].error.includes("is not set")).toBeTruthy()
}

describe("validateField", () => {
  it("should return error when name is not set", testMemberIsNotSet("name"))

  it(
    "should return error when name is not defined",
    testMemberIsNotDefined("name")
  )

  it("should return error when type is not set", testMemberIsNotSet("type"))

  it(
    "should return error when type is not defined",
    testMemberIsNotDefined("type")
  )

  it(
    "should return error when label is not defined",
    testMemberIsNotDefined("label")
  )

  it(
    "should return error when getInitialValue is not defined",
    testMemberIsNotDefined("getInitialValue")
  )

  it(
    "should return error when getInitialValue is not set",
    testMemberIsNotSet("getInitialValue")
  )

  it(
    "should return error when getUndefinedValue is not defined",
    testMemberIsNotDefined("getUndefinedValue")
  )

  it(
    "should return error when getUndefinedValue is not set",
    testMemberIsNotSet("getUndefinedValue")
  )

  it("should return no errors when valid field is supplied", async () => {
    const { templateApi } = await getMemoryTemplateApi()
    const field = getValidField(templateApi)
    const errors = templateApi.validateField([field])(field)
    expect(errors.length).toBe(0)
  })

  it("should return error when field with same name exists already", async () => {
    const { templateApi } = await getMemoryTemplateApi()
    const field1 = getValidField(templateApi)
    field1.name = "surname"

    const field2 = getValidField(templateApi)
    field2.name = "surname"
    const errors = templateApi.validateField([field1, field2])(field2)
    expect(errors.length).toBe(1)
    expect(errors[0].error).toBe("field name is duplicated")
    expect(errors[0].field).toBe("name")
  })

  it("should return error when field is not one of allowed types", async () => {
    const { templateApi } = await getMemoryTemplateApi()
    const field = getValidField(templateApi)
    field.type = "sometype"
    const errors = templateApi.validateField([field])(field)
    expect(errors.length).toBe(1)
    expect(errors[0].error).toBe("type is unknown")
    expect(errors[0].field).toBe("type")
  })
})

describe("addField", () => {
  it("should throw exception when field is invalid", async () => {
    const { templateApi } = await getMemoryTemplateApi()
    const record = getRecordTemplate(templateApi)
    const field = getValidField(templateApi)
    field.name = ""
    expect(() => templateApi.addField(record, field)).toThrow(
      new RegExp("^" + fieldErrors.AddFieldValidationFailed, "i")
    )
  })

  it("should add field when field is valid", async () => {
    const { templateApi } = await getMemoryTemplateApi()
    const record = getRecordTemplate(templateApi)
    const field = getValidField(templateApi)
    field.name = "some_new_field"
    templateApi.addField(record, field)
    expect(record.fields.length).toBe(1)
    expect(record.fields[0]).toBe(field)
  })
})