import type { Event, EventDto } from "@/entities/event";
import type { EventPlace } from "@/entities/place";

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, useWatch } from "react-hook-form";

import { Button } from "@/shared/ui/button";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/shared/ui/form";
import { Input } from "@/shared/ui/input";
import { Textarea } from "@/shared/ui/textarea";
import { DatePicker } from "./DatePicker";
import { PlaceSelect } from "./PlaceSelect";

import type { Socials } from "@/entities/user";
import { Camera } from "@/features/camera";
import { TimePicker } from "@/features/time-input";
import { assertIsDefined } from "@/shared/lib/assert";
import { getTimeString } from "@/shared/lib/getTimeString";
import { getTimezoneOffset } from "@/shared/lib/getTimezoneOffset";
import { handleNumericInput } from "@/shared/lib/handleNumericInput";
import { formatUsdt } from "@/shared/lib/money/usdt";
import { getCurrencySymbol } from "@/shared/lib/web3";
import { ErrorMessage } from "@/shared/ui/ErrorMessage";
import {
  Dialog,
  DialogContent,
  DialogTitle,
  DialogTrigger,
} from "@/shared/ui/dialog";
import { fromUnixTime } from "date-fns";
import { useEffect, useMemo, useState } from "react";
import type { z } from "zod";
import { useCheckSubmit } from "../hooks/useCheckSubmit";
import { useEventPersist } from "../hooks/useEventPersist";
import { useFetchImage } from "../hooks/useFetchImage";
import { extractBookedRangesForPlace } from "../lib/extractBookedRangesForPlace";
import { getPlaceDefaults } from "../lib/getPlaceDefaults";
import { mapEventFormToEventDto, mapEventToEventForm } from "../lib/mapEvent";
import { createFormSchema } from "../model/formSchema";
import type { CategoryDraft } from "../model/types";
import { CategorySection } from "./CategorySection";
import { PlaceCard } from "./PlaceCard";

type EventFormProps = {
  events: Event[];
  places: EventPlace[];
  onSumbit: (values: EventDto, maybeSocials?: Socials) => void;
  existingEvent?: Event;
  existingCategories?: CategoryDraft[];
};

export const EventForm: React.FC<EventFormProps> = ({
  events,
  places,
  onSumbit: submitHandler,
  existingEvent,
  existingCategories,
}) => {
  if (!places[0])
    return (
      <ErrorMessage errors={new Error("No availible places to create event")} />
    );

  const eventForEdit = existingEvent
    ? mapEventToEventForm(existingEvent, existingCategories)
    : undefined;

  const eventIdsToExclude = useMemo(
    () => (existingEvent == null ? undefined : [existingEvent.id]),
    [existingEvent],
  );

  const formSchema = createFormSchema(places, events);

  const [cameraOpen, setCameraOpen] = useState(false);
  const [timeOpen, setTimeOpen] = useState(false);

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: existingEvent
      ? {
          ...eventForEdit,
          startDate: fromUnixTime(existingEvent.startDateTimestamp),
        }
      : {
          title: "",
          description: "",
          website: "",
          categories: [
            {
              id: crypto.randomUUID(),
              name: "General",
              discount: 0,
              quota: places[0].maxTickets,
            },
          ],
          ...getPlaceDefaults(places[0], events, eventIdsToExclude),
        },
  });

  useFetchImage(form, existingEvent);

  !eventForEdit && useEventPersist(form);

  const watchedPlaceId = useWatch({ control: form.control, name: "place" });
  const watchedDaysAmount = useWatch({
    control: form.control,
    name: "daysAmount",
  });
  const watchedStartDate = useWatch({
    control: form.control,
    name: "startDate",
  });

  const selectedPlace = places.find(
    (place) => `${place?.id}` === watchedPlaceId,
  );
  const isSelected = !!selectedPlace;

  assertIsDefined(selectedPlace);
  const { WithSubmitCheck, props } = useCheckSubmit(
    selectedPlace.eventDepositSize,
  );
  const placeRanges = extractBookedRangesForPlace(
    events,
    selectedPlace,
    eventIdsToExclude,
  );

  useEffect(() => {
    if (!form.formState.isDirty) return;

    const updatedValues = getPlaceDefaults(
      selectedPlace,
      events,
      eventIdsToExclude,
    );

    const { daysAmount, startDate, ticketPrice } = updatedValues;
    form.setValue("daysAmount", daysAmount);
    form.setValue("startDate", startDate);
    form.setValue("ticketPrice", ticketPrice);
  }, [form, selectedPlace, events, eventIdsToExclude]);

  const currentDate = watchedStartDate;

  function onSubmit(values: z.infer<typeof formSchema>) {
    const eventDto = mapEventFormToEventDto(values);
    if (existingEvent) {
      submitHandler(eventDto, {
        value: existingEvent.creator.socials.value,
        network: existingEvent.creator.socials.network,
      });
    }
    submitHandler(eventDto);
  }

  const isIphoneDevice =
    /iPhone/.test(navigator.userAgent) && !/iPad/.test(navigator.userAgent);

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(onSubmit)}
        className="grid grid-rows gap-5 px-3 py-5"
      >
        <FormField
          control={form.control}
          name="image"
          render={({ field }) => {
            const previewUrl = useMemo(() => {
              if (!field.value) return null;
              return URL.createObjectURL(field.value);
            }, [field.value]);

            return (
              <FormItem>
                <p className="uppercase text-lg font-bold text-start mb-1">
                  Event image
                </p>
                <div className="relative">
                  <Camera
                    open={cameraOpen}
                    setOpen={setCameraOpen}
                    onShot={(file: File) => form.setValue("image", file)}
                  />
                  <FormLabel htmlFor="fileInput">
                    {field.value ? (
                      <img
                        className="w-full aspect-video object-contain"
                        src={previewUrl ?? ""}
                        alt="event-thumb"
                      />
                    ) : (
                      <div className="text-center border-2 border-input bg-input/10 p-5 w-full aspect-video flex flex-col justify-center">
                        <h2 className="text-secondary">Upload image banner</h2>
                        <p className="text-normal font-normal text-muted-foreground lowercase">
                          16:9 ratio recommended
                        </p>
                      </div>
                    )}
                  </FormLabel>
                </div>
                <div className="flex items-center justify-between text-lg uppercase font-medium underline underline-offset-2">
                  <label className="cursor-pointer" htmlFor="fileInput">
                    Upload file
                  </label>
                  {isIphoneDevice || (
                    <p
                      className="cursor-pointer"
                      onClick={(e) => {
                        e.preventDefault();
                        setCameraOpen(true);
                      }}
                    >
                      Take a picture
                    </p>
                  )}
                </div>
                <FormControl>
                  <Input
                    id="fileInput"
                    type="file"
                    accept="image/*"
                    hidden
                    onChange={(e) => {
                      const files = e.target.files;
                      if (!files || files.length === 0) {
                        return form.setError("image", {
                          message: "Event must have an image",
                        });
                      }
                      if (!files[0]?.type.startsWith("image")) {
                        form.setError("image", {
                          message: "Incorrect image type",
                        });
                        return;
                      }
                      field.onChange(files[0]);
                      form.clearErrors();
                    }}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            );
          }}
        />
        <FormField
          control={form.control}
          name="title"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Event name</FormLabel>
              <FormControl>
                <Input placeholder="Enter event name" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="description"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Event description</FormLabel>
              <FormControl>
                <Textarea placeholder="Describe your event" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="website"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Landing website</FormLabel>
              <FormControl>
                <Input placeholder="https://www.leningrad.spb.ru" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="place"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Place</FormLabel>
              <PlaceSelect
                value={field.value}
                onChange={field.onChange}
                places={places}
              />
              <FormMessage />
            </FormItem>
          )}
        />
        {isSelected && <PlaceCard place={selectedPlace} />}
        <FormField
          control={form.control}
          name="daysAmount"
          render={({ field }) => (
            <FormItem className="flex-1">
              <FormLabel>Duration in days</FormLabel>
              <FormControl>
                <Input
                  inputMode="decimal"
                  {...field}
                  onChange={(e) =>
                    field.onChange(
                      Math.min(handleNumericInput(e.target.value), 30),
                    )
                  }
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="startDate"
          render={({ field }) => (
            <FormItem className="flex-1">
              <FormLabel>Start date</FormLabel>
              <FormControl>
                <DatePicker
                  daysBeforeCancel={selectedPlace.daysBeforeCancel}
                  selectedDuration={watchedDaysAmount}
                  date={field.value}
                  setDate={field.onChange}
                  disabled={placeRanges}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Dialog open={timeOpen} onOpenChange={setTimeOpen}>
          <DialogTrigger>
            <p className="uppercase text-lg font-bold text-start mb-1">
              Start time{" "}
              <span className="text-gray-500 text-sm normal-case">
                In local time
              </span>
            </p>
            <div className="border-2 border-secondary px-3 py-2">
              <h2 className="text-xl text-white text-center">
                {getTimeString(currentDate)}
              </h2>
            </div>
          </DialogTrigger>
          <DialogContent aria-describedby={undefined} className="py-5">
            <DialogTitle>Select time</DialogTitle>
            <p className="text-sm text-gray-500 -mt-2 mb-4">
              Time is set in your local timezone (UTC{getTimezoneOffset()})
            </p>
            <TimePicker
              setValue={(data) => {
                const date = currentDate;
                date.setHours(data.hours);
                date.setMinutes(data.minutes);
                form.setValue("startDate", date);
                setTimeOpen(false);
              }}
              initialValue={{
                hours: currentDate.getHours(),
                minutes: currentDate.getMinutes(),
              }}
            />
          </DialogContent>
        </Dialog>
        <FormField
          control={form.control}
          name="ticketPrice"
          render={({ field }) => {
            const minimumPrice = formatUsdt(
              BigInt(selectedPlace?.minPrice ?? 0),
            );
            return (
              <FormItem>
                <FormLabel>
                  <span
                    className={`${!isSelected ? "text-gray-500" : "text-secondary"}`}
                  >
                    Ticket price
                    {isSelected ? (
                      <>
                        {` (minimum price ${minimumPrice} `}
                        <img
                          src={getCurrencySymbol()}
                          className="h-6 aspect-square inline"
                          alt="currency"
                        />
                        {")"}
                      </>
                    ) : (
                      "(select the place to enter the price)"
                    )}
                  </span>
                </FormLabel>
                <FormControl>
                  <Input
                    {...field}
                    disabled={!isSelected}
                    type="text"
                    inputMode="decimal"
                    onChange={(e) => field.onChange(e.target.value)}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            );
          }}
        />
        <CategorySection control={form.control} selectedPlace={selectedPlace} />
        <div className="sticky bottom-5 w-full mt-5">
          <WithSubmitCheck {...props}>
            <Button className="block mx-auto" type="submit" variant="default">
              Submit
            </Button>
          </WithSubmitCheck>
        </div>
      </form>
    </Form>
  );
};

Graph