"use client";

import { Minus, Plus } from "lucide-react";

import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

export type AdminNumericStepperProps = {
  id?: string;
  value: string;
  onValueChange: (value: string) => void;
  min?: number;
  max?: number;
  step?: number;
  /** 整数模式（授信额度等）；默认 false 支持小数（比例 %） */
  integer?: boolean;
  disabled?: boolean;
  /** 失焦时超出 max 是否保留原输入（供保存前校验报错），默认 clamp */
  preserveOverMaxOnBlur?: boolean;
  className?: string;
  suffix?: string;
};

function parseNumeric(value: string, integer: boolean): number {
  const parsed = integer ? Number.parseInt(value, 10) : Number.parseFloat(value);
  return Number.isFinite(parsed) ? parsed : 0;
}

function formatNumeric(value: number, integer: boolean, step: number): string {
  if (integer) {
    return String(Math.trunc(value));
  }

  const decimals = step < 1 ? 2 : step < 0.1 ? 3 : 1;
  const fixed = value.toFixed(decimals);
  return fixed.replace(/\.?0+$/, "") || "0";
}

function clamp(value: number, min?: number, max?: number): number {
  let next = value;
  if (min !== undefined) {
    next = Math.max(min, next);
  }
  if (max !== undefined) {
    next = Math.min(max, next);
  }
  return next;
}

function isPartialInput(text: string, integer: boolean): boolean {
  const trimmed = text.trim();
  if (trimmed === "" || trimmed === "-" || trimmed === ".") {
    return true;
  }
  if (!integer && trimmed.endsWith(".")) {
    return true;
  }
  return false;
}

export function AdminNumericStepper({
  id,
  value,
  onValueChange,
  min = 0,
  max,
  step = 1,
  integer = false,
  disabled = false,
  preserveOverMaxOnBlur = false,
  className,
  suffix,
}: AdminNumericStepperProps): React.ReactElement {
  const current = parseNumeric(value, integer);
  const atMin = min !== undefined && current <= min;
  const atMax = max !== undefined && current >= max;
  const outOfRange =
    !disabled &&
    Number.isFinite(current) &&
    ((min !== undefined && current < min) || (max !== undefined && current > max));

  const applyDelta = (delta: number) => {
    const next = clamp(current + delta, min, max);
    onValueChange(formatNumeric(next, integer, step));
  };

  const commitInput = (raw: string) => {
    if (isPartialInput(raw, integer)) {
      onValueChange(formatNumeric(min ?? 0, integer, step));
      return;
    }

    const parsed = parseNumeric(raw, integer);
    if (!Number.isFinite(parsed)) {
      onValueChange(formatNumeric(min ?? 0, integer, step));
      return;
    }

    if (
      preserveOverMaxOnBlur &&
      max !== undefined &&
      parsed > max
    ) {
      return;
    }

    onValueChange(formatNumeric(clamp(parsed, min, max), integer, step));
  };

  return (
    <div
      className={cn(
        "flex h-10 items-stretch overflow-hidden rounded-md border bg-background/50 shadow-xs",
        outOfRange ? "border-destructive/80 ring-1 ring-destructive/25" : "border-input",
        disabled && "pointer-events-none opacity-50",
        className,
      )}
    >
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className="h-auto shrink-0 rounded-none border-r border-input/80 px-3 hover:bg-muted/60"
        disabled={disabled || atMin}
        aria-label="减少"
        onClick={() => applyDelta(-step)}
      >
        <Minus className="size-4" aria-hidden />
      </Button>
      <div className="flex min-w-0 flex-1 items-center justify-center gap-1 px-2">
        <input
          id={id}
          type="text"
          inputMode={integer ? "numeric" : "decimal"}
          value={value}
          disabled={disabled}
          aria-valuemin={min}
          aria-valuemax={max}
          aria-invalid={outOfRange || undefined}
          className={cn(
            "h-full w-full min-w-0 border-0 bg-transparent text-center text-sm font-medium tabular-nums outline-none",
            "focus:ring-0",
          )}
          onBlur={(e) => {
            commitInput(e.target.value);
          }}
          onChange={(e) => {
            const next = e.target.value;
            if (integer && next !== "" && !/^-?\d*$/.test(next)) {
              return;
            }
            if (!integer && next !== "" && !/^-?\d*\.?\d*$/.test(next)) {
              return;
            }
            onValueChange(next);
          }}
          onKeyDown={(e) => {
            if (e.key === "Enter") {
              e.currentTarget.blur();
            }
          }}
        />
        {suffix ? (
          <span className="shrink-0 text-sm text-muted-foreground">{suffix}</span>
        ) : null}
      </div>
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className="h-auto shrink-0 rounded-none border-l border-input/80 px-3 hover:bg-muted/60"
        disabled={disabled || atMax}
        aria-label="增加"
        onClick={() => applyDelta(step)}
      >
        <Plus className="size-4" aria-hidden />
      </Button>
    </div>
  );
}
