import {
  setupApphierarchy,
  stubEventHandler,
  basicAppHierarchyCreator_WithFields,
  basicAppHierarchyCreator_WithFields_AndIndexes,
  hierarchyFactory,
  withFields,
} from "./specHelpers"
import { find } from "lodash"
import { addHours } from "date-fns"
import { events } from "../src/common"

describe("recordApi > validate", () => {
  it("should return errors when any fields do not parse", async () => {
    const { recordApi } = await setupApphierarchy(
      basicAppHierarchyCreator_WithFields
    )
    const record = recordApi.getNew("/customers", "customer")

    record.surname = "Ledog"
    record.isalive = "hello"
    record.age = "nine"
    record.createddate = "blah"

    const validationResult = await recordApi.validate(record)

    expect(validationResult.isValid).toBe(false)
    expect(validationResult.errors.length).toBe(3)
  })

  it("should return errors when mandatory field is empty", async () => {
    const withValidationRule = (hierarchy, templateApi) => {
      templateApi.addRecordValidationRule(hierarchy.customerRecord)(
        templateApi.commonRecordValidationRules.fieldNotEmpty("surname")
      )
    }

    const hierarchyCreator = hierarchyFactory(withFields, withValidationRule)
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")

    record.surname = ""

    const validationResult = await recordApi.validate(record)

    expect(validationResult.isValid).toBe(false)
    expect(validationResult.errors.length).toBe(1)
  })

  it("should return error when string field is beyond maxLength", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const surname = find(
        hierarchy.customerRecord.fields,
        f => f.name === "surname"
      )
      surname.typeOptions.maxLength = 5
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")
    record.surname = "more than 5 chars"

    const validationResult = await recordApi.validate(record)
    expect(validationResult.isValid).toBe(false)
    expect(validationResult.errors.length).toBe(1)
  })

  it("should return error when number field is > maxValue", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const age = find(hierarchy.customerRecord.fields, f => f.name === "age")
      age.typeOptions.maxValue = 10
      age.typeOptions.minValue = 5
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const tooOldRecord = recordApi.getNew("/customers", "customer")
    tooOldRecord.age = 11

    const tooOldResult = await recordApi.validate(tooOldRecord)
    expect(tooOldResult.isValid).toBe(false)
    expect(tooOldResult.errors.length).toBe(1)
  })

  it("should return error when number field is < minValue", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const age = find(hierarchy.customerRecord.fields, f => f.name === "age")
      age.typeOptions.minValue = 5
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const tooYoungRecord = recordApi.getNew("/customers", "customer")
    tooYoungRecord.age = 3

    const tooYoungResult = await recordApi.validate(tooYoungRecord)
    expect(tooYoungResult.isValid).toBe(false)
    expect(tooYoungResult.errors.length).toBe(1)
  })

  it("should return error when number has too many decimal places", async () => {
    const withFieldWithMaxLength = (hierarchy, templateApi) => {
      const age = find(hierarchy.customerRecord.fields, f => f.name === "age")
      age.typeOptions.decimalPlaces = 2
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")
    record.age = 3.123

    const validationResult = await recordApi.validate(record)
    expect(validationResult.isValid).toBe(false)
    expect(validationResult.errors.length).toBe(1)
  })

  it("should return error when datetime field is > maxValue", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const createddate = find(
        hierarchy.customerRecord.fields,
        f => f.name === "createddate"
      )
      createddate.typeOptions.maxValue = new Date()
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")
    record.createddate = addHours(new Date(), 1)

    const result = await recordApi.validate(record)
    expect(result.isValid).toBe(false)
    expect(result.errors.length).toBe(1)
  })

  it("should return error when number field is < minValue", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const createddate = find(
        hierarchy.customerRecord.fields,
        f => f.name === "createddate"
      )
      createddate.typeOptions.minValue = addHours(new Date(), 1)
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")
    record.createddate = new Date()

    const result = await recordApi.validate(record)
    expect(result.isValid).toBe(false)
    expect(result.errors.length).toBe(1)
  })

  it("should return error when string IS NOT one of declared values, and only declared values are allowed", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const surname = find(
        hierarchy.customerRecord.fields,
        f => f.name === "surname"
      )
      surname.typeOptions.allowDeclaredValuesOnly = true
      surname.typeOptions.values = ["thedog"]
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi } = await setupApphierarchy(hierarchyCreator)

    const record = recordApi.getNew("/customers", "customer")
    record.surname = "zeecat"

    const result = await recordApi.validate(record)
    expect(result.isValid).toBe(false)
    expect(result.errors.length).toBe(1)
  })

  it("should not return error when string IS one of declared values, and only declared values are allowed", async () => {
    const withFieldWithMaxLength = hierarchy => {
      const surname = find(
        hierarchy.customerRecord.fields,
        f => f.name === "surname"
      )
      surname.typeOptions.allowDeclaredValuesOnly = true
      surname.typeOptions.values = ["thedog"]
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi, appHierarchy } = await setupApphierarchy(
      hierarchyCreator
    )

    const record = recordApi.getNew("/customers", "customer")
    record.surname = "thedog"

    const result = await recordApi.validate(record)
    expect(result.isValid).toBe(true)
    expect(result.errors.length).toBe(0)
  })

  it("should not return error when string IS NOT one of declared values, but any values are allowed", async () => {
    const withFieldWithMaxLength = (hierarchy, templateApi) => {
      const surname = find(
        hierarchy.customerRecord.fields,
        f => f.name === "surname"
      )
      surname.typeOptions.allowDeclaredValuesOnly = false
      surname.typeOptions.values = ["thedog"]
    }

    const hierarchyCreator = hierarchyFactory(
      withFields,
      withFieldWithMaxLength
    )
    const { recordApi, appHierarchy } = await setupApphierarchy(
      hierarchyCreator
    )

    const record = recordApi.getNew("/customers", "customer")
    record.surname = "zeecat"

    const result = await recordApi.validate(record)
    expect(result.isValid).toBe(true)
    expect(result.errors.length).toBe(0)
  })

  it("should return error when reference field does not exist in options index", async () => {
    const { recordApi, appHierarchy } = await setupApphierarchy(
      basicAppHierarchyCreator_WithFields_AndIndexes
    )

    const partner = recordApi.getNew("/partners", "partner")
    partner.businessName = "ACME Inc"
    await recordApi.save(partner)

    const customer = recordApi.getNew("/customers", "customer")
    customer.partner = { key: "incorrect key", name: partner.businessName }
    const result = await await recordApi.validate(customer)
    expect(result.isValid).toBe(false)
    expect(result.errors.length).toBe(1)
  })

  it("should publish invalid events", async () => {
    const withValidationRule = (hierarchy, templateApi) => {
      templateApi.addRecordValidationRule(hierarchy.customerRecord)(
        templateApi.commonRecordValidationRules.fieldNotEmpty("surname")
      )
    }

    const hierarchyCreator = hierarchyFactory(withFields, withValidationRule)

    const { recordApi, subscribe } = await setupApphierarchy(hierarchyCreator)
    const handler = stubEventHandler()
    subscribe(events.recordApi.save.onInvalid, handler.handle)

    const record = recordApi.getNew("/customers", "customer")
    record.surname = ""

    try {
      await recordApi.save(record)
    } catch (e) {}

    const onInvalid = handler.getEvents(events.recordApi.save.onInvalid)
    expect(onInvalid.length).toBe(1)
    expect(onInvalid[0].context.record).toBeDefined()
    expect(onInvalid[0].context.record.key).toBe(record.key)
    expect(onInvalid[0].context.validationResult).toBeDefined()
  })
})