"use client";

import { clsx } from "clsx";
import { DateInput } from "~/components/ui/DateInput.js";
import { FieldType } from "@capakraken/shared";
import type { BlueprintFieldDefinition } from "@capakraken/shared";

interface Props {
  fieldDefs: BlueprintFieldDefinition[];
  values: Record<string, unknown>;
  onChange: (key: string, value: unknown) => void;
  errors?: Record<string, string>;
  className?: string;
}

const INPUT_BASE =
  "w-full px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 transition-colors";

const INPUT_NORMAL = "border-gray-300 bg-white text-gray-900 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100";
const INPUT_ERROR = "border-red-400 bg-red-50 text-gray-900 dark:border-red-500 dark:text-gray-100";

function inputClass(hasError: boolean) {
  return clsx(INPUT_BASE, hasError ? INPUT_ERROR : INPUT_NORMAL);
}

interface FieldInputProps {
  fieldDef: BlueprintFieldDefinition;
  value: unknown;
  onChange: (key: string, value: unknown) => void;
  hasError: boolean;
}

function FieldInput({ fieldDef, value, onChange, hasError }: FieldInputProps) {
  const { key, type, placeholder, validation, options } = fieldDef;

  switch (type) {
    case FieldType.TEXT:
      return (
        <input
          type="text"
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          placeholder={placeholder}
          maxLength={validation?.maxLength}
          minLength={validation?.minLength}
          onChange={(e) => onChange(key, e.target.value)}
          className={inputClass(hasError)}
        />
      );

    case FieldType.TEXTAREA:
      return (
        <textarea
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          placeholder={placeholder}
          maxLength={validation?.maxLength}
          onChange={(e) => onChange(key, e.target.value)}
          className={clsx(inputClass(hasError), "resize-y min-h-[80px]")}
          rows={3}
        />
      );

    case FieldType.NUMBER:
      return (
        <input
          type="number"
          id={key}
          value={value !== undefined && value !== null && value !== "" ? Number(value) : ""}
          placeholder={placeholder}
          min={validation?.min}
          max={validation?.max}
          onChange={(e) =>
            onChange(key, e.target.value === "" ? "" : Number(e.target.value))
          }
          className={inputClass(hasError)}
        />
      );

    case FieldType.BOOLEAN: {
      const checked = value === true || value === "true" || value === 1;
      return (
        <label className="inline-flex items-center gap-2 cursor-pointer">
          <input
            type="checkbox"
            id={key}
            checked={checked}
            onChange={(e) => onChange(key, e.target.checked)}
            className="w-4 h-4 rounded border-gray-300 text-brand-600 focus:ring-brand-500"
          />
          <span className="text-sm text-gray-700">
            {checked ? "Yes" : "No"}
          </span>
        </label>
      );
    }

    case FieldType.DATE:
      return (
        <DateInput
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          onChange={(v) => onChange(key, v)}
          className={inputClass(hasError)}
        />
      );

    case FieldType.SELECT:
      return (
        <select
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          onChange={(e) => onChange(key, e.target.value)}
          className={inputClass(hasError)}
        >
          <option value="">{placeholder ?? "Select…"}</option>
          {(options ?? []).map((opt) => (
            <option key={opt.value} value={opt.value}>
              {opt.label}
            </option>
          ))}
        </select>
      );

    case FieldType.MULTI_SELECT: {
      const selectedVals = Array.isArray(value) ? value.map(String) : [];
      return (
        <div className="space-y-1.5">
          {(options ?? []).map((opt) => {
            const checked = selectedVals.includes(opt.value);
            return (
              <label key={opt.value} className="inline-flex items-center gap-2 cursor-pointer mr-4">
                <input
                  type="checkbox"
                  value={opt.value}
                  checked={checked}
                  onChange={(e) => {
                    const next = e.target.checked
                      ? [...selectedVals, opt.value]
                      : selectedVals.filter((v) => v !== opt.value);
                    onChange(key, next);
                  }}
                  className="w-4 h-4 rounded border-gray-300 text-brand-600 focus:ring-brand-500"
                />
                <span className="text-sm text-gray-700">{opt.label}</span>
              </label>
            );
          })}
        </div>
      );
    }

    case FieldType.URL:
      return (
        <input
          type="url"
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          placeholder={placeholder ?? "https://"}
          onChange={(e) => onChange(key, e.target.value)}
          className={inputClass(hasError)}
        />
      );

    case FieldType.EMAIL:
      return (
        <input
          type="email"
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          placeholder={placeholder ?? "email@example.com"}
          onChange={(e) => onChange(key, e.target.value)}
          className={inputClass(hasError)}
        />
      );

    default:
      return (
        <input
          type="text"
          id={key}
          value={typeof value === "string" ? value : (value ?? "") as string}
          placeholder={placeholder}
          onChange={(e) => onChange(key, e.target.value)}
          className={inputClass(hasError)}
        />
      );
  }
}

interface FieldWrapperProps {
  fieldDef: BlueprintFieldDefinition;
  value: unknown;
  onChange: (key: string, value: unknown) => void;
  error?: string;
}

function FieldWrapper({ fieldDef, value, onChange, error }: FieldWrapperProps) {
  const hasError = Boolean(error);
  return (
    <div className="flex flex-col gap-1">
      <label
        htmlFor={fieldDef.key}
        className="text-sm font-medium text-gray-700"
      >
        {fieldDef.label}
        {fieldDef.required && (
          <span className="ml-0.5 text-red-500" aria-hidden="true">
            *
          </span>
        )}
      </label>

      <FieldInput
        fieldDef={fieldDef}
        value={value}
        onChange={onChange}
        hasError={hasError}
      />

      {fieldDef.description && !error && (
        <p className="text-xs text-gray-400">{fieldDef.description}</p>
      )}

      {error && (
        <p className="text-xs text-red-600" role="alert">
          {error}
        </p>
      )}
    </div>
  );
}

function FieldGroup({
  group,
  fields,
  values,
  onChange,
  errors,
}: {
  group: string;
  fields: BlueprintFieldDefinition[];
  values: Record<string, unknown>;
  onChange: (key: string, value: unknown) => void;
  errors?: Record<string, string>;
}) {
  return (
    <fieldset className="space-y-4 border border-gray-200 rounded-lg p-4">
      <legend className="text-sm font-semibold text-gray-700 px-1">{group}</legend>
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
        {fields.map((field) => (
          <FieldWrapper
            key={field.id}
            fieldDef={field}
            value={values[field.key]}
            onChange={onChange}
            {...(errors?.[field.key] !== undefined ? { error: errors[field.key] } : {})}
          />
        ))}
      </div>
    </fieldset>
  );
}

export function DynamicFieldEditor({
  fieldDefs,
  values,
  onChange,
  errors,
  className,
}: Props) {
  const sorted = [...fieldDefs].sort((a, b) => a.order - b.order);

  const ungrouped = sorted.filter((f) => !f.group);
  const groupMap = new Map<string, BlueprintFieldDefinition[]>();

  for (const field of sorted) {
    if (!field.group) continue;
    const existing = groupMap.get(field.group) ?? [];
    existing.push(field);
    groupMap.set(field.group, existing);
  }

  if (sorted.length === 0) {
    return null;
  }

  return (
    <div className={clsx("space-y-6", className)}>
      {ungrouped.length > 0 && (
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          {ungrouped.map((field) => (
            <FieldWrapper
              key={field.id}
              fieldDef={field}
              value={values[field.key]}
              onChange={onChange}
              {...(errors?.[field.key] !== undefined ? { error: errors[field.key] } : {})}
            />
          ))}
        </div>
      )}

      {[...groupMap.entries()].map(([group, fields]) => (
        <FieldGroup
          key={group}
          group={group}
          fields={fields}
          values={values}
          onChange={onChange}
          {...(errors !== undefined ? { errors } : {})}
        />
      ))}
    </div>
  );
}
