'use client';

import { useEffect, useRef, useState } from 'react';
import { ChevronDown, Loader2, Pencil } from 'lucide-react';

import { toastError } from '@/lib/api/toast-error';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';

interface SelectOption {
  value: string;
  label: string;
}

interface BaseProps {
  value: string | null | undefined;
  /**
   * Optional formatted version shown in display mode only. The edit
   * input still works against the raw `value` (so the input shows the
   * editable raw number, not the formatted string). Useful for
   * currency, percentages, etc.
   */
  displayValue?: string | null;
  onSave: (next: string | null) => Promise<void>;
  placeholder?: string;
  emptyText?: string;
  className?: string;
  disabled?: boolean;
}

interface TextProps extends BaseProps {
  variant?: 'text';
}

interface SelectFieldProps extends BaseProps {
  variant: 'select';
  options: SelectOption[];
}

interface TextareaProps extends BaseProps {
  variant: 'textarea';
  rows?: number;
}

interface DateProps extends BaseProps {
  variant: 'date';
  /** Optional min/max bounds in YYYY-MM-DD form (e.g. for incorporation dates that
   *  can't be in the future). */
  min?: string;
  max?: string;
}

export type InlineEditableFieldProps = TextProps | SelectFieldProps | TextareaProps | DateProps;

/**
 * Click-to-edit field used in detail panels. Shows the value as plain text
 * with a pencil affordance on hover; clicking swaps to an input that saves on
 * Enter/blur and cancels on Escape.
 */
export function InlineEditableField(props: InlineEditableFieldProps) {
  // Key-based remount: keying the inner body on `value` re-runs the
  // `useState(value)` initializer whenever the prop changes externally
  // (optimistic-update settle, parent refetch). Replaces the prior
  // useEffect(setDraft, [value]) Compiler-flagged set-state-in-effect.
  return <InlineEditableFieldBody key={String(props.value ?? '')} {...props} />;
}

function InlineEditableFieldBody(props: InlineEditableFieldProps) {
  const { value, displayValue, onSave, placeholder, emptyText = '-', className, disabled } = props;
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(value ?? '');
  const [saving, setSaving] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  useEffect(() => {
    if (editing) {
      if (inputRef.current) {
        inputRef.current.focus();
        inputRef.current.select();
      } else if (textareaRef.current) {
        textareaRef.current.focus();
        textareaRef.current.select();
      }
    }
  }, [editing]);

  async function commit(nextRaw: string) {
    const trimmed = nextRaw.trim();
    if (trimmed === (value ?? '')) {
      setEditing(false);
      return;
    }
    setSaving(true);
    try {
      await onSave(trimmed === '' ? null : trimmed);
      setEditing(false);
    } catch (err) {
      toastError(err);
      setDraft(value ?? '');
    } finally {
      setSaving(false);
    }
  }

  function cancel() {
    setDraft(value ?? '');
    setEditing(false);
  }

  if (props.variant === 'select') {
    // Picker fields don't need a read/edit mode toggle - a Select is
    // already a click-to-open control. Rendering one consistent
    // SelectTrigger eliminates the width jump that happened when we
    // swapped from a content-sized ReadButton to a w-full SelectTrigger
    // on click (and back again on selection). The trigger now stays at
    // a single width whether the popover is open or closed, so the
    // dropdown menu visually aligns to the same control across states.
    const labelFor = (v: string | null | undefined) =>
      v ? (props.options.find((o) => o.value === v)?.label ?? v) : null;

    return (
      <div className={cn('flex items-center gap-1', className)}>
        <Select
          value={value ?? ''}
          onValueChange={(v) => void commit(v)}
          disabled={disabled || saving}
        >
          <SelectTrigger className="h-8 text-sm w-full">
            <SelectValue placeholder={emptyText}>{labelFor(value) ?? emptyText}</SelectValue>
          </SelectTrigger>
          <SelectContent>
            {props.options.map((o) => (
              <SelectItem key={o.value} value={o.value}>
                {o.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
        {saving && <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" aria-hidden />}
      </div>
    );
  }

  if (props.variant === 'date') {
    // Native date input: the browser provides the calendar UI, ISO-formatted
    // value (YYYY-MM-DD) keeps the backend payload uniform. Saves on change
    // (no extra blur tap on mobile) and on Enter; Escape reverts.
    return (
      <div className={cn('flex items-center gap-1', className)}>
        <Input
          type="date"
          value={draft}
          min={props.min}
          max={props.max}
          onChange={(e) => {
            const next = e.target.value;
            setDraft(next);
            void commit(next);
          }}
          onKeyDown={(e) => {
            if (e.key === 'Escape') {
              e.preventDefault();
              cancel();
            }
          }}
          disabled={saving || disabled}
          className="h-8 text-sm w-auto"
        />
        {saving && <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" aria-hidden />}
      </div>
    );
  }

  if (props.variant === 'textarea') {
    if (!editing) {
      return (
        <ReadButton
          value={value || null}
          displayValue={displayValue}
          emptyText={emptyText}
          disabled={disabled}
          onClick={() => setEditing(true)}
          multiline
          className={className}
        />
      );
    }

    return (
      <div className={cn('flex flex-col gap-1', className)}>
        <Textarea
          ref={textareaRef}
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'Escape') {
              e.preventDefault();
              cancel();
            }
            if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
              e.preventDefault();
              void commit(draft);
            }
          }}
          onBlur={() => {
            if (!saving) void commit(draft);
          }}
          placeholder={placeholder}
          disabled={saving}
          rows={props.rows ?? 4}
          className="text-sm"
        />
        {saving && <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" aria-hidden />}
      </div>
    );
  }

  if (!editing) {
    return (
      <ReadButton
        value={value || null}
        displayValue={displayValue}
        emptyText={emptyText}
        disabled={disabled}
        onClick={() => setEditing(true)}
        className={className}
      />
    );
  }

  return (
    <div className={cn('flex items-center gap-1', className)}>
      <Input
        ref={inputRef}
        value={draft}
        onChange={(e) => setDraft(e.target.value)}
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            e.preventDefault();
            void commit(draft);
          }
          if (e.key === 'Escape') {
            e.preventDefault();
            cancel();
          }
        }}
        onBlur={() => {
          if (!saving) void commit(draft);
        }}
        placeholder={placeholder}
        disabled={saving}
        className="h-7 text-sm"
      />
      {saving && <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" aria-hidden />}
    </div>
  );
}

function ReadButton({
  value,
  displayValue,
  emptyText,
  disabled,
  onClick,
  multiline,
  kind = 'text',
  className,
}: {
  value: string | null;
  /** Optional formatted version for display only (currency, percent, etc.) */
  displayValue?: string | null;
  emptyText: string;
  disabled?: boolean;
  onClick: () => void;
  multiline?: boolean;
  /** Icon affordance: 'text' shows a pencil (free-text edit), 'select'
   *  shows a chevron-down (fixed-list picker). The chevron clarifies
   *  that clicking opens a dropdown, not a text input. */
  kind?: 'text' | 'select';
  className?: string;
}) {
  const Icon = kind === 'select' ? ChevronDown : Pencil;
  return (
    <button
      type="button"
      disabled={disabled}
      onClick={onClick}
      className={cn(
        'group rounded px-1 -mx-1 py-0.5 text-left text-sm',
        multiline ? 'flex w-full items-start gap-1.5' : 'inline-flex items-center gap-1.5',
        'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring',
        // Select-kind buttons get a faint border so they read as a
        // distinct interactive element rather than text-with-an-icon.
        kind === 'select' && 'border border-border bg-background hover:bg-accent',
        disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
        className,
      )}
    >
      <span
        className={cn(
          'flex-1',
          multiline && 'whitespace-pre-wrap',
          !value && 'text-muted-foreground',
        )}
      >
        {value ? (displayValue ?? value) : emptyText}
      </span>
      {!disabled && (
        <Icon
          className={cn(
            // Pencil sits faintly so users discover free-text editability
            // on hover; chevron is more present so the dropdown affordance
            // is obvious before any interaction.
            kind === 'select'
              ? 'h-3.5 w-3.5 opacity-60 transition-opacity group-hover:opacity-100'
              : 'h-3 w-3 opacity-20 transition-opacity group-hover:opacity-60',
            multiline && 'mt-1 shrink-0',
          )}
        />
      )}
    </button>
  );
}
