import * as Sentry from "@sentry/nextjs"
import { TRPCError } from "@trpc/server"
import { cookies } from "next/headers"
import { v4 as uuidv4 } from "uuid"
import { z } from "zod"

import { env } from "@/env/server"
import { protectedProcedure } from "@/server/trpc"

import { getSasToken } from "../../getSasToken"
import { SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME } from "../constants"
import {
  parseSASRequestOtpError,
  type RequestOtpGeneralError,
} from "./requestOtpError"

import type { OtpState } from "../getOTPState"

const successSchema = z.object({
  status: z.literal("SENT"),
  referenceId: z.string().uuid(),
  databaseUUID: z.string().uuid(),
  otpExpiration: z.number(),
  otpReceiver: z.string(),
})

const failureSchema = z.object({
  status: z.enum([
    "VERIFIED",
    "ABUSED",
    "EXPIRED",
    "PENDING",
    "RETRY",
    "NULL",
    "NOTSENT",
  ]),
})

const outputSchema = z.union([successSchema, failureSchema])

export const requestOtp = protectedProcedure
  .output(outputSchema)
  .mutation(async function () {
    const sasAuthToken = await getSasToken()

    if (!sasAuthToken) {
      throw createError("AUTH_TOKEN_NOT_FOUND")
    }

    const tokenResponse = await fetchRequestOtp({ sasAuthToken })
    console.log(
      "[SAS] requestOtp",
      tokenResponse.status,
      tokenResponse.statusText
    )

    const body = await tokenResponse.json()
    const parseResult = outputSchema.safeParse(body)
    if (!parseResult.success) {
      console.error("[SAS] requestOtp error", body)

      if (!tokenResponse.ok) {
        throw createError(body)
      }
      throw createError(parseResult.error)
    }

    if (parseResult.data.status === "SENT") {
      setSASOtpCookie(parseResult.data)
    } else {
      const sasRequestOtpErrorMessage = `[SAS] requestOtp did not return SENT status with body: ${JSON.stringify(body)}`
      console.warn(sasRequestOtpErrorMessage)
      Sentry.captureMessage(sasRequestOtpErrorMessage)
    }

    return parseResult.data
  })

function createError(
  errorBody:
    | {
        status: string
        error: string
        errorCode: number
        databaseUUID: string
      }
    | Error
    | RequestOtpGeneralError
): TRPCError {
  const errorInfo = parseSASRequestOtpError(errorBody)
  console.error("[SAS] createError", errorInfo)
  return new TRPCError({
    code: "BAD_REQUEST",
    cause: errorInfo,
  })
}

async function fetchRequestOtp({ sasAuthToken }: { sasAuthToken: string }) {
  const endpoint = `${env.SAS_API_ENDPOINT}/api/scandic-partnership/customer/send-otp`

  console.log("[SAS]: Requesting OTP")

  return await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Ocp-Apim-Subscription-Key": env.SAS_OCP_APIM,
      Authorization: `Bearer ${sasAuthToken}`,
    },
    body: JSON.stringify({
      referenceId: uuidv4(),
    }),
  })
}

async function setSASOtpCookie({
  referenceId,
  databaseUUID,
}: {
  referenceId: string
  databaseUUID: string
}) {
  const cookieStore = await cookies()
  cookieStore.set(
    SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME,
    JSON.stringify({
      referenceId: referenceId,
      databaseUUID: databaseUUID,
    } satisfies OtpState),
    {
      httpOnly: true,
      maxAge: 3600,
    }
  )
}
