'use client';

import type { ComponentType, ReactNode } from 'react';

import type { UseChatComposerReturn } from '../hooks/useChatComposer';
import type { ChatAttachment } from '../types';
import type { SlashConfig } from './slash/types';

/** Composer visual size — shared across the `<Composer>` API. */
export type ComposerSize = 'sm' | 'md' | 'lg';

/**
 * Spaciousness of the input surface — orthogonal to `size`:
 * - `compact` — default; embedded composer (FAB panel, dock).
 * - `full`    — full-page chat; roomier surface, bigger textarea + padding.
 */
export type ComposerAppearance = 'compact' | 'full';

/**
 * Geometry of the input surface:
 * - `stacked` — textarea on top, action bar pinned below, one bordered surface.
 * - `inline`  — single horizontal row (compact; `size="sm"` defaults to this).
 */
export type ComposerLayout = 'stacked' | 'inline';

/** Conditional visibility of an action, evaluated against composer state. */
export type ComposerActionVisibility =
  | 'streaming'
  | 'empty'
  | 'hasText'
  | 'disabled';

/**
 * Declarative descriptor for one action-bar button (Tier A). The Composer
 * renders these into consistent `<ComposerButton>`s — size, aria and tooltip
 * are handled centrally so hosts cannot misalign anything.
 */
export interface ComposerAction {
  /** Stable key. */
  id: string;
  /** Lucide icon element. */
  icon: ReactNode;
  /** Used as `aria-label` and tooltip — required for a11y. */
  label: string;
  onClick: () => void;
  disabled?: boolean;
  /** Toggle state → emits `aria-pressed`. */
  pressed?: boolean;
  /** `send` — theme-inverting filled circle (ChatGPT send button). */
  variant?: 'ghost' | 'secondary' | 'primary' | 'send';
  /** Hide the action when the composer is in this state. */
  hideWhen?: ComposerActionVisibility;
  /** Sort weight within its cluster (ascending). Built-ins reserve high values. */
  order?: number;
}

/**
 * Tier A — declarative slot arrays + raw-node escape hatches. Hosts supply
 * action descriptors; the Composer owns the rendering.
 */
export interface ComposerSlots {
  /** Bottom-left cluster. Built-in attach prepends here when enabled. */
  actionsStart?: ComposerAction[];
  /** Bottom-right cluster. Built-in mic/send append here. */
  actionsEnd?: ComposerAction[];
  /** Full-width row above the textarea (reply banner, custom tray). */
  blockStart?: ReactNode;
  /** Raw nodes placed left of the textarea in the `inline` layout. */
  inlineStart?: ReactNode;
  /** Raw nodes placed right of the textarea in the `inline` layout. */
  inlineEnd?: ReactNode;
  /**
   * Slash-command surface. When provided, the composer mounts an
   * internal `<SlashMenu>` as a floating popover anchored above the
   * input surface and routes ↑/↓/Enter/Tab/Esc to the slash hook
   * while the menu is open. See `./slash/README.md`.
   */
  slashCommands?: SlashConfig;
}

// ── Tier B — full slot replacement ─────────────────────────────────────────

export interface SendButtonProps {
  /** True while a reply is streaming — render the stop affordance. */
  streaming: boolean;
  disabled: boolean;
  size: ComposerSize;
  onSend: () => void;
  onCancel?: () => void;
}

export interface AttachButtonProps {
  disabled: boolean;
  size: ComposerSize;
  onClick: () => void;
}

export interface ComposerTextareaProps {
  composer: UseChatComposerReturn;
  placeholder: string;
  disabled: boolean;
  size: ComposerSize;
  className?: string;
}

export interface ActionBarProps {
  /** Already filtered + sorted by `useComposerActions`. */
  actionsStart: ComposerAction[];
  actionsEnd: ComposerAction[];
  size: ComposerSize;
  layout: ComposerLayout;
  inlineStart?: ReactNode;
  inlineEnd?: ReactNode;
}

/** Tier B — swap a primitive entirely. Each is optional. */
export interface ComposerSlotComponents {
  SendButton?: ComponentType<SendButtonProps>;
  AttachButton?: ComponentType<AttachButtonProps>;
  Textarea?: ComponentType<ComposerTextareaProps>;
  /** Replace the whole `blockEnd` action-bar row. */
  ActionBar?: ComponentType<ActionBarProps>;
}

/** Per-slot prop overrides forwarded to the built-in primitives. */
export interface ComposerSlotProps {
  send?: Partial<SendButtonProps>;
  attach?: Partial<AttachButtonProps>;
  textarea?: { className?: string };
  actionBar?: { className?: string };
}

// ── Footer ─────────────────────────────────────────────────────────────────

/** Props for the strip below the input surface. */
export interface ComposerFooterProps {
  start?: ReactNode;
  center?: ReactNode;
  end?: ReactNode;
  /** Auto char counter — reads `composer.value` + `maxLength`. */
  showCounter?: boolean;
  /** Auto keyboard-shortcut hint — reads `submitOn`. @default false */
  showHint?: boolean;
  /** Live value, for the counter. */
  value?: string;
  /** Character cap, for the counter. */
  maxLength?: number;
  /** Submit binding, for the hint. */
  submitOn?: 'enter' | 'cmd+enter';
  size?: ComposerSize;
  className?: string;
}

// ── Attach pipeline ─────────────────────────────────────────────────────────

/** Asset categories the picker accepts. Maps to MIME prefixes / extensions. */
export type ComposerAcceptType = 'image' | 'audio' | 'video' | 'document';

/**
 * Result a host's `uploadFn` resolves to — the remote location the
 * attachment's `url` is rewritten to once the upload completes.
 */
export interface ComposerUploadResult {
  url: string;
  thumbnailUrl?: string;
}

/**
 * Optional upload transport. The single environment-specific seam:
 * - omitted  → attachments stay local (`blob:` object-URL, `status:'ready'`).
 * - web      → multipart POST to a CDN.
 * - Wails    → a Wails-bound upload method.
 *
 * `onProgress` receives 0..1; call it to drive the staging-tray progress.
 */
export type ComposerUploadFn = (
  file: File,
  onProgress: (fraction: number) => void,
) => Promise<ComposerUploadResult>;

/**
 * Declarative config for the built-in attach pipeline. When present (or
 * when `showAttachmentButton` is set), the composer mounts its own file
 * picker — paperclip, `+` menu, drag-drop and paste all funnel through it.
 */
export interface ComposerAttachConfig {
  /** Accepted asset categories. Default: all four. */
  accept?: ComposerAcceptType[];
  /** Max files per message. Falls back to the composer's `maxAttachments`. */
  maxFiles?: number;
  /** Per-file size cap in bytes. Default: unlimited. */
  maxSizeBytes?: number;
  /** Allow selecting more than one file at once. Default `true`. */
  multiple?: boolean;
  /** Upload transport — see {@link ComposerUploadFn}. */
  uploadFn?: ComposerUploadFn;
  /** Enable Ctrl+V / Cmd+V paste-to-attach. Default `true`. */
  pasteEnabled?: boolean;
  /** Called when a file is rejected (size / type / count). */
  onReject?: (file: File, reason: 'size' | 'type' | 'count') => void;
  /**
   * Convert a long plain-text paste (⌘V / Ctrl+V) into a "Pasted text"
   * attachment chip instead of dumping it into the textarea — the
   * ChatGPT/Claude behaviour. Default `true`. Short pastes (below
   * {@link pasteTextThreshold}) always go straight into the textarea.
   */
  pasteTextAsChunk?: boolean;
  /**
   * Character threshold above which a plain-text paste becomes a chunk.
   * Default `2000` (matches Claude / LibreChat). Ignored when
   * `pasteTextAsChunk` is `false`.
   */
  pasteTextThreshold?: number;
}

/** Imperative handle the attach pipeline exposes to the composer + menu. */
export interface ComposerAttachHandle {
  /** Open the native file picker. */
  openPicker: () => void;
  /** Funnel already-resolved files (drop / paste) through validation. */
  attachFiles: (files: File[]) => void;
  /** Remove a staged attachment, revoking its object-URL first. */
  removeAttachment: (id: string) => void;
  /** True when attaching is unavailable (disabled / at file cap). */
  disabled: boolean;
  /** Hidden `<input type=file>` props — the composer renders this node. */
  inputProps: {
    ref: React.RefObject<HTMLInputElement | null>;
    type: 'file';
    accept: string;
    multiple: boolean;
    hidden: true;
    onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  };
}
