import { useRef } from "react";
import { flushSync } from "react-dom";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { zodResolver } from "@hookform/resolvers/zod";
import { ImagePlus, Loader2, X } from "lucide-react";
import { useController, useForm, useFormContext } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";

import {
  CheckboxField as GenericCheckboxField,
  DropdownField as GenericDropdownField,
  RequiredMark,
  TextAreaField as GenericTextAreaField,
  TextField as GenericTextField,
} from "@/components/app/FormFields";
import { Page } from "@/components/app/Page";
import { PageHeading } from "@/components/app/Primitives";
import { UnsavedChangesGuard } from "@/components/app/UnsavedChangesGuard";
import { Button } from "@/components/ui/button";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  useCreateDestination,
  useUpdateDestination,
  type Destination,
} from "@/features/destinations/api";
import { useUploadImage } from "@/features/uploads/api";
import { ApiClientError } from "@/lib/api";
import { toWebP } from "@/lib/image";

export const Route = createFileRoute("/destinations/new")({
  head: () => ({
    meta: [{ title: "Add destination | KareVoyage Operations" }],
  }),
  component: NewDestinationPage,
});

const toSlug = (name: string) =>
  name
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");

const destinationFormSchema = z.object({
  name: z.string().trim().min(2, "Enter a destination name"),
  slug: z
    .string()
    .trim()
    .regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "Lowercase, hyphen-separated"),
  country: z.string().trim().min(1, "Required"),
  region: z.string(),
  travelStyle: z.string(),
  bestTime: z.string(),
  description: z.string(),
  heroImageUrl: z.string(),
  displayOrder: z.string(),
  featured: z.boolean(),
  isActive: z.boolean(),
});

type DestinationFormValues = z.infer<typeof destinationFormSchema>;

const TextField = GenericTextField<DestinationFormValues>;
const TextAreaField = GenericTextAreaField<DestinationFormValues>;
const DropdownField = GenericDropdownField<DestinationFormValues>;
const CheckboxField = GenericCheckboxField<DestinationFormValues>;

const DEFAULT_VALUES: DestinationFormValues = {
  name: "",
  slug: "",
  country: "",
  region: "",
  travelStyle: "",
  bestTime: "",
  description: "",
  heroImageUrl: "",
  displayOrder: "0",
  featured: false,
  isActive: true,
};

function destinationToFormValues(destination: Destination): DestinationFormValues {
  const str = (v: string | null | undefined) => v ?? "";
  return {
    name: destination.name,
    slug: destination.slug,
    country: destination.country,
    region: str(destination.region),
    travelStyle: str(destination.travelStyle),
    bestTime: str(destination.bestTime),
    description: str(destination.description),
    heroImageUrl: str(destination.heroImageUrl),
    displayOrder: String(destination.displayOrder),
    featured: destination.featured,
    isActive: destination.isActive,
  };
}

/** Same upload-on-select + client-side WebP conversion pattern as the Tour form's PrimaryImageField. */
function HeroImageField() {
  const { control } = useFormContext<DestinationFormValues>();
  const { field } = useController({ control, name: "heroImageUrl" });
  const uploadImage = useUploadImage();
  const inputRef = useRef<HTMLInputElement>(null);
  const url = field.value;

  const handleFile = async (file: File | undefined) => {
    if (!file) return;
    const webpFile = await toWebP(file).catch(() => file);
    uploadImage.mutate(webpFile, {
      onSuccess: (result) => field.onChange(result.url),
      onError: (err) =>
        toast.error(err instanceof ApiClientError ? err.message : "Couldn't upload the image."),
    });
  };

  return (
    <div className="space-y-1.5 lg:col-span-2">
      <Label className="kv-eyebrow">Hero Image</Label>
      <input
        ref={inputRef}
        type="file"
        accept="image/*"
        className="hidden"
        onChange={(e) => {
          void handleFile(e.target.files?.[0]);
          e.target.value = "";
        }}
      />
      {url ? (
        <div className="group relative overflow-hidden rounded-lg border border-border">
          <img src={url} alt="Destination hero" className="h-40 w-full object-cover" />
          <button
            type="button"
            onClick={() => field.onChange("")}
            className="absolute right-2 top-2 rounded-full bg-background/90 p-1.5 text-foreground shadow-sm transition-colors hover:bg-background"
            aria-label="Remove hero image"
          >
            <X className="h-3.5 w-3.5" />
          </button>
        </div>
      ) : (
        <button
          type="button"
          onClick={() => inputRef.current?.click()}
          disabled={uploadImage.isPending}
          className="flex w-full flex-col items-center justify-center gap-1.5 rounded-lg border border-dashed border-border-strong bg-surface-muted/40 px-4 py-8 text-center transition-colors hover:border-primary/40 hover:bg-primary-soft/40 disabled:opacity-60"
        >
          {uploadImage.isPending ? (
            <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
          ) : (
            <ImagePlus className="h-6 w-6 text-muted-foreground" />
          )}
          <span className="text-sm font-medium text-foreground">
            {uploadImage.isPending ? "Uploading…" : "Upload image"}
          </span>
          <span className="text-xs text-muted-foreground">Recommended size: 1200x800px</span>
        </button>
      )}
    </div>
  );
}

export function DestinationFormPage(
  props: { mode: "create" } | { mode: "edit"; destination: Destination },
) {
  const { mode } = props;
  const destination = props.mode === "edit" ? props.destination : undefined;
  const navigate = useNavigate();
  const createDestination = useCreateDestination();
  const updateDestination = useUpdateDestination(destination?.id ?? "");
  const isPending = mode === "edit" ? updateDestination.isPending : createDestination.isPending;

  const form = useForm<DestinationFormValues>({
    resolver: zodResolver(destinationFormSchema),
    defaultValues: destination ? destinationToFormValues(destination) : DEFAULT_VALUES,
  });

  // Destinations has no separate read-only detail page — "/destinations/$id" is this same edit
  // form, so Cancel always returns to the list (same target as the Unsaved Changes guard's
  // "Discard"), not back to the edit form itself.
  const cancelTo = () => navigate({ to: "/destinations" });

  const handleSave = form.handleSubmit(
    (values) => {
      const payload = {
        name: values.name.trim(),
        slug: values.slug.trim(),
        country: values.country.trim(),
        region: values.region || undefined,
        travelStyle: values.travelStyle || undefined,
        bestTime: values.bestTime || undefined,
        description: values.description || undefined,
        heroImageUrl: values.heroImageUrl || undefined,
        displayOrder: values.displayOrder.trim() === "" ? 0 : Number(values.displayOrder),
        featured: values.featured,
        isActive: values.isActive,
      };
      const onError = (err: unknown) =>
        toast.error(err instanceof ApiClientError ? err.message : "Couldn't save the destination.");

      if (mode === "edit" && destination) {
        updateDestination.mutate(payload, {
          onSuccess: () => {
            toast.success("Destination updated.");
            // Mark the form clean before navigating — otherwise UnsavedChangesGuard's blocker
            // (still seeing isDirty: true, since nothing else resets it after a successful
            // submit) intercepts this exact redirect too.
            flushSync(() => form.reset(values));
            navigate({ to: "/destinations/$id", params: { id: destination.id } });
          },
          onError,
        });
      } else {
        createDestination.mutate(payload, {
          onSuccess: (created) => {
            toast.success(`${created.name} created.`);
            flushSync(() => form.reset(values));
            // Back to the list, not straight into the edit form — Destinations has no separate
            // read-only view, so landing on "Edit Destination" the instant it's created skips the
            // "here's what you just made" moment. Editing is still one click away from the list.
            navigate({ to: "/destinations" });
          },
          onError,
        });
      }
    },
    () => toast.error("Check the highlighted fields before saving."),
  );

  return (
    <Page className="space-y-6">
      <PageHeading
        title={mode === "edit" ? "Edit Destination" : "Add Destination"}
        description={
          mode === "edit"
            ? `Update ${destination?.name}'s details.`
            : "All fields marked with * are required."
        }
        actions={
          <Button variant="outline" onClick={cancelTo}>
            Cancel
          </Button>
        }
      />

      <Form {...form}>
        <form className="space-y-6" onSubmit={(e) => e.preventDefault()}>
          <div className="rounded-xl border border-border bg-surface p-6">
            <div className="grid gap-6 lg:grid-cols-2">
              <FormField_Name />
              <TextField name="slug" label="Slug / URL" placeholder="Enter slug (auto-generated)" hint="Used in website URL" required />
              <DropdownField name="country" label="Country" groupKey="country" required />
              <DropdownField name="region" label="Region" groupKey="region" />
              <DropdownField name="travelStyle" label="Travel Style" groupKey="travel_style" />
              <TextField name="bestTime" label="Best Time To Visit" placeholder="e.g. May – Sep" />
              <TextField name="displayOrder" label="Display Order" type="number" hint="Lower number shows first" />
              <div className="lg:col-span-2">
                <TextAreaField name="description" label="Description" placeholder="Enter a short description of the destination" />
              </div>
              <HeroImageField />
              <CheckboxField name="featured" label="Featured Destination" hint="Show on top of website / home sections" />
              <CheckboxField name="isActive" label="Active" hint="Make destination active" />
            </div>
          </div>

          <div className="flex flex-wrap items-center justify-end gap-2 border-t border-border pt-5">
            <Button type="button" variant="outline" onClick={cancelTo}>
              Cancel
            </Button>
            <Button type="button" disabled={isPending} onClick={() => void handleSave()}>
              {mode === "edit" ? "Save changes" : "Create destination"}
            </Button>
          </div>
        </form>
      </Form>
      <UnsavedChangesGuard
        isDirty={form.formState.isDirty}
        onSave={() => void handleSave()}
        isSaving={isPending}
      />
    </Page>
  );
}

/** Name field with auto-slug, same pattern as the Tour form's title→slug sync. */
function FormField_Name() {
  const { control, setValue, getValues } = useFormContext<DestinationFormValues>();
  return (
    <FormField
      control={control}
      name="name"
      render={({ field }) => (
        <FormItem>
          <FormLabel className="kv-eyebrow">
            Destination Name
            <RequiredMark />
          </FormLabel>
          <FormControl>
            <Input
              placeholder="Enter destination name"
              {...field}
              onChange={(e) => {
                field.onChange(e);
                if (!getValues("slug") || getValues("slug") === toSlug(field.value)) {
                  setValue("slug", toSlug(e.target.value));
                }
              }}
            />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  );
}

function NewDestinationPage() {
  return <DestinationFormPage mode="create" />;
}
