"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, useForm } from "react-hook-form";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { FormField } from "@/components/ui/form-field";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { useT } from "@/i18n";
import { applyFieldErrors, friendlyErrorMessage } from "@/lib/errors";
import { queryKeys } from "@/lib/query-keys";
import { updateDriver, type UpdateDriverInput } from "@/services/drivers";
import type { Driver } from "@/types/driver";

/**
 * The record fields, mirroring `UpdateDriverRequest`.
 *
 * Neither the status nor the activation flag is here, on purpose: each is a decision with its own
 * endpoint — approving assigns a vehicle, disabling revokes tokens — and putting them in a form
 * beside a name would make them look like corrections.
 */
const schema = z.object({
  name: z.string().trim().min(1, "errors.required").max(255, "errors.generic"),
  // The server's own shape. A phone typed without its country code is the commonest way this form
  // fails, and catching it here says so before the round trip.
  phone: z.string().trim().regex(/^\+[0-9]{8,15}$/, "drivers.edit.phoneShape"),
  email: z.union([z.literal(""), z.string().trim().email("errors.generic").max(255)]),
  national_id: z.string().trim().min(1, "errors.required").max(50, "errors.generic"),
  date_of_birth: z.string().min(1, "errors.required"),
  employment_type: z.enum(["employee", "freelance"]),
  driving_license_number: z.string().trim().min(1, "errors.required").max(50, "errors.generic"),
  driving_license_expires_at: z.string().min(1, "errors.required"),
});

type EditValues = z.infer<typeof schema>;

interface EditCaptainDialogProps {
  driver: Driver;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}

/**
 * Correct a captain's record.
 *
 * **Only what changed is sent.** Every field is optional to the server, and the unique rules on the
 * phone, email and national id ignore this captain — but sending an unchanged field still makes it
 * part of the write, and a form that resubmits everything turns one person's correction into an
 * overwrite of whatever somebody else fixed in the meantime.
 */
export function EditCaptainDialog({ driver, open, onOpenChange }: EditCaptainDialogProps) {
  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="max-w-lg">
        {/* Keyed on the captain and remounted per open, so the form always starts from the record
            as it is now rather than from the values loaded when the page first rendered. */}
        {open ? <EditForm driver={driver} onOpenChange={onOpenChange} /> : null}
      </DialogContent>
    </Dialog>
  );
}

function EditForm({ driver, onOpenChange }: Omit<EditCaptainDialogProps, "open">) {
  const t = useT();
  const queryClient = useQueryClient();

  const initial: EditValues = {
    name: driver.name,
    phone: driver.phone,
    email: driver.email ?? "",
    national_id: driver.national_id ?? "",
    // The API sends dates as ISO timestamps; a date input only accepts the day part.
    date_of_birth: (driver.date_of_birth ?? "").slice(0, 10),
    employment_type: driver.employment_type,
    driving_license_number: driver.driving_license_number ?? "",
    driving_license_expires_at: (driver.driving_license_expires_at ?? "").slice(0, 10),
  };

  const form = useForm<EditValues>({
    resolver: zodResolver(schema),
    mode: "onTouched",
    defaultValues: initial,
  });

  const save = useMutation({
    mutationFn: (input: UpdateDriverInput) => updateDriver(driver.uuid, input),
    onSuccess: (response) => {
      toast.success(response.Message ?? t("drivers.edit.done"));
      onOpenChange(false);
      void queryClient.invalidateQueries({ queryKey: queryKeys.drivers.detail(driver.uuid) });
      void queryClient.invalidateQueries({ queryKey: ["drivers"] });
    },
    onError: (error) => {
      // A phone or national id already on another captain is the refusal this form actually gets,
      // and it means nothing unless it appears under that field.
      const shown = applyFieldErrors(
        error,
        (field, message) => form.setError(field as never, { message }),
        { only: ["name", "phone", "email", "national_id", "date_of_birth", "employment_type", "driving_license_number", "driving_license_expires_at"] },
      );
      if (shown === 0) toast.error(friendlyErrorMessage(error, t));
    },
  });

  /** The changed fields only — an empty email is a deliberate `null`, not an omission. */
  const diff = (values: EditValues): UpdateDriverInput => {
    const input: UpdateDriverInput = {};
    if (values.name !== initial.name) input.name = values.name;
    if (values.phone !== initial.phone) input.phone = values.phone;
    if (values.email !== initial.email) input.email = values.email === "" ? null : values.email;
    if (values.national_id !== initial.national_id) input.national_id = values.national_id;
    if (values.date_of_birth !== initial.date_of_birth) input.date_of_birth = values.date_of_birth;
    if (values.employment_type !== initial.employment_type) {
      input.employment_type = values.employment_type;
    }
    if (values.driving_license_number !== initial.driving_license_number) {
      input.driving_license_number = values.driving_license_number;
    }
    if (values.driving_license_expires_at !== initial.driving_license_expires_at) {
      input.driving_license_expires_at = values.driving_license_expires_at;
    }
    return input;
  };

  return (
    <>
      <DialogHeader>
        <DialogTitle>{t("drivers.edit.title")}</DialogTitle>
        <DialogDescription>{t("drivers.edit.body")}</DialogDescription>
      </DialogHeader>

      <form
        className="space-y-4"
        onSubmit={form.handleSubmit((values) => {
          if (save.isPending) return;
          const input = diff(values);
          if (Object.keys(input).length === 0) {
            toast.info(t("drivers.edit.noChanges"));
            return;
          }
          save.mutate(input);
        })}
      >
        <div className="grid gap-4 sm:grid-cols-2">
          <FormField
            label={t("drivers.fieldName")}
            htmlFor="captain-name"
            required
            error={form.formState.errors.name?.message}
          >
            <Input id="captain-name" {...form.register("name")} />
          </FormField>

          <FormField
            label={t("drivers.fieldPhone")}
            htmlFor="captain-phone"
            required
            error={form.formState.errors.phone?.message}
            hint={t("drivers.edit.phoneHint")}
          >
            <Input id="captain-phone" dir="ltr" {...form.register("phone")} />
          </FormField>

          <FormField
            label={t("drivers.fieldEmail")}
            htmlFor="captain-email"
            error={form.formState.errors.email?.message}
            hint={t("drivers.edit.emailHint")}
          >
            <Input id="captain-email" type="email" dir="ltr" {...form.register("email")} />
          </FormField>

          <FormField
            label={t("drivers.fieldNationalId")}
            htmlFor="captain-national-id"
            required
            error={form.formState.errors.national_id?.message}
          >
            <Input id="captain-national-id" dir="ltr" {...form.register("national_id")} />
          </FormField>

          <FormField
            label={t("drivers.fieldDateOfBirth")}
            htmlFor="captain-dob"
            required
            error={form.formState.errors.date_of_birth?.message}
          >
            <Input id="captain-dob" type="date" {...form.register("date_of_birth")} />
          </FormField>

          <FormField label={t("drivers.fieldEmploymentType")} htmlFor="captain-employment" required>
            {/* Through a controller rather than `watch()`: the subscription belongs to this one
                field, and watching the whole form here is what makes a form re-render on every
                keystroke somewhere else. */}
            <Controller
              control={form.control}
              name="employment_type"
              render={({ field }) => (
                <Select value={field.value} onValueChange={field.onChange}>
                  <SelectTrigger id="captain-employment">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="employee">{t("drivers.employment.employee")}</SelectItem>
                    <SelectItem value="freelance">{t("drivers.employment.freelance")}</SelectItem>
                  </SelectContent>
                </Select>
              )}
            />
          </FormField>

          <FormField
            label={t("drivers.fieldDrivingLicenseNumber")}
            htmlFor="captain-license"
            required
            error={form.formState.errors.driving_license_number?.message}
          >
            <Input id="captain-license" dir="ltr" {...form.register("driving_license_number")} />
          </FormField>

          <FormField
            label={t("drivers.fieldDrivingLicenseExpires")}
            htmlFor="captain-license-expiry"
            required
            error={form.formState.errors.driving_license_expires_at?.message}
            hint={t("drivers.edit.expiryHint")}
          >
            <Input
              id="captain-license-expiry"
              type="date"
              {...form.register("driving_license_expires_at")}
            />
          </FormField>
        </div>

        <DialogFooter>
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            {t("common.cancel")}
          </Button>
          <Button type="submit" disabled={save.isPending}>
            {save.isPending ? t("common.saving") : t("common.save")}
          </Button>
        </DialogFooter>
      </form>
    </>
  );
}
