import {
  createContext,
  useContext,
  type ComponentType,
  type ReactNode,
} from 'react';
import type { Components, Options } from 'react-markdown';
import type { DaemonStreamingState } from '@qwen-code/webui/daemon-react-sdk';
import type { ACPToolCall } from './adapters/types';
import type { WelcomeHeaderProps } from './components/WelcomeHeader';
import type { WebShellTheme } from './themeContext';

export type MarkdownContentSource = 'assistant' | 'thinking';

export interface MarkdownRenderContext {
  source: MarkdownContentSource;
}

export interface WebShellCodeBlockRenderInfo {
  /**
   * Raw fenced-code language from the markdown class name, restricted to safe
   * fence-language characters.
   */
  language: string;
  /**
   * Canonical Shiki language id after applying built-in aliases, or `text`
   * when the language is unsupported by the fallback highlighter.
   */
  resolvedLanguage: string;
  className?: string;
  code: string;
  /** True while the assistant message is still streaming partial content. */
  isStreaming: boolean;
  source: MarkdownContentSource;
  theme: WebShellTheme;
}

/**
 * Return a React node to replace the default code block rendering. Return
 * `null`, `undefined`, or `false` to decline and fall back to the built-in code
 * block renderer. Expensive renderers should debounce or defer work while
 * `info.isStreaming` is true.
 */
export type CodeBlockRenderer = (
  info: WebShellCodeBlockRenderInfo,
) => ReactNode | null | undefined;

export interface WebShellMarkdownCustomization {
  transformMarkdown?: (
    markdown: string,
    context: MarkdownRenderContext,
  ) => string;
  renderCodeBlock?: CodeBlockRenderer;
  /**
   * Custom markdown components override Web Shell's built-ins. In particular,
   * `components.code` replaces the default code renderer, so `renderCodeBlock`
   * will not be called for that source.
   */
  components?: Components;
  remarkPlugins?: Options['remarkPlugins'];
  rehypePlugins?: Options['rehypePlugins'];
}

export type MarkdownTableMode = 'basic' | 'advanced';

export type ToolHeaderKind =
  | 'agent'
  | 'ask'
  | 'edit'
  | 'fetch'
  | 'read'
  | 'shell'
  | 'todo'
  | 'write'
  | 'other';

export interface ToolHeaderExtraRenderInfo {
  kind: ToolHeaderKind;
  tool: ACPToolCall;
  displayName: string;
  description: string;
  elapsed: string;
  workspaceCwd?: string;
}

export type ToolHeaderExtraRenderer = (
  info: ToolHeaderExtraRenderInfo,
) => ReactNode;

export type WelcomeHeaderRenderer = (props: WelcomeHeaderProps) => ReactNode;
export type WelcomeFooterRenderer = (props: WelcomeHeaderProps) => ReactNode;

export interface UserMessageContentRenderInfo {
  content: string;
  images?: readonly { data: string; mimeType: string }[];
}

export type UserMessageContentRenderer = (
  info: UserMessageContentRenderInfo,
) => ReactNode;

export interface WebShellBottomStatusItem {
  id: string;
  label: ReactNode;
  title?: string;
  ariaLabel?: string;
  onClick?: () => void;
}

export type WebShellBuiltinComposerTagKind =
  | 'extension'
  | 'mcp'
  | 'file'
  | 'skill';

export type WebShellComposerTagKind =
  | WebShellBuiltinComposerTagKind
  | (string & {});

export type WebShellComposerTagIconMap = Readonly<Record<string, string>>;

export interface WebShellComposerTag {
  id: string;
  label?: string;
  value?: string;
  removable?: boolean;
  kind?: WebShellComposerTagKind;
  serialized?: string;
}

export type WebShellComposerTagPlacement = 'top' | 'inline';

export interface WebShellComposerTagOptions {
  placement?: WebShellComposerTagPlacement;
}

export interface WebShellComposerTextOptions {
  mode?: 'append' | 'replace';
}

export interface WebShellComposerInput {
  text?: string;
  tags?: readonly WebShellComposerTag[];
  tagPlacement?: WebShellComposerTagPlacement;
  submit?: boolean;
}

export interface WebShellAtItem {
  id: string;
  label: string;
  description?: string;
  detail?: string;
  insertText?: string;
  composerTag?: WebShellComposerTag;
}

export interface WebShellAtProvider {
  id: string;
  label: string;
  description?: string;
  order?: number;
  search(params: {
    query: string;
    signal: AbortSignal;
  }): Promise<readonly WebShellAtItem[]>;
}

export interface WebShellComposerApi {
  insertText(text: string, options?: WebShellComposerTextOptions): void;
  setText(text: string): void;
  addTags(
    tags: readonly WebShellComposerTag[],
    options?: WebShellComposerTagOptions,
  ): void;
  removeTag(id: string): void;
  /** Clears text and/or top tags. Inline tags are part of the editor text. */
  clear(options?: { text?: boolean; tags?: boolean }): void;
  submit(input?: WebShellComposerInput): void;
}

export interface WebShellComposerToolbarRenderInfo {
  disabled: boolean;
  isRunning: boolean;
  currentMode: string;
  currentModel: string;
  sessionName?: string;
}

export type WebShellComposerToolbarStartRenderInfo =
  WebShellComposerToolbarRenderInfo;

export type WebShellComposerToolbarRightRenderInfo =
  WebShellComposerToolbarRenderInfo;

export type ComposerToolbarStartRenderer =
  ComponentType<WebShellComposerToolbarStartRenderInfo>;

export type ComposerToolbarEndRenderer =
  ComponentType<WebShellComposerToolbarRenderInfo>;

export type ComposerToolbarRightRenderer =
  ComponentType<WebShellComposerToolbarRightRenderInfo>;

// ---- Background task info (public type for footer renderer) ----

interface WebShellTaskBase {
  id: string;
  label: string;
  description: string;
  runtimeMs: number;
  startTime: number;
  endTime?: number;
  error?: string;
}

export interface WebShellAgentTask extends WebShellTaskBase {
  kind: 'agent';
  status: 'running' | 'paused' | 'completed' | 'failed' | 'cancelled';
  subagentType?: string;
  isBackgrounded: boolean;
  prompt?: string;
}

export interface WebShellShellTask extends WebShellTaskBase {
  kind: 'shell';
  status: 'running' | 'completed' | 'failed' | 'cancelled';
  command: string;
  cwd: string;
  pid?: number;
  exitCode?: number;
}

export interface WebShellMonitorTask extends WebShellTaskBase {
  kind: 'monitor';
  status: 'running' | 'completed' | 'failed' | 'cancelled';
  command: string;
  pid?: number;
  exitCode?: number;
}

export type WebShellTaskInfo =
  | WebShellAgentTask
  | WebShellShellTask
  | WebShellMonitorTask;

// ---- Model info (public type for footer renderer) ----

export interface WebShellModelInfo {
  id: string;
  label?: string;
  contextWindow?: number;
}

// ---- Skill info (public type for footer renderer) ----

export interface WebShellSkillInfo {
  name: string;
  description: string;
}

// ---- Footer renderer ----

export interface WebShellFooterRenderInfo {
  connected: boolean;
  mode: string;
  model: string;
  streamingState: DaemonStreamingState;
  contextUsageRatio: number;
  activeGoal: { condition: string; setAt: number } | null;
  tasks: readonly WebShellTaskInfo[];
  availableModes: readonly string[];
  availableModels: readonly WebShellModelInfo[];
  skills: readonly WebShellSkillInfo[];

  onSelectMode: (mode: string) => void;
  onSelectModel: (model: string) => void;
}

export type FooterRenderer = ComponentType<WebShellFooterRenderInfo>;

// ---- Loading phrases ----

/**
 * Resolves the witty phrases cycled while a prompt is streaming. Receives the
 * resolved UI language. Return phrases to override the built-in defaults, an
 * empty array to hide the phrase entirely, or `undefined`/`null` to fall back
 * to the built-in defaults for that language.
 */
export type LoadingPhrasesResolver = (
  language: string,
) => readonly string[] | undefined | null;

export interface WebShellCustomization {
  renderToolHeaderExtra?: ToolHeaderExtraRenderer;
  renderWelcomeHeader?: WelcomeHeaderRenderer;
  renderWelcomeFooter?: WelcomeFooterRenderer;
  renderUserMessageContent?: UserMessageContentRenderer;
  renderComposerToolbarStart?: ComposerToolbarStartRenderer;
  renderComposerToolbarEnd?: ComposerToolbarEndRenderer;
  renderComposerToolbarRight?: ComposerToolbarRightRenderer;
  renderFooter?: FooterRenderer;
  compactThinking?: boolean;
  /**
   * Auto-collapse each completed turn's intermediate steps (thinking, tool
   * calls, mid-turn assistant text) behind a toggle on the prompt row, leaving
   * just the prompt and the final answer. The active turn always stays
   * expanded. Defaults to enabled when unset.
   */
  collapseCompletedTurns?: boolean;
  markdownTableMode?: MarkdownTableMode;
  markdown?: WebShellMarkdownCustomization;
  loadingPhrases?: LoadingPhrasesResolver;
}

const WebShellCustomizationContext = createContext<WebShellCustomization>({});

export const WebShellCustomizationProvider =
  WebShellCustomizationContext.Provider;

export function useWebShellCustomization(): WebShellCustomization {
  return useContext(WebShellCustomizationContext);
}
