import {
  ButtonHTMLAttributes,
  HTMLAttributes,
  KeyboardEvent,
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useRef,
  useState,
} from 'react';
import { cn } from '../lib/cn';

interface TabsCtx {
  value: string;
  setValue: (v: string) => void;
  registerTrigger: (v: string, el: HTMLButtonElement | null) => void;
  focusAdjacent: (current: string, dir: 1 | -1) => void;
}

const Ctx = createContext<TabsCtx | null>(null);

function useTabs() {
  const ctx = useContext(Ctx);
  if (!ctx) throw new Error('Tabs.* must be inside <Tabs>');
  return ctx;
}

export interface TabsProps extends HTMLAttributes<HTMLDivElement> {
  value?: string;
  defaultValue?: string;
  onValueChange?: (v: string) => void;
  children?: ReactNode;
}

export function Tabs({
  value: controlled,
  defaultValue = '',
  onValueChange,
  className,
  children,
  ...rest
}: TabsProps) {
  const [uncontrolled, setUncontrolled] = useState(defaultValue);
  const isControlled = controlled !== undefined;
  const value = isControlled ? (controlled as string) : uncontrolled;

  const triggersRef = useRef<Map<string, HTMLButtonElement>>(new Map());

  const setValue = useCallback(
    (v: string) => {
      if (!isControlled) setUncontrolled(v);
      onValueChange?.(v);
    },
    [isControlled, onValueChange],
  );

  const registerTrigger = useCallback((v: string, el: HTMLButtonElement | null) => {
    if (el) triggersRef.current.set(v, el);
    else triggersRef.current.delete(v);
  }, []);

  const focusAdjacent = useCallback(
    (current: string, dir: 1 | -1) => {
      const keys = Array.from(triggersRef.current.keys());
      const idx = keys.indexOf(current);
      if (idx === -1) return;
      const nextIdx = (idx + dir + keys.length) % keys.length;
      const nextKey = keys[nextIdx];
      const el = triggersRef.current.get(nextKey);
      el?.focus();
      setValue(nextKey);
    },
    [setValue],
  );

  return (
    <Ctx.Provider value={{ value, setValue, registerTrigger, focusAdjacent }}>
      <div className={cn('flex flex-col gap-3', className)} {...rest}>
        {children}
      </div>
    </Ctx.Provider>
  );
}

Tabs.List = function TabsList({ className, children, ...rest }: HTMLAttributes<HTMLDivElement>) {
  return (
    <div
      role="tablist"
      className={cn(
        'inline-flex items-center gap-1 p-1 rounded-lg bg-surface-2 border border-border',
        className,
      )}
      {...rest}
    >
      {children}
    </div>
  );
};

interface TriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  value: string;
  children?: ReactNode;
}

Tabs.Trigger = function TabsTrigger({ value: v, className, children, onClick, ...rest }: TriggerProps) {
  const { value, setValue, registerTrigger, focusAdjacent } = useTabs();
  const selected = value === v;
  const handleKey = (e: KeyboardEvent<HTMLButtonElement>) => {
    if (e.key === 'ArrowRight') {
      e.preventDefault();
      focusAdjacent(v, 1);
    } else if (e.key === 'ArrowLeft') {
      e.preventDefault();
      focusAdjacent(v, -1);
    }
  };
  return (
    <button
      ref={(el) => registerTrigger(v, el)}
      type="button"
      role="tab"
      aria-selected={selected}
      tabIndex={selected ? 0 : -1}
      onClick={(e) => {
        setValue(v);
        onClick?.(e);
      }}
      onKeyDown={handleKey}
      className={cn(
        'px-3 h-8 rounded-md text-sm transition-colors',
        selected
          ? 'bg-surface text-text shadow-sm'
          : 'text-text-2 hover:text-text',
        className,
      )}
      {...rest}
    >
      {children}
    </button>
  );
};

interface ContentProps extends HTMLAttributes<HTMLDivElement> {
  value: string;
  children?: ReactNode;
}

Tabs.Content = function TabsContent({ value: v, className, children, ...rest }: ContentProps) {
  const { value } = useTabs();
  if (value !== v) return null;
  return (
    <div role="tabpanel" className={cn('text-text', className)} {...rest}>
      {children}
    </div>
  );
};
