"use client";

import * as React from "react";
import { Loader2 } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { api } from "@/lib/trpc";
import { cn } from "@/lib/utils";

export type FormField = {
  id: string;
  label: string;
  type: string;
  required: boolean;
  placeholder?: string;
  helpText?: string;
  options?: { label: string; value: string }[];
  mappedProperty: string | null;
  validation?: { min?: number; max?: number };
  conditionals?: {
    fieldId: string;
    operator: string;
    value: unknown;
    action: string;
  }[];
};

function matchesConditional(
  fieldValue: unknown,
  operator: string,
  expected: unknown,
): boolean {
  switch (operator) {
    case "eq":
      return fieldValue === expected;
    case "neq":
      return fieldValue !== expected;
    case "contains": {
      const a = String(fieldValue ?? "").toLowerCase();
      const b = String(expected ?? "").toLowerCase();
      return a.includes(b);
    }
    case "isEmpty":
      return (
        fieldValue === undefined ||
        fieldValue === null ||
        fieldValue === "" ||
        (Array.isArray(fieldValue) && fieldValue.length === 0)
      );
    default:
      return true;
  }
}

/** Resolves whether a field should be shown given current answer values and its conditionals. */
export function isFieldVisible(
  field: FormField,
  values: Record<string, unknown>,
): boolean {
  if (!field.conditionals?.length) return true;

  let visible = true;
  for (const c of field.conditionals) {
    const other = values[c.fieldId];
    const ok = matchesConditional(other, c.operator, c.value);
    if (c.action === "hide" && ok) visible = false;
    if (c.action === "show" && !ok) visible = false;
  }
  return visible;
}

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function validateField(
  field: FormField,
  value: unknown,
  visible: boolean,
): string | null {
  if (!visible) return null;

  const skip = new Set(["section_header", "divider"]);
  if (skip.has(field.type)) return null;

  const empty =
    value === undefined ||
    value === null ||
    value === "" ||
    (Array.isArray(value) && value.length === 0);

  if (field.required && empty) {
    return `${field.label || "This field"} is required`;
  }
  if (empty) return null;

  if (field.type === "email" && typeof value === "string" && !EMAIL_RE.test(value)) {
    return "Enter a valid email address";
  }

  if (field.type === "number" && typeof value === "number") {
    const { min, max } = field.validation ?? {};
    if (min !== undefined && value < min) return `Must be at least ${min}`;
    if (max !== undefined && value > max) return `Must be at most ${max}`;
  }

  if (field.type === "rating" && typeof value === "number") {
    const min = field.validation?.min ?? 1;
    const max = field.validation?.max ?? 5;
    if (value < min || value > max) return `Pick a rating between ${min} and ${max}`;
  }

  return null;
}

function parseFields(raw: unknown): FormField[] {
  if (!Array.isArray(raw)) return [];
  return raw as FormField[];
}

function defaultValueForField(field: FormField): unknown {
  switch (field.type) {
    case "checkbox":
      return false;
    case "multi_select":
      return [];
    case "rating": {
      const min = field.validation?.min ?? 1;
      return min;
    }
    default:
      return "";
  }
}

export interface FormRendererProps {
  formId: string;
  onSubmitted?: (objectId: string) => void;
  className?: string;
}

export function FormRenderer({ formId, onSubmitted, className }: FormRendererProps) {
  const formQuery = api.forms.getById.useQuery({ id: formId }, { enabled: Boolean(formId) });

  const fields = React.useMemo(
    () => parseFields(formQuery.data?.fields),
    [formQuery.data?.fields],
  );

  const [values, setValues] = React.useState<Record<string, unknown>>({});
  const [errors, setErrors] = React.useState<Record<string, string>>({});
  const [submitted, setSubmitted] = React.useState(false);

  React.useEffect(() => {
    setSubmitted(false);
    setErrors({});
    setValues({});
  }, [formId]);

  React.useEffect(() => {
    if (!fields.length) return;
    setValues((prev) => {
      const next = { ...prev };
      for (const f of fields) {
        if (!(f.id in next)) {
          next[f.id] = defaultValueForField(f);
        }
      }
      return next;
    });
  }, [fields]);

  const confirmationMessage = React.useMemo(() => {
    const s = formQuery.data?.settings;
    if (s && typeof s === "object" && s !== null && "confirmationMessage" in s) {
      const m = (s as { confirmationMessage?: unknown }).confirmationMessage;
      if (typeof m === "string" && m.trim()) return m.trim();
    }
    return "Thank you — your response was recorded.";
  }, [formQuery.data?.settings]);

  const submitMutation = api.forms.submit.useMutation({
    onSuccess: (result) => {
      setSubmitted(true);
      onSubmitted?.(result.object.id);
    },
  });

  const setField = (id: string, v: unknown) => {
    setValues((p) => ({ ...p, [id]: v }));
    setErrors((p) => {
      const { [id]: _, ...rest } = p;
      return rest;
    });
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!formQuery.data) return;

    const nextErrors: Record<string, string> = {};
    for (const f of fields) {
      const vis = isFieldVisible(f, values);
      const err = validateField(f, values[f.id], vis);
      if (err) nextErrors[f.id] = err;
    }

    setErrors(nextErrors);
    if (Object.keys(nextErrors).length > 0) return;

    const data: Record<string, unknown> = {};
    for (const f of fields) {
      if (!isFieldVisible(f, values)) continue;
      if (f.type === "section_header" || f.type === "divider") continue;
      data[f.id] = values[f.id];
    }

    submitMutation.mutate({ formId, data });
  };

  if (formQuery.isPending) {
    return (
      <div className={cn("flex items-center justify-center py-16", className)}>
        <Loader2 className="size-8 animate-spin text-muted-foreground" aria-hidden />
        <span className="sr-only">Loading form</span>
      </div>
    );
  }

  if (formQuery.isError || !formQuery.data) {
    return (
      <p className={cn("text-sm text-muted-foreground", className)}>
        Could not load this form.
      </p>
    );
  }

  if (submitted) {
    return (
      <div
        className={cn(
          "rounded-lg border border-border bg-muted/30 px-6 py-8 text-center",
          className,
        )}
        role="status"
      >
        <p className="text-sm font-medium text-foreground">{confirmationMessage}</p>
        <Badge variant="secondary" className="mt-4">
          Submitted
        </Badge>
      </div>
    );
  }

  const form = formQuery.data;

  return (
    <form onSubmit={handleSubmit} className={cn("space-y-6", className)}>
      {form.description ? (
        <p className="text-sm text-muted-foreground">{form.description}</p>
      ) : null}

      {fields.map((field) => {
        if (!isFieldVisible(field, values)) return null;

        if (field.type === "section_header") {
          return (
            <h3
              key={field.id}
              className="border-b border-border pb-1 text-sm font-semibold tracking-tight"
            >
              {field.label}
            </h3>
          );
        }

        if (field.type === "divider") {
          return <hr key={field.id} className="border-border" />;
        }

        const err = errors[field.id];
        const v = values[field.id];

        const inputTypes = new Set([
          "short_text",
          "text",
          "long_text",
          "textarea",
          "number",
          "email",
          "url",
          "date",
          "datetime",
          "datetime-local",
          "select",
          "multi_select",
          "checkbox",
          "radio",
          "rating",
          "file_upload",
        ]);

        return (
          <div key={field.id} className="space-y-1.5">
            <label
              htmlFor={field.type === "rating" ? undefined : field.id}
              className="text-sm font-medium leading-none"
            >
              {field.label}
              {field.required ? (
                <span className="text-destructive" aria-hidden>
                  {" "}
                  *
                </span>
              ) : null}
            </label>
            {field.helpText ? (
              <p className="text-xs text-muted-foreground">{field.helpText}</p>
            ) : null}

            {field.type === "short_text" || field.type === "text" ? (
              <Input
                id={field.id}
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                placeholder={field.placeholder}
                aria-invalid={!!err}
                aria-describedby={err ? `${field.id}-err` : undefined}
              />
            ) : null}

            {field.type === "long_text" || field.type === "textarea" ? (
              <textarea
                id={field.id}
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                placeholder={field.placeholder}
                rows={4}
                aria-invalid={!!err}
                className={cn(
                  "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
                  "ring-offset-background placeholder:text-muted-foreground",
                  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
                  "disabled:cursor-not-allowed disabled:opacity-50",
                )}
              />
            ) : null}

            {field.type === "number" ? (
              <Input
                id={field.id}
                type="number"
                value={
                  typeof v === "number"
                    ? String(v)
                    : v === "" || v === undefined
                      ? ""
                      : String(v ?? "")
                }
                onChange={(e) => {
                  const raw = e.target.value;
                  if (raw === "") setField(field.id, "");
                  else setField(field.id, Number(raw));
                }}
                placeholder={field.placeholder}
                min={field.validation?.min}
                max={field.validation?.max}
                aria-invalid={!!err}
              />
            ) : null}

            {field.type === "email" ? (
              <Input
                id={field.id}
                type="email"
                autoComplete="email"
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                placeholder={field.placeholder ?? "you@example.com"}
                aria-invalid={!!err}
              />
            ) : null}

            {field.type === "url" ? (
              <Input
                id={field.id}
                type="url"
                inputMode="url"
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                placeholder={field.placeholder ?? "https://"}
                aria-invalid={!!err}
              />
            ) : null}

            {field.type === "date" ? (
              <Input
                id={field.id}
                type="date"
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                aria-invalid={!!err}
              />
            ) : null}

            {field.type === "datetime" || field.type === "datetime-local" ? (
              <Input
                id={field.id}
                type="datetime-local"
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                aria-invalid={!!err}
              />
            ) : null}

            {field.type === "select" ? (
              <select
                id={field.id}
                value={typeof v === "string" ? v : ""}
                onChange={(e) => setField(field.id, e.target.value)}
                aria-invalid={!!err}
                className={cn(
                  "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
                  "ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
                )}
              >
                <option value="">{field.placeholder ?? "Choose…"}</option>
                {(field.options ?? []).map((opt) => (
                  <option key={opt.value} value={opt.value}>
                    {opt.label}
                  </option>
                ))}
              </select>
            ) : null}

            {field.type === "multi_select" ? (
              <div className="flex flex-col gap-2 rounded-md border border-input p-3">
                {(field.options ?? []).map((opt) => {
                  const selected = Array.isArray(v) && v.includes(opt.value);
                  return (
                    <label key={opt.value} className="flex items-center gap-2 text-sm">
                      <input
                        type="checkbox"
                        checked={!!selected}
                        onChange={() => {
                          const cur = Array.isArray(v) ? [...v] : [];
                          if (selected) {
                            setField(
                              field.id,
                              cur.filter((x) => x !== opt.value),
                            );
                          } else {
                            setField(field.id, [...cur, opt.value]);
                          }
                        }}
                      />
                      {opt.label}
                    </label>
                  );
                })}
              </div>
            ) : null}

            {field.type === "checkbox" ? (
              <label className="flex items-center gap-2 text-sm">
                <input
                  type="checkbox"
                  checked={v === true}
                  onChange={(e) => setField(field.id, e.target.checked)}
                  aria-invalid={!!err}
                />
                {field.placeholder ?? "Yes"}
              </label>
            ) : null}

            {field.type === "radio" ? (
              <div className="flex flex-col gap-2">
                {(field.options ?? []).map((opt) => (
                  <label key={opt.value} className="flex items-center gap-2 text-sm">
                    <input
                      type="radio"
                      name={field.id}
                      value={opt.value}
                      checked={v === opt.value}
                      onChange={() => setField(field.id, opt.value)}
                    />
                    {opt.label}
                  </label>
                ))}
              </div>
            ) : null}

            {field.type === "rating" ? (
              <RatingStars
                id={field.id}
                min={field.validation?.min ?? 1}
                max={field.validation?.max ?? 5}
                value={typeof v === "number" ? v : field.validation?.min ?? 1}
                onChange={(n) => setField(field.id, n)}
              />
            ) : null}

            {field.type === "file_upload" ? (
              <Input
                id={field.id}
                type="file"
                onChange={(e) => {
                  const file = e.target.files?.[0];
                  setField(field.id, file ? file.name : "");
                }}
                aria-invalid={!!err}
              />
            ) : null}

            {!inputTypes.has(field.type) ? (
              <p className="text-xs text-muted-foreground">
                Unsupported field type: {field.type}
              </p>
            ) : null}

            {err ? (
              <p id={`${field.id}-err`} className="text-xs text-destructive" role="alert">
                {err}
              </p>
            ) : null}
          </div>
        );
      })}

      {submitMutation.isError ? (
        <p className="text-sm text-destructive" role="alert">
          Something went wrong while submitting. Please try again.
        </p>
      ) : null}

      <Button type="submit" disabled={submitMutation.isPending}>
        {submitMutation.isPending ? (
          <>
            <Loader2 className="size-4 animate-spin" aria-hidden />
            Submitting…
          </>
        ) : (
          "Submit"
        )}
      </Button>
    </form>
  );
}

function RatingStars({
  id,
  min,
  max,
  value,
  onChange,
}: {
  id: string;
  min: number;
  max: number;
  value: number;
  onChange: (n: number) => void;
}) {
  const stars = React.useMemo(
    () => Array.from({ length: max - min + 1 }, (_, i) => min + i),
    [min, max],
  );

  return (
    <div id={id} className="flex flex-wrap items-center gap-1" role="group">
      {stars.map((n) => (
        <button
          key={n}
          type="button"
          onClick={() => onChange(n)}
          className={cn(
            "rounded p-0.5 text-2xl leading-none transition-colors",
            n <= value ? "text-amber-500" : "text-muted-foreground/30 hover:text-muted-foreground/60",
          )}
          aria-label={`${n} stars`}
          aria-pressed={n === value}
        >
          ★
        </button>
      ))}
    </div>
  );
}
