import extensionStrategy, {
  hasExtensionInstalled,
  hasChromeExtensionInstalled,
  hasFirefoxExtensionInstalled,
  cancelRunningExtensionRequest,
} from "../ExtensionStrategy"

jest.mock("../../utils/b64", () => ({
  __esModule: true,
  decodeB64StringToArrayBuffer: jest.fn((data) => `${data}-converted`),
}))

jest.mock("~/newstore/settings", () => {
  return {
    __esModule: true,
    settingsStore: {
      value: {
        EXTENSIONS_ENABLED: true,
        PROXY_ENABLED: true,
        PROXY_URL: "test",
      },
    },
  }
})

describe("hasExtensionInstalled", () => {
  test("returns true if extension is present and hooked", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}

    expect(hasExtensionInstalled()).toEqual(true)
  })

  test("returns false if extension not present or not hooked", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined

    expect(hasExtensionInstalled()).toEqual(false)
  })
})

describe("hasChromeExtensionInstalled", () => {
  test("returns true if extension is hooked and browser is chrome", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Chrome")
    jest.spyOn(navigator, "vendor", "get").mockReturnValue("Google")

    expect(hasChromeExtensionInstalled()).toEqual(true)
  })

  test("returns false if extension is hooked and browser is not chrome", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Firefox")
    jest.spyOn(navigator, "vendor", "get").mockReturnValue("Google")

    expect(hasChromeExtensionInstalled()).toEqual(false)
  })

  test("returns false if extension not installed and browser is chrome", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Chrome")
    jest.spyOn(navigator, "vendor", "get").mockReturnValue("Google")

    expect(hasChromeExtensionInstalled()).toEqual(false)
  })

  test("returns false if extension not installed and browser is not chrome", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Firefox")
    jest.spyOn(navigator, "vendor", "get").mockReturnValue("Google")

    expect(hasChromeExtensionInstalled()).toEqual(false)
  })
})

describe("hasFirefoxExtensionInstalled", () => {
  test("returns true if extension is hooked and browser is firefox", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Firefox")

    expect(hasFirefoxExtensionInstalled()).toEqual(true)
  })

  test("returns false if extension is hooked and browser is not firefox", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Chrome")

    expect(hasFirefoxExtensionInstalled()).toEqual(false)
  })

  test("returns false if extension not installed and browser is firefox", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Firefox")

    expect(hasFirefoxExtensionInstalled()).toEqual(false)
  })

  test("returns false if extension not installed and browser is not firefox", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined
    jest.spyOn(navigator, "userAgent", "get").mockReturnValue("Chrome")

    expect(hasFirefoxExtensionInstalled()).toEqual(false)
  })
})

describe("cancelRunningExtensionRequest", () => {
  const cancelFunc = jest.fn()

  beforeEach(() => {
    cancelFunc.mockClear()
  })

  test("cancels request if extension installed and function present in hook", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {
      cancelRunningRequest: cancelFunc,
    }

    cancelRunningExtensionRequest()
    expect(cancelFunc).toHaveBeenCalledTimes(1)
  })

  test("does not cancel request if extension not installed", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = undefined

    cancelRunningExtensionRequest()
    expect(cancelFunc).not.toHaveBeenCalled()
  })

  test("does not cancel request if extension installed but function not present", () => {
    global.__POSTWOMAN_EXTENSION_HOOK__ = {}

    cancelRunningExtensionRequest()
    expect(cancelFunc).not.toHaveBeenCalled()
  })
})

describe("extensionStrategy", () => {
  const sendReqFunc = jest.fn()

  beforeEach(() => {
    sendReqFunc.mockClear()
  })

  describe("Proxy Requests", () => {
    test("asks extension to send request", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success":true,"data":""}',
      })

      await extensionStrategy({})

      expect(sendReqFunc).toHaveBeenCalledTimes(1)
    })

    test("sends POST request to proxy if proxy is enabled", async () => {
      let passedUrl
      let passedMethod

      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockImplementation(({ method, url }) => {
        passedUrl = url
        passedMethod = method

        return Promise.resolve({
          data: '{"success":true,"data":""}',
        })
      })

      await extensionStrategy({})

      expect(passedUrl).toEqual("test")
      expect(passedMethod).toEqual("post")
    })

    test("passes request fields properly", async () => {
      const reqFields = {
        testA: "testA",
        testB: "testB",
        testC: "testC",
      }

      let passedFields

      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockImplementation(({ data }) => {
        passedFields = data

        return Promise.resolve({
          data: '{"success":true,"data":""}',
        })
      })

      await extensionStrategy(reqFields)

      expect(passedFields).toMatchObject(reqFields)
    })

    test("passes wantsBinary field", async () => {
      let passedFields

      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockImplementation(({ data }) => {
        passedFields = data

        return Promise.resolve({
          data: '{"success":true,"data":""}',
        })
      })

      await extensionStrategy({})

      expect(passedFields).toHaveProperty("wantsBinary")
    })

    test("checks for proxy response success field and throws error message for non-success", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success":false,"data": { "message": "testerr" } }',
      })

      await expect(extensionStrategy({})).rejects.toThrow("testerr")
    })

    test("checks for proxy response success field and throws error 'Proxy Error' for non-success", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success":false,"data": {} }',
      })

      await expect(extensionStrategy({})).rejects.toThrow("Proxy Error")
    })

    test("checks for proxy response success and doesn't throw for success", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success":true,"data": {} }',
      })

      await expect(extensionStrategy({})).resolves.toBeDefined()
    })

    test("checks isBinary response field and resolve with the converted value if so", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success": true, "isBinary": true, "data": "testdata" }',
      })

      await expect(extensionStrategy({})).resolves.toMatchObject({
        data: "testdata-converted",
      })
    })

    test("checks isBinary response field and resolve with the actual value if not so", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockResolvedValue({
        data: '{"success": true, "isBinary": false, "data": "testdata" }',
      })

      await expect(extensionStrategy({})).resolves.toMatchObject({
        data: "testdata",
      })
    })

    test("failed request errors are thrown as-is", async () => {
      global.__POSTWOMAN_EXTENSION_HOOK__ = {
        sendRequest: sendReqFunc,
      }

      sendReqFunc.mockRejectedValue("err")

      await expect(extensionStrategy({})).rejects.toBe("err")
    })
  })
})
