import { cookies } from "next/headers"
import { redirect } from "next/navigation"
import { z } from "zod"

import { env } from "@/env/server"
import { serverClient } from "@/lib/trpc/server"

import { safeTry } from "@/utils/safeTry"

import { SAS_TOKEN_STORAGE_KEY, stateSchema } from "../sasUtils"

import type { NextRequest } from "next/server"

const searchParamsSchema = z.object({
  code: z.string(),
  state: z.string(),
})
const tokenResponseSchema = z.object({
  access_token: z.string(),
  expires_in: z.number(),
  token_type: z.literal("Bearer"),
})

export async function GET(
  request: NextRequest,
  props: { params: Promise<{ lang: string }> }
) {
  const params = await props.params
  const { lang } = params

  const result = searchParamsSchema.safeParse({
    code: request.nextUrl.searchParams.get("code"),
    state: request.nextUrl.searchParams.get("state"),
  })

  if (!result.success) {
    console.error("[SAS] Invalid search params", result.error)
    redirect(`/${lang}/sas-x-scandic/error?errorCode=invalid_query`)
  }
  const { code, state } = result.data

  const tokenResponse = await fetch(
    new URL("oauth/token", env.SAS_AUTH_ENDPOINT),
    {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        grant_type: "authorization_code",
        code: code,
        redirect_uri: new URL(
          `/${lang}/sas-x-scandic/callback`,
          new URL(env.PUBLIC_URL)
        ).toString(),
        client_id: env.SAS_AUTH_CLIENTID,
      }),
    }
  )

  if (!tokenResponse.ok) {
    const error = await tokenResponse.text()
    console.error("[SAS] Failed to get token", error)
    redirect(`/${lang}/sas-x-scandic/error?errorCode=token_error`)
  }

  const tokenData = tokenResponseSchema.parse(await tokenResponse.json())

  const stateResult = stateSchema.safeParse(
    JSON.parse(decodeURIComponent(state))
  )
  if (!stateResult.success) {
    redirect(`/${lang}/sas-x-scandic/error?errorCode=invalid_state`)
  }

  const cookieStore = await cookies()
  cookieStore.set(SAS_TOKEN_STORAGE_KEY, tokenData.access_token, {
    maxAge: 3600,
    httpOnly: true,
  })

  if (
    stateResult.data.intent === "link" ||
    stateResult.data.intent === "unlink" ||
    stateResult.data.intent === "transfer"
  ) {
    const caller = await serverClient()
    const [data, error] = await safeTry(caller.partner.sas.requestOtp())
    if (!data || error) {
      console.error("[SAS] Failed to request OTP", error)
      redirect(`/${lang}/sas-x-scandic/error`)
    }

    switch (data.status) {
      case "ABUSED":
      case "NOTSENT":
        redirect(`/${params.lang}/sas-x-scandic/error?errorCode=tooManyCodes`)
      case "NULL":
      case "RETRY":
      case "EXPIRED":
      case "PENDING":
      case "VERIFIED":
        // These errors should never happen for request, but according to the API spec they can
        throw new Error(`Unhandled request OTP status ${data.status}`)
    }

    console.log("[SAS] Request OTP response", data)

    const otpUrl = new URL(
      `/${lang}/sas-x-scandic/otp`,
      new URL(env.PUBLIC_URL)
    )
    otpUrl.searchParams.set("intent", stateResult.data.intent)
    otpUrl.searchParams.set("to", data.otpReceiver)

    redirect(otpUrl.toString())
  }

  redirect(`/${lang}/sas-x-scandic/error?errorCode=unknown_intent`)
}
