"use client";

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

import { Example } from "@/components/design-system/example";
import { Section } from "@/components/design-system/section";
import { Button } from "@/components/ui/button";
import { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
import { Switch } from "@/components/ui/switch";
import { toast } from "@/components/ui/toast";
import { products } from "@/lib/mock-data";

import { DatePicker } from "./dates";

const CATEGORIES = [...new Set(products.map((p) => p.category))];

const isNum = (v: string) => v.trim() !== "" && !Number.isNaN(Number(v));

// Numbers stay strings in the form (that is what inputs hold) and are checked here, then parsed on submit.
const schema = z
  .object({
    type: z.enum(["Product", "Service"]),
    name: z.string().trim().min(2, "Enter at least 2 characters."),
    category: z.string().min(1, "Choose a category."),
    barcode: z.string(),
    cost: z.string(),
    price: z.string().refine((v) => isNum(v) && Number(v) > 0, "Enter a selling price above zero."),
    stock: z.string(),
    reorder: z.string(),
    expiry: z.date().optional(),
    requiresRx: z.boolean(),
  })
  .superRefine((v, ctx) => {
    if (v.type === "Service") return;
    const need = (path: keyof typeof v, ok: boolean, message: string) => !ok && ctx.addIssue({ code: "custom", path: [path], message });
    need("barcode", /^\d{8,14}$/.test(v.barcode), "Barcode must be 8 to 14 digits.");
    need("cost", isNum(v.cost) && Number(v.cost) >= 0, "Enter a cost price, 0 or more.");
    need("stock", /^\d+$/.test(v.stock), "Enter a whole number of units.");
    need("reorder", /^\d+$/.test(v.reorder), "Enter a whole reorder level.");
    need("expiry", v.expiry !== undefined, "Pick an expiry date.");
    if (isNum(v.cost) && isNum(v.price) && Number(v.price) < Number(v.cost)) need("price", false, "Price is below cost.");
  });

type FormValues = z.input<typeof schema>;

const defaults: FormValues = { type: "Product", name: "", category: "", barcode: "", cost: "", price: "", stock: "", reorder: "", expiry: undefined, requiresRx: false };

function kes(prefix = "KES") {
  return (
    <InputGroupAddon>
      <InputGroupText>{prefix}</InputGroupText>
    </InputGroupAddon>
  );
}

export function ProductForm() {
  const {
    register,
    control,
    handleSubmit,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<FormValues>({ resolver: zodResolver(schema), defaultValues: defaults });

  // useWatch, not watch(): watch() returns a fresh function each render, which the React Compiler can't memoise.
  const isService = useWatch({ control, name: "type" }) === "Service";

  const onSubmit = async (v: FormValues) => {
    await new Promise((r) => setTimeout(r, 700));
    toast.add({
      type: "success",
      title: `${v.name} added`,
      description: v.type === "Service" ? `Service at KES ${Number(v.price).toLocaleString("en-US")}` : `${v.stock} units at KES ${Number(v.price).toLocaleString("en-US")}`,
    });
    reset(defaults);
  };

  const onInvalid = () => toast.add({ type: "error", title: "Fix the highlighted fields", description: "The product was not saved." });

  const num = "py-0 text-sm tabular-nums focus:ring-0";

  return (
    <Section
      id="product-form"
      title="Complete form: Add product"
      description="react-hook-form with a zod resolver. Submit empty to see every inline error and an error toast; fill it in for a success toast. Switching to Service drops the stock rules, as in the inventory page."
    >
      <Example
        title="Add product"
        description="Validated on submit, re-validated on change"
        code={`const form = useForm<FormValues>({ resolver: zodResolver(schema), defaultValues });
<form onSubmit={handleSubmit(onSubmit, onInvalid)} noValidate>
  <Field data-invalid={!!errors.name}>
    <FieldLabel htmlFor="name">Product name</FieldLabel>
    <Input id="name" aria-invalid={!!errors.name} {...register("name")} />
    <FieldError errors={[errors.name]} />
  </Field>
</form>`}
      >
        <form onSubmit={handleSubmit(onSubmit, onInvalid)} noValidate className="max-w-2xl">
          <FieldGroup>
            <Controller
              control={control}
              name="type"
              render={({ field }) => (
                <FieldSet>
                  <FieldLegend variant="label">Type</FieldLegend>
                  <RadioGroup value={field.value} onValueChange={field.onChange} className="flex gap-6">
                    {(["Product", "Service"] as const).map((t) => (
                      <Field key={t} orientation="horizontal" className="w-auto">
                        <RadioGroupItem id={`pf-type-${t}`} value={t} />
                        <FieldLabel htmlFor={`pf-type-${t}`} className="font-normal">
                          {t}
                        </FieldLabel>
                      </Field>
                    ))}
                  </RadioGroup>
                </FieldSet>
              )}
            />

            <div className="grid gap-5 sm:grid-cols-2">
              <Field data-invalid={!!errors.name}>
                <FieldLabel htmlFor="pf-name">Name</FieldLabel>
                <Input id="pf-name" placeholder="Panadol" aria-invalid={!!errors.name} {...register("name")} />
                <FieldError errors={[errors.name]} />
              </Field>
              <Controller
                control={control}
                name="category"
                render={({ field, fieldState }) => (
                  <Field data-invalid={fieldState.invalid}>
                    <FieldLabel htmlFor="pf-category">Category</FieldLabel>
                    <Select value={field.value || null} onValueChange={(v) => field.onChange(v ?? "")}>
                      <SelectTrigger id="pf-category" className="w-full" aria-invalid={fieldState.invalid || undefined} onBlur={field.onBlur}>
                        <SelectValue placeholder="Choose category" />
                      </SelectTrigger>
                      <SelectContent>
                        {CATEGORIES.map((c) => (
                          <SelectItem key={c} value={c}>
                            {c}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <FieldError errors={[fieldState.error]} />
                  </Field>
                )}
              />
            </div>

            {!isService && (
              <Field data-invalid={!!errors.barcode}>
                <FieldLabel htmlFor="pf-barcode">Barcode</FieldLabel>
                <Input id="pf-barcode" inputMode="numeric" placeholder="6161100000137" aria-invalid={!!errors.barcode} {...register("barcode")} />
                <FieldDescription>8 to 14 digits, as printed under the bars.</FieldDescription>
                <FieldError errors={[errors.barcode]} />
              </Field>
            )}

            <div className="grid gap-5 sm:grid-cols-2">
              {!isService && (
                <Field data-invalid={!!errors.cost}>
                  <FieldLabel htmlFor="pf-cost">Cost price</FieldLabel>
                  <InputGroup aria-invalid={!!errors.cost || undefined}>
                    {kes()}
                    <InputGroupInput id="pf-cost" className={num} inputMode="decimal" placeholder="0.00" aria-invalid={!!errors.cost || undefined} {...register("cost")} />
                  </InputGroup>
                  <FieldError errors={[errors.cost]} />
                </Field>
              )}
              <Field data-invalid={!!errors.price}>
                <FieldLabel htmlFor="pf-price">Selling price</FieldLabel>
                <InputGroup aria-invalid={!!errors.price || undefined}>
                  {kes()}
                  <InputGroupInput id="pf-price" className={num} inputMode="decimal" placeholder="0.00" aria-invalid={!!errors.price || undefined} {...register("price")} />
                </InputGroup>
                <FieldError errors={[errors.price]} />
              </Field>
            </div>

            {!isService && (
              <>
                <div className="grid gap-5 sm:grid-cols-3">
                  <Field data-invalid={!!errors.stock}>
                    <FieldLabel htmlFor="pf-stock">Opening stock</FieldLabel>
                    <Input id="pf-stock" className="tabular-nums" inputMode="numeric" aria-invalid={!!errors.stock} {...register("stock")} />
                    <FieldError errors={[errors.stock]} />
                  </Field>
                  <Field data-invalid={!!errors.reorder}>
                    <FieldLabel htmlFor="pf-reorder">Reorder level</FieldLabel>
                    <Input id="pf-reorder" className="tabular-nums" inputMode="numeric" aria-invalid={!!errors.reorder} {...register("reorder")} />
                    <FieldError errors={[errors.reorder]} />
                  </Field>
                  <Controller
                    control={control}
                    name="expiry"
                    render={({ field, fieldState }) => (
                      <Field data-invalid={fieldState.invalid}>
                        <FieldLabel htmlFor="pf-expiry">Expiry date</FieldLabel>
                        <DatePicker id="pf-expiry" value={field.value} onChange={field.onChange} aria-invalid={fieldState.invalid} />
                        <FieldError errors={[fieldState.error]} />
                      </Field>
                    )}
                  />
                </div>

                <Controller
                  control={control}
                  name="requiresRx"
                  render={({ field }) => (
                    <Field orientation="horizontal" className="justify-between rounded-lg border border-border p-3">
                      <FieldContent>
                        <FieldLabel htmlFor="pf-rx">Requires prescription</FieldLabel>
                        <FieldDescription>The till asks for a prescription reference before selling.</FieldDescription>
                      </FieldContent>
                      <Switch id="pf-rx" checked={field.value} onCheckedChange={field.onChange} />
                    </Field>
                  )}
                />
              </>
            )}

            <div className="flex items-center gap-2 border-t border-border pt-4">
              <Button type="submit" disabled={isSubmitting}>
                {isSubmitting && <Spinner data-icon="inline-start" />}
                {isSubmitting ? "Saving" : "Add product"}
              </Button>
              <Button type="button" variant="ghost" onClick={() => reset(defaults)} disabled={isSubmitting}>
                Reset
              </Button>
            </div>
          </FieldGroup>
        </form>
      </Example>
    </Section>
  );
}
