import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js"

import * as api from "@/lib/api"
import { protectedProcedure, router } from "@/server/trpc"

import { countries } from "@/components/TempDesignSystem/Form/Country/countries"
import * as maskValue from "@/utils/maskValue"
import { getMembershipCards } from "@/utils/user"

import {
  friendTransactionsInput,
  getUserInputSchema,
  staysInput,
} from "./input"
import {
  getCreditCardsSchema,
  getFriendTransactionsSchema,
  getMembershipCardsSchema,
  getStaysSchema,
  getUserSchema,
} from "./output"
import { benefits, extendedUser, nextLevelPerks } from "./temp"

function fakingRequest<T>(payload: T): Promise<T> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(payload)
    }, 1500)
  })
}

export const userQueryRouter = router({
  get: protectedProcedure
    .input(getUserInputSchema)
    .query(async function getUser({ ctx, input }) {
      const apiResponse = await api.get(api.endpoints.v1.profile, {
        cache: "no-store",
        headers: {
          Authorization: `Bearer ${ctx.session.token.access_token}`,
        },
      })

      if (!apiResponse.ok) {
        // switch (apiResponse.status) {
        //   case 400:
        //     throw badRequestError()
        //   case 401:
        //     throw unauthorizedError()
        //   case 403:
        //     throw forbiddenError()
        //   default:
        //     throw internalServerError()
        // }
        console.info(`API Response Failed - Getting User`)
        console.info(`User: (${JSON.stringify(ctx.session.user)})`)
        console.error(apiResponse)
        return null
      }

      const apiJson = await apiResponse.json()
      if (!apiJson.data?.attributes) {
        // throw notFound(apiJson)
        console.error(
          `User has no data - (user: ${JSON.stringify(ctx.session.user)})`
        )
        return null
      }

      const verifiedData = getUserSchema.safeParse(apiJson.data.attributes)
      if (!verifiedData.success) {
        console.info(
          `Failed to validate User - (User: ${JSON.stringify(ctx.session.user)})`
        )
        console.error(verifiedData.error)
        return null
      }

      const country = countries.find(
        (c) => c.code === verifiedData.data.address.countryCode
      )

      const user = {
        ...extendedUser,
        address: {
          city: verifiedData.data.address.city,
          country: country?.name ?? "",
          countryCode: verifiedData.data.address.countryCode,
          streetAddress: verifiedData.data.address.streetAddress,
          zipCode: verifiedData.data.address.zipCode,
        },
        dateOfBirth: verifiedData.data.dateOfBirth,
        email: verifiedData.data.email,
        firstName: verifiedData.data.firstName,
        language: verifiedData.data.language,
        lastName: verifiedData.data.lastName,
        memberships: verifiedData.data.memberships,
        name: `${verifiedData.data.firstName} ${verifiedData.data.lastName}`,
        phoneNumber: verifiedData.data.phoneNumber,
        profileId: verifiedData.data.profileId,
      }

      if (input.mask) {
        if (user.address.city) {
          user.address.city = maskValue.text(user.address.city)
        }
        if (user.address.streetAddress) {
          user.address.streetAddress = maskValue.text(
            user.address.streetAddress
          )
        }

        user.address.zipCode = verifiedData.data.address?.zipCode
          ? maskValue.text(verifiedData.data.address.zipCode)
          : undefined
        user.email = maskValue.email(user.email)

        user.phoneNumber = user.phoneNumber
          ? maskValue.phone(user.phoneNumber)
          : undefined
      }

      return user
    }),

  benefits: router({
    current: protectedProcedure.query(async function (opts) {
      // TODO: Make request to get user data from Scandic API
      return await fakingRequest<typeof benefits>(benefits)
    }),
    nextLevel: protectedProcedure.query(async function (opts) {
      // TODO: Make request to get user data from Scandic API
      return await fakingRequest<typeof nextLevelPerks>(nextLevelPerks)
    }),
  }),

  stays: router({
    previous: protectedProcedure
      .input(staysInput)
      .query(async ({ ctx, input }) => {
        const { limit, cursor } = input

        const params = new URLSearchParams()
        params.set("limit", limit.toString())

        if (cursor) {
          params.set("offset", cursor.toString())
        }

        const apiResponse = await api.get(
          api.endpoints.v1.previousStays,
          {
            headers: {
              Authorization: `Bearer ${ctx.session.token.access_token}`,
            },
          },
          params
        )

        if (!apiResponse.ok) {
          // switch (apiResponse.status) {
          //   case 400:
          //     throw badRequestError(apiResponse)
          //   case 401:
          //     throw unauthorizedError(apiResponse)
          //   case 403:
          //     throw forbiddenError(apiResponse)
          //   default:
          //     throw internalServerError(apiResponse)
          // }
          console.info(`API Response Failed - Getting Previous Stays`)
          console.info(`User: (${JSON.stringify(ctx.session.user)})`)
          console.error(apiResponse)
          return null
        }

        const apiJson = await apiResponse.json()

        const verifiedData = getStaysSchema.safeParse(apiJson)
        if (!verifiedData.success) {
          console.info(`Failed to validate Previous Stays Data`)
          console.info(`User: (${JSON.stringify(ctx.session.user)})`)
          console.error(verifiedData.error)
          return null
        }

        const nextCursor =
          verifiedData.data.links &&
          verifiedData.data.links.offset < verifiedData.data.links.totalCount
            ? verifiedData.data.links.offset
            : undefined

        return {
          data: verifiedData.data.data,
          nextCursor,
        }
      }),

    upcoming: protectedProcedure
      .input(staysInput)
      .query(async ({ ctx, input }) => {
        const { limit, cursor } = input

        const params = new URLSearchParams()
        params.set("limit", limit.toString())

        if (cursor) {
          params.set("offset", cursor.toString())
        }

        const apiResponse = await api.get(
          api.endpoints.v1.upcomingStays,
          {
            headers: {
              Authorization: `Bearer ${ctx.session.token.access_token}`,
            },
          },
          params
        )

        if (!apiResponse.ok) {
          // switch (apiResponse.status) {
          //   case 400:
          //     throw badRequestError(apiResponse)
          //   case 401:
          //     throw unauthorizedError(apiResponse)
          //   case 403:
          //     throw forbiddenError(apiResponse)
          //   default:
          //     throw internalServerError(apiResponse)
          // }
          console.info(`API Response Failed - Getting Upcoming Stays`)
          console.info(`User: (${JSON.stringify(ctx.session.user)})`)
          console.error(apiResponse)
          return null
        }

        const apiJson = await apiResponse.json()
        const verifiedData = getStaysSchema.safeParse(apiJson)
        if (!verifiedData.success) {
          console.info(`Failed to validate Upcoming Stays Data`)
          console.info(`User: (${JSON.stringify(ctx.session.user)})`)
          console.error(verifiedData.error)
          return null
        }

        const nextCursor =
          verifiedData.data.links &&
          verifiedData.data.links.offset < verifiedData.data.links.totalCount
            ? verifiedData.data.links.offset
            : undefined

        return {
          data: verifiedData.data.data,
          nextCursor,
        }
      }),
  }),
  transaction: router({
    friendTransactions: protectedProcedure
      .input(friendTransactionsInput)
      .query(async (opts) => {
        const { limit, cursor } = opts.input

        const params = new URLSearchParams()
        params.set("limit", limit.toString())

        if (cursor) {
          params.set("offset", cursor.toString())
        }

        const apiResponse = await api.get(
          api.endpoints.v1.friendTransactions,
          {
            headers: {
              Authorization: `Bearer ${opts.ctx.session.token.access_token}`,
            },
          },
          params
        )

        if (!apiResponse.ok) {
          // switch (apiResponse.status) {
          //   case 400:
          //     throw badRequestError()
          //   case 401:
          //     throw unauthorizedError()
          //   case 403:
          //     throw forbiddenError()
          //   default:
          //     throw internalServerError()
          // }
          console.info(`API Response Failed - Getting Friend Transactions`)
          console.info(`User: (${JSON.stringify(opts.ctx.session.user)})`)
          console.error(apiResponse)
          return null
        }

        const apiJson = await apiResponse.json()
        const verifiedData = getFriendTransactionsSchema.safeParse(apiJson)
        if (!verifiedData.success) {
          console.info(`Failed to validate Friend Transactions Data`)
          console.info(`User: (${JSON.stringify(opts.ctx.session.user)})`)
          console.error(verifiedData.error)
          return null
        }

        const nextCursor =
          verifiedData.data.links &&
          verifiedData.data.links.offset < verifiedData.data.links.totalCount
            ? verifiedData.data.links.offset
            : undefined

        return {
          data: verifiedData.data.data.map(({ attributes }) => {
            return {
              awardPoints: attributes.awardPoints,
              checkinDate: attributes.checkinDate,
              checkoutDate: attributes.checkoutDate,
              city: attributes.hotelInformation?.city,
              confirmationNumber: attributes.confirmationNumber,
              hotelName: attributes.hotelInformation?.name,
              nights: attributes.nights,
            }
          }),
          nextCursor,
        }
      }),
  }),

  creditCards: protectedProcedure.query(async function ({ ctx }) {
    const apiResponse = await api.get(api.endpoints.v1.creditCards, {
      cache: "no-store",
      headers: {
        Authorization: `Bearer ${ctx.session.token.access_token}`,
      },
    })

    if (!apiResponse.ok) {
      console.info(`API Response Failed - Getting Creadit Cards`)
      console.info(`User: (${JSON.stringify(ctx.session.user)})`)
      console.error(apiResponse)
      return null
    }

    const apiJson = await apiResponse.json()
    const verifiedData = getCreditCardsSchema.safeParse(apiJson)
    if (!verifiedData.success) {
      console.info(`Failed to validate Credit Cards Data`)
      console.info(`User: (${JSON.stringify(ctx.session.user)})`)
      console.error(verifiedData.error)
      return null
    }

    return verifiedData.data.data
  }),

  membershipCards: protectedProcedure.query(async function ({ ctx }) {
    const apiResponse = await api.get(api.endpoints.v1.profile, {
      cache: "no-store",
      headers: {
        Authorization: `Bearer ${ctx.session.token.access_token}`,
      },
    })

    if (!apiResponse.ok) {
      // switch (apiResponse.status) {
      //   case 400:
      //     throw badRequestError()
      //   case 401:
      //     throw unauthorizedError()
      //   case 403:
      //     throw forbiddenError()
      //   default:
      //     throw internalServerError()
      // }
      console.info(`API Response Failed - Getting Membership Cards`)
      console.info(`User: (${JSON.stringify(ctx.session.user)})`)
      console.error(apiResponse)
    }

    const apiJson = await apiResponse.json()

    const verifiedData = getMembershipCardsSchema.safeParse(
      apiJson.data.attributes.memberships
    )

    if (!verifiedData.success) {
      console.info(`Failed to validate Memberships Cards Data`)
      console.info(`User: (${JSON.stringify(ctx.session.user)})`)
      console.error(verifiedData.error)
      return null
    }
    const cards = getMembershipCards(verifiedData.data)

    return cards
  }),
})
