/* eslint-disable formatjs/no-literal-string-in-jsx */
/* TODO remove disable and add i18n */
/* TODO add analytics */
import { zodResolver } from "@hookform/resolvers/zod"
import { cx } from "class-variance-authority"
import { useState } from "react"
import { FormProvider, useForm, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import z from "zod"

import { dt } from "@scandic-hotels/common/dt"
import { Button } from "@scandic-hotels/design-system/Button"
import { FormInput } from "@scandic-hotels/design-system/Form/FormInput"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import { MessageBanner } from "@scandic-hotels/design-system/MessageBanner"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client"

import useLang from "@/hooks/useLang"

import styles from "./claimPoints.module.css"

type PointClaimBookingInfo = {
  from: string
  to: string
  city: string
  hotel: string
}
export function ClaimPointsWizard({
  onSuccess,
  onClose,
}: {
  onSuccess: () => void
  onClose: () => void
}) {
  const [state, setState] = useState<
    "initial" | "loading" | "invalid" | "form"
  >("initial")
  const [bookingDetails, setBookingDetails] =
    useState<PointClaimBookingInfo | null>(null)

  const { data, isLoading } = trpc.user.getSafely.useQuery()

  if (state === "invalid") {
    return <InvalidBooking onClose={onClose} />
  }

  if (state === "form") {
    if (isLoading) {
      return null
    }

    return (
      <ClaimPointsForm
        onSuccess={onSuccess}
        initialData={{
          ...bookingDetails,
          firstName: data?.firstName ?? "",
          lastName: data?.lastName ?? "",
          email: data?.email ?? "",
          phone: data?.phoneNumber ?? "",
        }}
      />
    )
  }

  const handleBookingNumberEvent = (event: BookingNumberEvent) => {
    switch (event.type) {
      case "submit":
        setState("loading")
        break
      case "error":
        setState("initial")
        break
      case "invalid":
        setState("invalid")
        break
      case "success":
        setBookingDetails(event.data)
        setState("form")
        break
    }
  }

  return (
    <div className={styles.introWrapper}>
      {state === "loading" && (
        <div
          className={styles.spinner}
          aria-live="polite"
          aria-label="Loading booking details, please wait.."
        >
          <LoadingSpinner />
        </div>
      )}
      <div
        className={cx(styles.options, { [styles.hidden]: state === "loading" })}
      >
        <section className={styles.sectionCard}>
          <div className={styles.sectionInfo}>
            <Typography variant="Body/Paragraph/mdBold">
              <h4>Claim points with booking number</h4>
            </Typography>
            <Typography variant="Body/Supporting text (caption)/smRegular">
              <p>
                Enter a valid booking number to load booking details
                automatically.
              </p>
            </Typography>
          </div>
          <BookingNumberInput onEvent={handleBookingNumberEvent} />
        </section>
        <Divider />
        <section className={styles.sectionCard}>
          <div className={styles.sectionInfo}>
            <Typography variant="Body/Paragraph/mdBold">
              <h4>Claim points without booking number</h4>
            </Typography>
            <Typography variant="Body/Supporting text (caption)/smRegular">
              <p>You need to add booking details in a form.</p>
            </Typography>
          </div>
          <Button variant="Secondary" onPress={() => setState("form")}>
            Fill form to claim points
          </Button>
        </section>
      </div>
      <MessageBanner
        type="info"
        text="Points can be claimed up to 6 months back if you were a member at the time of your stay."
      />
    </div>
  )
}

type BookingNumberFormData = {
  bookingNumber: string
}
type BookingNumberEvent =
  | { type: "submit" }
  | { type: "success"; data: PointClaimBookingInfo }
  | { type: "error" }
  | { type: "invalid" }
function BookingNumberInput({
  onEvent,
}: {
  onEvent: (event: BookingNumberEvent) => void
}) {
  const lang = useLang()
  const form = useForm<BookingNumberFormData>({
    resolver: zodResolver(
      z.object({
        bookingNumber: z
          .string()
          // TODO Check UX for validation as different environments have different lengths
          .min(9, { message: "Booking number must be 10 digits" })
          .max(10, { message: "Booking number must be 10 digits" }),
      })
    ),
    defaultValues: {
      bookingNumber: "",
    },
  })

  const confirmationNumber = useWatch({
    name: "bookingNumber",
    control: form.control,
  })

  const { refetch, isFetching } =
    trpc.booking.findBookingForCurrentUser.useQuery(
      {
        confirmationNumber,
        lang,
      },
      { enabled: false }
    )

  const handleSubmit = async () => {
    onEvent({ type: "submit" })
    const result = await refetch()
    if (!result.data) {
      onEvent({ type: "error" })
      form.setError("bookingNumber", {
        type: "manual",
        message:
          "We could not find a booking with this number registered in your name.",
      })
      return
    }

    const data = result.data

    // TODO validate if this should be check out or check in date
    const checkOutDate = dt(data.booking.checkOutDate)
    const sixMonthsAgo = dt().subtract(6, "months")
    if (checkOutDate.isBefore(sixMonthsAgo, "day")) {
      onEvent({ type: "invalid" })
      return
    }

    onEvent({
      type: "success",
      data: {
        from: data.booking.checkInDate,
        to: data.booking.checkOutDate,
        city: data.hotel.cityName,
        hotel: data.hotel.name,
      },
    })
  }

  return (
    <FormProvider {...form}>
      <form onSubmit={form.handleSubmit(handleSubmit)}>
        <FormInput
          name="bookingNumber"
          label="Booking number"
          leftIcon={<MaterialIcon icon="edit_document" />}
          description="Enter your 10-digit booking number"
          maxLength={10}
          showClearContentIcon
          disabled={isFetching}
          autoFocus
          autoComplete="off"
          onChange={(e) => {
            const value = e.target.value
            if (value.length !== 10) return

            form.handleSubmit(handleSubmit)()
          }}
        />
      </form>
    </FormProvider>
  )
}

function InvalidBooking({ onClose }: { onClose: () => void }) {
  return (
    <div className={styles.invalidWrapper}>
      <Typography variant="Body/Paragraph/mdRegular">
        <p>
          We can’t add these points to your account as it has been longer than 6
          months since your stay.
        </p>
      </Typography>
      <Button variant="Primary" fullWidth onPress={onClose}>
        Close
      </Button>
    </div>
  )
}

type PointClaimUserInfo = {
  firstName: string
  lastName: string
  email: string
  phone: string
}
function ClaimPointsForm({
  onSuccess,
  initialData,
}: {
  onSuccess: () => void
  initialData: Partial<PointClaimBookingInfo & PointClaimUserInfo> | null
}) {
  const form = useForm({
    resolver: zodResolver(
      z.object({
        from: z.string().min(1, { message: "Arrival date is required" }),
        to: z.string().min(1, { message: "Departure date is required" }),
        city: z.string().min(1, { message: "City is required" }),
        hotel: z.string().min(1, { message: "Hotel is required" }),
        firstName: z.string().min(1, { message: "First name is required" }),
        lastName: z.string().min(1, { message: "Last name is required" }),
        email: z
          .string()
          .email("Enter a valid email")
          .min(1, { message: "Email is required" }),
        phone: z.string().min(1, { message: "Phone is required" }),
      })
    ),
    defaultValues: {
      from: initialData?.from || "",
      to: initialData?.to || "",
      city: initialData?.city || "",
      hotel: initialData?.hotel || "",
      firstName: initialData?.firstName || "",
      lastName: initialData?.lastName || "",
      email: initialData?.email || "",
      phone: initialData?.phone || "",
    },
    mode: "all",
  })

  const { mutate, isPending } = trpc.user.claimPoints.useMutation({
    onSuccess,
  })

  const autoFocusField = getAutoFocus(initialData)

  return (
    <FormProvider {...form}>
      <form
        className={styles.form}
        onSubmit={form.handleSubmit((data) => mutate(data))}
      >
        <div className={styles.formInputs}>
          {!initialData?.firstName && (
            <FormInput
              name="firstName"
              label="First name"
              autoFocus={autoFocusField === "firstName"}
              readOnly={isPending}
              registerOptions={{ required: true }}
            />
          )}
          {!initialData?.lastName && (
            <FormInput
              name="lastName"
              label="Last name"
              autoFocus={autoFocusField === "lastName"}
              readOnly={isPending}
              registerOptions={{ required: true }}
            />
          )}
          {!initialData?.email && (
            <FormInput
              name="email"
              label="Email"
              type="email"
              autoFocus={autoFocusField === "email"}
              readOnly={isPending}
              registerOptions={{ required: true }}
            />
          )}
          {!initialData?.phone && (
            <FormInput
              name="phone"
              label="Phone"
              type="tel"
              autoFocus={autoFocusField === "phone"}
              readOnly={isPending}
              registerOptions={{ required: true }}
            />
          )}
          <FormInput
            name="from"
            label="Arrival (YYYY-MM-DD)"
            leftIcon={<MaterialIcon icon="calendar_today" />}
            autoFocus={autoFocusField === "from"}
            readOnly={isPending}
            registerOptions={{ required: true }}
          />
          <FormInput
            name="to"
            label="Departure (YYYY-MM-DD)"
            leftIcon={<MaterialIcon icon="calendar_today" />}
            readOnly={isPending}
            registerOptions={{ required: true }}
          />
          <FormInput
            name="city"
            label="City"
            readOnly={isPending}
            registerOptions={{ required: true }}
          />
          <FormInput
            name="hotel"
            label="Hotel"
            readOnly={isPending}
            registerOptions={{ required: true }}
          />
        </div>
        <MessageBanner
          type="info"
          text="Points can be claimed up to 6 months back if you were a member at the time of your stay."
        />
        <Button
          type="submit"
          variant="Primary"
          fullWidth
          isDisabled={!form.formState.isValid}
          isPending={isPending}
          className={styles.formSubmit}
        >
          Send points claim
        </Button>
      </form>
    </FormProvider>
  )
}

function getAutoFocus(userInfo: Partial<PointClaimUserInfo> | null) {
  if (!userInfo?.firstName) {
    return "firstName"
  }

  if (!userInfo?.lastName) {
    return "lastName"
  }

  if (!userInfo?.email) {
    return "email"
  }

  if (!userInfo?.phone) {
    return "phone"
  }

  return "from"
}

function Divider() {
  const intl = useIntl()

  return (
    <div className={styles.divider}>
      <Typography variant="Body/Paragraph/mdRegular">
        <span>
          {intl.formatMessage({
            id: "common.or",
            defaultMessage: "or",
          })}
        </span>
      </Typography>
    </div>
  )
}
