const path = require("path")

const setupServer = require("../../../environment-helpers/setup-server")
const { useApi } = require("../../../environment-helpers/use-api")
const { useDb, initDb } = require("../../../environment-helpers/use-db")

const adminSeeder = require("../../../helpers/admin-seeder")
const promotionsSeeder = require("../../../helpers/price-selection-seeder")
const {
  Product,
  ShippingProfileType,
  ShippingProfile,
  ProductOption,
  CustomerGroup,
  PriceList,
  ProductVariant,
  Customer,
} = require("@medusajs/medusa")

jest.setTimeout(30000)

describe("Promotions", () => {
  let medusaProcess
  let dbConnection

  beforeAll(async () => {
    const cwd = path.resolve(path.join(__dirname, "..", ".."))
    dbConnection = await initDb({ cwd })
    medusaProcess = await setupServer({ cwd })
  })

  afterAll(async () => {
    const db = useDb()
    await db.shutdown()

    medusaProcess.kill()
  })

  describe("Money amount", () => {
    beforeEach(async () => {
      await adminSeeder(dbConnection)
      await promotionsSeeder(dbConnection)
    })

    afterEach(async () => {
      const db = useDb()
      await db.teardown()
    })

    it("calculated_price contains lowest price", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product?region_id=test-region")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      const lowestPrice = variant.prices.reduce(
        (prev, curr) => (curr.amount < prev ? curr.amount : prev),
        Infinity
      )

      expect(variant.calculated_price).toEqual(lowestPrice)

      expect(variant).toEqual(
        expect.objectContaining({ original_price: 120, calculated_price: 110 })
      )
    })

    it("returns no money amounts belonging to customer groups without login", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product?cart_id=test-cart")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.prices.length).toEqual(2)
      variant.prices.forEach((price) => {
        if (price.price_list) {
          expect(price.price_list.customer_groups).toEqual(undefined)
        } else {
          expect(price.price_list).toEqual(null)
        }
      })
      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-1",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
          }),
          expect.objectContaining({
            id: "test-price-3",
            region_id: "test-region",
            currency_code: "usd",
            price_list_id: "pl",
            amount: 110,
          }),
        ])
      )
    })

    it("sets default price as original price", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product?cart_id=test-cart")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )
    })

    it("gets prices for currency if no region prices exist", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product?cart_id=test-cart-2")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )
      expect(variant.prices.length).toEqual(2)
      variant.prices.forEach((price) => {
        if (price.price_list) {
          expect(price.price_list.customer_groups).toEqual(undefined)
        } else {
          expect(price.price_list).toEqual(null)
        }
      })
      variant.prices.forEach((price) => {
        expect(price.region_id).toEqual("test-region")
        expect(price.currency_code).toEqual("usd")
      })
      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-1",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
          }),
          expect.objectContaining({
            id: "test-price-3",
            region_id: "test-region",
            currency_code: "usd",
            price_list_id: "pl",
            amount: 110,
          }),
        ])
      )
    })

    it("gets prices for cart region for multi region product", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product-multi-region?cart_id=test-cart-1")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant).toEqual(
        expect.objectContaining({ original_price: 130, calculated_price: 110 })
      )
      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )

      expect(variant.prices.length).toEqual(2)

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price1-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            amount: 130,
          }),
          expect.objectContaining({
            id: "test-price3-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            price_list_id: "pl",
            amount: 110,
          }),
        ])
      )
    })

    it("gets prices for multi region product", async () => {
      const api = useApi()
      const res = await api
        .get(
          "/store/products/test-product-multi-region?region_id=test-region-2"
        )
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant).toEqual(
        expect.objectContaining({ original_price: 130, calculated_price: 110 })
      )
      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )

      expect(variant.prices.length).toEqual(2)

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price1-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            amount: 130,
          }),
          expect.objectContaining({
            id: "test-price3-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            price_list_id: "pl",
            amount: 110,
          }),
        ])
      )
    })

    it("gets prices for multi currency product", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product-multi-region?currency_code=dkk")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant).toEqual(
        expect.objectContaining({ original_price: 130, calculated_price: 110 })
      )
      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )

      expect(variant.prices.length).toEqual(2)

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price1-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            amount: 130,
          }),
          expect.objectContaining({
            id: "test-price3-region-2",
            region_id: "test-region-2",
            currency_code: "dkk",
            price_list_id: "pl",
            amount: 110,
          }),
        ])
      )
    })

    it("gets moneyamounts only with valid date interval", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product-sale?cart_id=test-cart")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      const date = new Date()

      expect(variant.prices.length).toEqual(2)
      variant.prices.forEach((price) => {
        if (price.starts_at) {
          expect(new Date(price.starts_at).getTime()).toBeLessThan(
            date.getTime()
          )
        }
        if (price.ends_at) {
          expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
            date.getTime()
          )
        }
      })
    })

    it("gets moneyamounts with valid date intervals and finds lowest price with overlapping intervals", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product-sale-overlap?cart_id=test-cart")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      const date = new Date()

      expect(variant).toEqual(
        expect.objectContaining({
          original_price: 150,
          calculated_price: 120,
        })
      )
      expect(variant.prices.length).toEqual(3)
      variant.prices.forEach((price) => {
        if (price.starts_at) {
          expect(new Date(price.starts_at).getTime()).toBeLessThan(
            date.getTime()
          )
        }
        if (price.ends_at) {
          expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
            date.getTime()
          )
        }
      })

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-sale-overlap-1",
            region_id: "test-region",
            currency_code: "usd",
            amount: 140,
            price_list_id: "pl_current_1",
          }),
          expect.objectContaining({
            id: "test-price1-sale-overlap",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
            price_list_id: "pl_current",
          }),
          expect.objectContaining({
            id: "test-price2-sale-overlap-default",
            region_id: "test-region",
            currency_code: "usd",
            amount: 150,
          }),
        ])
      )
    })

    it("gets all prices with varying quantity limits with no quantity", async () => {
      const api = useApi()
      const res = await api
        .get("/store/products/test-product-quantity?cart_id=test-cart")
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.prices.length).toEqual(5)
      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-quantity",
            region_id: "test-region",
            currency_code: "usd",
            amount: 100,
            price_list_id: "pl",
            min_quantity: 10,
            max_quantity: 100,
          }),
          expect.objectContaining({
            id: "test-price1-quantity",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
            price_list_id: "pl",
            min_quantity: 101,
            max_quantity: 1000,
          }),
          expect.objectContaining({
            id: "test-price2-quantity",
            region_id: "test-region",
            currency_code: "usd",
            amount: 130,
            price_list_id: "pl",
            max_quantity: 9,
          }),
          expect.objectContaining({
            id: "test-price3-quantity-now",
            region_id: "test-region",
            currency_code: "usd",
            amount: 140,
            price_list_id: "pl_current",
            min_quantity: 101,
            max_quantity: 1000,
          }),
          expect.objectContaining({
            id: "test-price3-quantity-default",
            region_id: "test-region",
            currency_code: "usd",
            amount: 150,
          }),
        ])
      )

      expect(variant.calculated_price).toEqual(130)
      expect(variant.original_price).toEqual(150)
      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )
    })

    it("fetches product with groups in money amounts with login", async () => {
      const api = useApi()

      // customer with customer-group 5
      const authResponse = await api.post("/store/auth", {
        email: "test5@email.com",
        password: "test",
      })

      const [authCookie] = authResponse.headers["set-cookie"][0].split(";")

      const res = await api
        .get("/store/products/test-product?cart_id=test-cart", {
          headers: {
            Cookie: authCookie,
          },
        })
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.prices.length).toEqual(3)

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-1",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
          }),
          expect.objectContaining({
            id: "test-price-3",
            region_id: "test-region",
            currency_code: "usd",
            price_list_id: "pl",
            amount: 110,
          }),
          expect.objectContaining({
            id: "test-price",
            region_id: "test-region",
            currency_code: "usd",
            amount: 100,
            price_list: expect.objectContaining({}),
          }),
        ])
      )
    })

    it("should only join price lists with customer groups for the customer, not empty groups", async () => {
      const api = useApi()

      const authResponse = await api.post("/store/auth", {
        email: "test5@email-pl.com",
        password: "test",
      })

      const [authCookie] = authResponse.headers["set-cookie"][0].split(";")

      const res = await api
        .get("/store/products/test-product-pl?cart_id=test-cart", {
          headers: {
            Cookie: authCookie,
          },
        })
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.prices.length).toEqual(2)

      expect(variant).toEqual(
        expect.objectContaining({
          id: "test-variant-pl",
          calculated_price: 110,
        })
      )

      expect(variant.prices.length).toEqual(2)
      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price1120",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
          }),
          expect.objectContaining({
            id: "test-price2120",
            region_id: "test-region",
            currency_code: "usd",
            price_list_id: "pl_current_pl",
            amount: 110,
          }),
        ])
      )
    })

    it("fetches product with groups and quantities in money amounts with login", async () => {
      const api = useApi()

      // customer with customer-group 5
      const authResponse = await api.post("/store/auth", {
        email: "test5@email.com",
        password: "test",
      })

      const [authCookie] = authResponse.headers["set-cookie"][0].split(";")

      const res = await api
        .get(
          "/store/products/test-product-quantity-customer?cart_id=test-cart",
          {
            headers: {
              Cookie: authCookie,
            },
          }
        )
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      expect(variant.prices.length).toEqual(6)
      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price-quantity-customer",
            region_id: "test-region",
            currency_code: "usd",
            amount: 100,
            min_quantity: 10,
            max_quantity: 100,
          }),
          expect.objectContaining({
            id: "test-price1-quantity-customer",
            region_id: "test-region",
            currency_code: "usd",
            amount: 120,
            min_quantity: 101,
            max_quantity: 1000,
          }),
          expect.objectContaining({
            id: "test-price2-quantity-customer",
            region_id: "test-region",
            currency_code: "usd",
            amount: 130,
            max_quantity: 9,
          }),
          expect.objectContaining({
            id: "test-price2-quantity-customer-group",
            region_id: "test-region",
            currency_code: "usd",
            amount: 100,
            max_quantity: 9,
            price_list: expect.objectContaining({}),
          }),
          expect.objectContaining({
            id: "test-price3-quantity-customer-now",
            region_id: "test-region",
            currency_code: "usd",
            amount: 140,
            min_quantity: 101,
            max_quantity: 1000,
          }),
          expect.objectContaining({
            id: "test-price3-quantity-customer-default",
            region_id: "test-region",
            currency_code: "usd",
            amount: 150,
            price_list_id: null,
          }),
        ])
      )

      expect(variant.calculated_price).toEqual(100)
      expect(variant.original_price).toEqual(150)
      expect(variant.original_price).toEqual(
        variant.prices.find((p) => p.price_list_id === null).amount
      )
    })

    it("gets moneyamounts only with valid date interval for customer", async () => {
      const api = useApi()

      // customer with customer-group 5
      const authResponse = await api.post("/store/auth", {
        email: "test5@email.com",
        password: "test",
      })

      const [authCookie] = authResponse.headers["set-cookie"][0].split(";")

      const res = await api
        .get("/store/products/test-product-sale-customer?cart_id=test-cart", {
          headers: {
            Cookie: authCookie,
          },
        })
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      const date = new Date()

      expect(variant).toEqual(
        expect.objectContaining({
          original_price: 150,
          calculated_price: 100,
        })
      )

      expect(variant.prices.length).toEqual(2)
      variant.prices.forEach((price) => {
        if (price.starts_at) {
          expect(new Date(price.starts_at).getTime()).toBeLessThan(
            date.getTime()
          )
        }
        if (price.ends_at) {
          expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
            date.getTime()
          )
        }
      })
    })

    it("gets moneyamounts only with valid date interval for customer regardless of quantity limits", async () => {
      const api = useApi()

      // customer with customer-group 5
      const authResponse = await api.post("/store/auth", {
        email: "test5@email.com",
        password: "test",
      })

      const [authCookie] = authResponse.headers["set-cookie"][0].split(";")

      const res = await api
        .get(
          "/store/products/test-product-sale-customer-quantity?cart_id=test-cart",
          {
            headers: {
              Cookie: authCookie,
            },
          }
        )
        .catch((error) => console.log(error))

      const variant = res.data.product.variants[0]

      const date = new Date()

      expect(variant).toEqual(
        expect.objectContaining({
          original_price: 150,
          calculated_price: 100,
        })
      )

      expect(variant.prices.length).toEqual(3)
      variant.prices.forEach((price) => {
        if (price.starts_at) {
          expect(new Date(price.starts_at).getTime()).toBeLessThan(
            date.getTime()
          )
        }
        if (price.ends_at) {
          expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
            date.getTime()
          )
        }
      })

      expect(variant.prices).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-price1-sale-customer-quantity-groups",
            region_id: "test-region",
            currency_code: "usd",
            amount: 100,
            max_quantity: 99,
            price_list: expect.objectContaining({}),
          }),
          expect.objectContaining({
            id: "test-price2-sale-customer-quantity-default",
            region_id: "test-region",
            currency_code: "usd",
            amount: 150,
          }),
          expect.objectContaining({
            id: "test-price1-sale-customer-quantity",
            region_id: "test-region",
            currency_code: "usd",
            amount: 110,
            max_quantity: 99,
          }),
        ])
      )
    })
  })
})
