const path = require("path")
const {
  Region,
  ShippingProfile,
  ShippingOption,
  ShippingOptionRequirement,
} = require("@medusajs/medusa")

const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const shippingOptionSeeder = require("../../helpers/shipping-option-seeder")

jest.setTimeout(30000)

describe("/admin/shipping-options", () => {
  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("POST /admin/shipping-options/:id", () => {
    beforeEach(async () => {
      try {
        await adminSeeder(dbConnection)
        await shippingOptionSeeder(dbConnection)
      } catch (err) {
        console.error(err)
        throw err
      }
    })

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

    it("updates a shipping option with no existing requirements", async () => {
      const api = useApi()

      const payload = {
        name: "Test option",
        amount: 100,
        requirements: [
          {
            type: "min_subtotal",
            amount: 1,
          },
          {
            type: "max_subtotal",
            amount: 2,
          },
        ],
      }

      const res = await api.post(`/admin/shipping-options/test-out`, payload, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      const requirements = res.data.shipping_option.requirements

      expect(res.status).toEqual(200)
      expect(requirements.length).toEqual(2)
      expect(requirements[0]).toEqual(
        expect.objectContaining({
          type: "min_subtotal",
          shipping_option_id: "test-out",
          amount: 1,
        })
      )
      expect(requirements[1]).toEqual(
        expect.objectContaining({
          type: "max_subtotal",
          shipping_option_id: "test-out",
          amount: 2,
        })
      )
    })

    it("fails as it is not allowed to set id from client side", async () => {
      const api = useApi()

      const payload = {
        name: "Test option",
        amount: 100,
        requirements: [
          {
            id: "not_allowed",
            type: "min_subtotal",
            amount: 1,
          },
          {
            id: "really_not_allowed",
            type: "max_subtotal",
            amount: 2,
          },
        ],
      }

      const res = await api
        .post(`/admin/shipping-options/test-out`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
        .catch((err) => {
          return err.response
        })

      expect(res.status).toEqual(400)
      expect(res.data.message).toEqual("ID does not exist")
    })

    it("it successfully updates a set of existing requirements", async () => {
      const api = useApi()

      const payload = {
        requirements: [
          {
            id: "option-req",
            type: "min_subtotal",
            amount: 15,
          },
          {
            id: "option-req-2",
            type: "max_subtotal",
            amount: 20,
          },
        ],
        amount: 200,
      }

      const res = await api
        .post(`/admin/shipping-options/test-option-req`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
        .catch((err) => {
          console.log(err.response.data.message)
        })

      expect(res.status).toEqual(200)
    })

    it("it successfully updates a set of existing requirements by updating one and deleting the other", async () => {
      const api = useApi()

      const payload = {
        requirements: [
          {
            id: "option-req",
            type: "min_subtotal",
            amount: 15,
          },
        ],
      }

      const res = await api
        .post(`/admin/shipping-options/test-option-req`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
        .catch((err) => {
          console.log(err.response.data.message)
        })

      expect(res.status).toEqual(200)
    })

    it("successfully updates a set of requirements because max. subtotal >= min. subtotal", async () => {
      const api = useApi()

      const payload = {
        requirements: [
          {
            id: "option-req",
            type: "min_subtotal",
            amount: 150,
          },
          {
            id: "option-req-2",
            type: "max_subtotal",
            amount: 200,
          },
        ],
      }

      const res = await api
        .post(`/admin/shipping-options/test-option-req`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
        .catch((err) => {
          console.log(err.response.data.message)
        })

      expect(res.status).toEqual(200)
      expect(res.data.shipping_option.requirements[0].amount).toEqual(150)
      expect(res.data.shipping_option.requirements[1].amount).toEqual(200)
    })

    it("fails to updates a set of requirements because max. subtotal <= min. subtotal", async () => {
      const api = useApi()

      const payload = {
        requirements: [
          {
            id: "option-req",
            type: "min_subtotal",
            amount: 1500,
          },
          {
            id: "option-req-2",
            type: "max_subtotal",
            amount: 200,
          },
        ],
      }

      const res = await api
        .post(`/admin/shipping-options/test-option-req`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
        .catch((err) => {
          return err.response
        })

      expect(res.status).toEqual(400)
      expect(res.data.message).toEqual(
        "Max. subtotal must be greater than Min. subtotal"
      )
    })
  })

  describe("POST /admin/shipping-options", () => {
    let payload

    beforeEach(async () => {
      try {
        await adminSeeder(dbConnection)
        await shippingOptionSeeder(dbConnection)

        const api = useApi()
        await api.post(
          `/admin/regions/region`,
          {
            fulfillment_providers: ["test-ful"],
          },
          {
            headers: {
              Authorization: "Bearer test_token",
            },
          }
        )

        const manager = dbConnection.manager
        const defaultProfile = await manager.findOne(ShippingProfile, {
          type: "default",
        })

        payload = {
          name: "Test option",
          amount: 100,
          price_type: "flat_rate",
          region_id: "region",
          provider_id: "test-ful",
          data: {},
          profile_id: defaultProfile.id,
        }
      } catch (err) {
        console.error(err)
        throw err
      }
    })

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

    it("creates a shipping option with requirements", async () => {
      const api = useApi()
      payload.requirements = [
        {
          type: "max_subtotal",
          amount: 2,
        },
        {
          type: "min_subtotal",
          amount: 1,
        },
      ]

      const res = await api.post(`/admin/shipping-options`, payload, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      expect(res.status).toEqual(200)
      expect(res.data.shipping_option.requirements.length).toEqual(2)
    })

    it("creates a shipping option with no requirements", async () => {
      const api = useApi()
      const res = await api.post(`/admin/shipping-options`, payload, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      expect(res.status).toEqual(200)
      expect(res.data.shipping_option.requirements.length).toEqual(0)
    })

    it("fails on same requirement types", async () => {
      const api = useApi()
      payload.requirements = [
        {
          type: "max_subtotal",
          amount: 2,
        },
        {
          type: "max_subtotal",
          amount: 1,
        },
      ]

      try {
        await api.post(`/admin/shipping-options`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
      } catch (error) {
        expect(error.response.data.message).toEqual(
          "Only one requirement of each type is allowed"
        )
      }
    })

    it("fails when min_subtotal > max_subtotal", async () => {
      const api = useApi()
      payload.requirements = [
        {
          type: "max_subtotal",
          amount: 2,
        },
        {
          type: "min_subtotal",
          amount: 4,
        },
      ]

      try {
        await api.post(`/admin/shipping-options`, payload, {
          headers: {
            Authorization: "Bearer test_token",
          },
        })
      } catch (error) {
        expect(error.response.data.message).toEqual(
          "Max. subtotal must be greater than Min. subtotal"
        )
      }
    })
  })
  describe("GET /admin/shipping-options", () => {
    beforeEach(async () => {
      try {
        await adminSeeder(dbConnection)
        await shippingOptionSeeder(dbConnection)
      } catch (err) {
        console.error(err)
        throw err
      }
    })

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

    it("lists shipping options", async () => {
      const api = useApi()
      const res = await api.get(`/admin/shipping-options`, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      expect(res.status).toEqual(200)
    })

    it("lists admin only shipping options", async () => {
      const api = useApi()
      const res = await api.get(`/admin/shipping-options?admin_only=true`, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      expect(res.status).toEqual(200)
      expect(res.data.shipping_options).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-option-req-admin-only",
            admin_only: true,
          }),
        ])
      )
    })

    it("lists return shipping options", async () => {
      const api = useApi()
      const res = await api.get(`/admin/shipping-options?is_return=true`, {
        headers: {
          Authorization: "Bearer test_token",
        },
      })

      expect(res.status).toEqual(200)
      expect(res.data.shipping_options).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-option-req-return",
            is_return: true,
          }),
        ])
      )
    })

    it("lists shipping options without return and admin options", async () => {
      const api = useApi()
      const res = await api.get(
        `/admin/shipping-options?is_return=false&admin_only=true`,
        {
          headers: {
            Authorization: "Bearer test_token",
          },
        }
      )

      expect(res.status).toEqual(200)
      expect(res.data.shipping_options).not.toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            id: "test-option-req-return",
            is_return: true,
          }),
          expect.objectContaining({
            id: "test-option-req-admin-only",
            admin_only: true,
          }),
        ])
      )
    })
  })
})
