"use client";

import * as React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

import { Button } from "@/components/ui/button";
import { CurrencyInput } from "@/components/ui/currency-input";
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { FormField } from "@/components/ui/form-field";
import { Typography } from "@/components/ui/typography";
import { useToggleProviderPackage } from "@/hooks/catalog/useToggleProviderPackage";
import { usePriceFormat } from "@/hooks/usePriceFormat";
import { useTranslation } from "@/hooks/useTranslation";
import {
  createProviderPriceSchema,
  type ProviderPriceFormValues,
} from "@/lib/validations/catalog.schema";
import type { ProviderPackage } from "@/types/catalog";

interface ServicePriceDialogProps {
  /** The package being priced, or null when no dialog should be shown */
  target: ProviderPackage | null;
  onClose: () => void;
}

/**
 * Sets this provider's price for one admin catalog package.
 *
 * Serves both "start offering this" and "change my price" — the backend has a
 * single write path (`optIn: true` upserts the price), so splitting them in the
 * UI would be a distinction the API doesn't make.
 *
 * The admin's min/max is enforced here as well as backend-side (it answers 400
 * outside the range), so the provider is told before the request goes out. The
 * bounds can't be interpolated into a zod message — FormField resolves keys
 * with no params — so the range is shown as the field description instead.
 */
export function ServicePriceDialog({
  target,
  onClose,
}: ServicePriceDialogProps) {
  const { t } = useTranslation();
  const { formatPrice } = usePriceFormat();

  // Retain the last target so the dialog keeps its copy while it animates out.
  // React's "adjust state during render" pattern — a ref would be read during
  // render, which the compiler rules correctly reject.
  const [lastTarget, setLastTarget] = React.useState<ProviderPackage | null>(
    null,
  );
  if (target && target !== lastTarget) setLastTarget(target);
  const shown = target ?? lastTarget;

  // The allowed range is per-package, so the schema is rebuilt when it changes.
  const schema = React.useMemo(
    () => createProviderPriceSchema(shown?.minPrice ?? 0, shown?.maxPrice ?? 0),
    [shown?.minPrice, shown?.maxPrice],
  );

  const form = useForm<ProviderPriceFormValues>({
    resolver: zodResolver(schema),
    mode: "onChange",
    reValidateMode: "onChange",
    defaultValues: { price: "" },
  });

  const toggleService = useToggleProviderPackage({ onSuccess: onClose });

  // Re-seed on every open — one dialog instance serves every row, and the
  // stored price may have changed since it first mounted.
  React.useEffect(() => {
    if (target) {
      form.reset({ price: target.price === null ? "" : String(target.price) });
    }
  }, [target, form]);

  if (!shown) return null;

  const onSubmit = (values: ProviderPriceFormValues) =>
    toggleService.mutate({
      packageId: shown.id,
      optIn: true,
      price: Number(values.price),
    });

  return (
    <Dialog
      open={!!target}
      onOpenChange={(next) => {
        if (!next) onClose();
      }}
    >
      <DialogContent>
        <DialogHeader>
          <DialogTitle>
            {shown.isOffered
              ? t("catalog.myServices.priceDialog.editTitle")
              : t("catalog.myServices.priceDialog.enableTitle")}
          </DialogTitle>
          <DialogDescription>
            {t("catalog.myServices.priceDialog.description", {
              name: shown.name,
            })}
          </DialogDescription>
        </DialogHeader>

        <form
          className="flex min-h-0 flex-1 flex-col gap-5"
          onSubmit={form.handleSubmit(onSubmit)}
        >
          <DialogBody>
            <FormField
              control={form.control}
              name="price"
              label={t("catalog.myServices.priceDialog.label")}
              showErrorOnChange
              render={({ field, hasError, id }) => (
                <div className="flex flex-col gap-1.5">
                  <CurrencyInput
                    id={id}
                    placeholder="0"
                    aria-invalid={hasError}
                    value={field.value}
                    onChange={field.onChange}
                    onBlur={field.onBlur}
                    name={field.name}
                  />
                  <Typography as="p" variant="caption" color="secondary">
                    {t("catalog.myServices.priceDialog.rangeHint", {
                      range: `${formatPrice(shown.minPrice)} - ${formatPrice(shown.maxPrice)}`,
                    })}
                  </Typography>
                </div>
              )}
            />
          </DialogBody>

          <DialogFooter>
            <Button type="button" variant="secondary" onClick={onClose}>
              {t("common.cancel")}
            </Button>
            <Button type="submit" loading={toggleService.isPending}>
              {shown.isOffered
                ? t("common.save")
                : t("catalog.myServices.priceDialog.enable")}
            </Button>
          </DialogFooter>
        </form>
      </DialogContent>
    </Dialog>
  );
}
