"use server"

import { z } from "zod"

import { ApiLang } from "@/constants/languages"
import * as api from "@/lib/api"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { protectedServerActionProcedure } from "@/server/trpc"

import { editProfileSchema } from "@/components/Forms/Edit/Profile/schema"
import { countriesMap } from "@/components/TempDesignSystem/Form/Country/countries"
import { getIntl } from "@/i18n"
import { phoneValidator } from "@/utils/phoneValidator"
import { getMembership } from "@/utils/user"

import { Status } from "@/types/components/myPages/myProfile/edit"

const editProfilePayload = z
  .object({
    address: z.object({
      city: z.string().optional(),
      countryCode: z.nativeEnum(countriesMap),
      streetAddress: z.string().optional(),
      zipCode: z.string().min(1, { message: "Zip code is required" }),
    }),
    dateOfBirth: z.string(),
    email: z.string().email(),
    language: z.nativeEnum(ApiLang),
    newPassword: z.string().optional(),
    password: z.string().optional(),
    phoneNumber: phoneValidator("Phone is required"),
  })
  .transform((data) => {
    if (!data.password || !data.newPassword) {
      delete data.password
      delete data.newPassword
    }
    if (data.phoneNumber) {
      data.phoneNumber = data.phoneNumber.replaceAll(" ", "").trim()
    }
    return data
  })

export const editProfile = protectedServerActionProcedure
  .input(editProfileSchema)
  .mutation(async function ({ ctx, input }) {
    const intl = await getIntl()
    const payload = editProfilePayload.safeParse(input)
    if (!payload.success) {
      console.error(
        "editProfile payload validation error",
        JSON.stringify({
          query: input,
          error: payload.error,
        })
      )

      return {
        data: input,
        issues: payload.error.issues.map((issue) => ({
          field: issue.path.join("."),
          message: issue.message,
        })),
        message: intl.formatMessage({
          id: "An error occured when trying to update profile.",
        }),
        status: Status.error,
      }
    }

    const profile = await getProfile()
    if (!profile || "error" in profile) {
      console.error(
        "editProfile profile fetch error",
        JSON.stringify({
          query: input,
          error: profile?.error,
        })
      )

      return {
        data: input,
        issues: [],
        message: intl.formatMessage({
          id: "An error occured when trying to update profile.",
        }),
        status: Status.error,
      }
    }

    const body: Partial<z.infer<typeof editProfilePayload>> = {}
    Object.keys(payload.data).forEach((key) => {
      const typedKey = key as unknown as keyof z.infer<
        typeof editProfilePayload
      >
      if (typedKey === "password" || typedKey === "newPassword") {
        body[typedKey] = payload.data[typedKey]
        return
      }

      if (typedKey === "address") {
        if (
          (payload.data.address.city === profile.address.city ||
            (!payload.data.address.city && !profile.address.city)) &&
          (payload.data.address.countryCode === profile.address.countryCode ||
            (!payload.data.address.countryCode &&
              !profile.address.countryCode)) &&
          (payload.data.address.streetAddress ===
            profile.address.streetAddress ||
            (!payload.data.address.streetAddress &&
              !profile.address.streetAddress)) &&
          (payload.data.address.zipCode === profile.address.zipCode ||
            (!payload.data.address.zipCode && !profile.address.zipCode))
        ) {
          // untouched - noop
        } else {
          /** API clears all other fields not sent in address payload */
          body.address = payload.data.address
        }
        return
      }

      // Handle case where dateOfBirth is missing in profile and not updated in form
      if (
        typedKey === "dateOfBirth" &&
        !profile.dateOfBirth &&
        payload.data.dateOfBirth === "1900-01-01"
      ) {
        return
      }

      if (payload.data[typedKey] !== profile[typedKey]) {
        // @ts-ignore
        body[typedKey] = payload.data[typedKey]
      }
    })

    if (Object.keys(body).length === 0) {
      return {
        data: input,
        message: intl.formatMessage({ id: "Successfully updated profile!" }),
        status: Status.success,
      }
    } else {
      const membership = getMembership(profile.memberships)
      console.log(
        `[edit profile: ${membership?.membershipNumber}] body keys: ${JSON.stringify(Object.keys(body))}`
      )
    }

    const apiResponse = await api.patch(api.endpoints.v1.Profile.profile, {
      body,
      cache: "no-store",
      headers: {
        Authorization: `Bearer ${ctx.session.token.access_token}`,
      },
    })

    if (!apiResponse.ok) {
      const text = await apiResponse.text()
      console.error(
        "editProfile api patch error",
        JSON.stringify({
          query: input,
          error: {
            status: apiResponse.status,
            statusText: apiResponse.statusText,
            error: text,
          },
        })
      )

      return {
        data: input,
        issues: [],
        message: intl.formatMessage({
          id: "An error occured when trying to update profile.",
        }),
        status: Status.error,
      }
    }

    const json = await apiResponse.json()
    if (json.errors?.length) {
      json.errors.forEach((error: any) => {
        console.warn(
          "editProfile api patch errors (not aborting)",
          JSON.stringify({
            query: input,
            error,
          })
        )
      })
    }

    const validatedData = editProfileSchema.safeParse(json.data.attributes)
    if (!validatedData.success) {
      console.error(
        "editProfile validation error",
        JSON.stringify({
          query: input,
          error: validatedData.error,
        })
      )

      return {
        data: input,
        issues: validatedData.error.issues.map((issue) => ({
          field: issue.path.join("."),
          message: issue.message,
        })),
        message: intl.formatMessage({
          id: "An error occured when trying to update profile.",
        }),
        status: Status.error,
      }
    }

    return {
      data: validatedData.data,
      message: intl.formatMessage({ id: "Successfully updated profile!" }),
      status: Status.success,
    }
  })
