import type { ModelProviderAuthMode } from "../utils/modelProviders";
import type { ProviderProtocol } from "../utils/providerProtocol";
import type {
  AdvancedModelParams,
  ActiveNoteContext,
  ChatAttachment,
  CollectionContextRef,
  PaperContentSourceMode,
  PaperContextRef,
  SelectedTextSource,
  TagContextRef,
} from "../shared/types";
import type {
  ChatMessage,
  ReasoningConfig as LLMReasoningConfig,
  UsageStats,
} from "../shared/llm";
import type { ContextCachePlan } from "../contextCache/manager";

export type AgentRequest = {
  conversationKey: number;
  mode: "agent";
  userText: string;
  conversationKind?: "global" | "paper";
  activeItemId?: number;
  selectedTexts?: string[];
  selectedTextSources?: SelectedTextSource[];
  selectedTextPaperContexts?: (PaperContextRef | undefined)[];
  selectedPaperContexts?: PaperContextRef[];
  fullTextPaperContexts?: PaperContextRef[];
  pinnedPaperContexts?: PaperContextRef[];
  selectedCollectionContexts?: CollectionContextRef[];
  selectedTagContexts?: TagContextRef[];
  availableAttachmentResources?: AgentAttachmentResource[];
  attachmentResourceSummaries?: AgentAttachmentResourceSummary[];
  attachments?: ChatAttachment[];
  screenshots?: string[];
  /** Skill IDs to force-activate regardless of regex matching (from slash menu selection). */
  forcedSkillIds?: string[];
  model?: string;
  apiBase?: string;
  apiKey?: string;
  providerProtocol?: ProviderProtocol;
  reasoning?: LLMReasoningConfig;
  advanced?: AdvancedModelParams;
};

export type AgentPendingActionButton = {
  id: string;
  label: string;
  style?: "primary" | "secondary" | "danger";
  approved?: boolean;
  executionMode?: "immediate" | "edit";
  submitLabel?: string;
  backLabel?: string;
};

type AgentPendingFieldBase = {
  id: string;
  visibleForActionIds?: string[];
  requiredForActionIds?: string[];
};

export type AgentPendingField =
  | (AgentPendingFieldBase & {
      type: "textarea";
      label: string;
      value?: string;
      placeholder?: string;
      editorMode?: "plain" | "json";
      spellcheck?: boolean;
    })
  | (AgentPendingFieldBase & {
      type: "text";
      label: string;
      value?: string;
      placeholder?: string;
    })
  | (AgentPendingFieldBase & {
      type: "code_preview";
      label: string;
      value: string;
      language?: string;
    })
  | (AgentPendingFieldBase & {
      type: "select";
      label: string;
      value?: string;
      options: Array<{
        id: string;
        label: string;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "review_table";
      label?: string;
      rows: Array<{
        key: string;
        label: string;
        before?: string;
        after: string;
        multiline?: boolean;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "diff_preview";
      label?: string;
      before?: string;
      after?: string;
      sourceFieldId?: string;
      contextLines?: number;
      emptyMessage?: string;
    })
  | (AgentPendingFieldBase & {
      type: "image_gallery";
      label?: string;
      items: Array<{
        label: string;
        storedPath: string;
        mimeType?: string;
        title?: string;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "checklist";
      label: string;
      items: Array<{
        id: string;
        label: string;
        description?: string;
        checked?: boolean;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "assignment_table";
      label: string;
      options: Array<{
        id: string;
        label: string;
      }>;
      rows: Array<{
        id: string;
        label: string;
        description?: string;
        value?: string;
        checked?: boolean;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "tag_assignment_table";
      label: string;
      rows: Array<{
        id: string;
        label: string;
        description?: string;
        value?: string | string[];
        placeholder?: string;
      }>;
    })
  | (AgentPendingFieldBase & {
      type: "paper_result_list";
      label: string;
      rows: Array<{
        id: string;
        title: string;
        subtitle?: string;
        body?: string;
        badges?: string[];
        href?: string;
        importIdentifier?: string;
        checked?: boolean;
        year?: number;
        citationCount?: number;
      }>;
      /**
       * Optional multi-mode view. When present, the renderer shows a toggle
       * group above the list (e.g. Recommendations / References / Citations)
       * and swaps the visible rows per selected mode. Selections persist
       * across mode switches — the submitted value is the union of checked
       * row IDs across all modes.
       *
       * When omitted, the card renders the flat `rows` list (legacy).
       */
      modes?: Array<{
        id: string;
        label: string;
        rows: Array<{
          id: string;
          title: string;
          subtitle?: string;
          body?: string;
          badges?: string[];
          href?: string;
          importIdentifier?: string;
          checked?: boolean;
          year?: number;
          citationCount?: number;
        }>;
        emptyMessage?: string;
      }>;
      defaultModeId?: string;
      /**
       * When set, the renderer shows a "Load more" button at the bottom of
       * the list. Clicking it resolves the confirmation with this actionId
       * plus the current selection, letting the action fetch an expanded
       * result set and re-invoke requestConfirmation with the larger list.
       * The action is responsible for the re-fetch loop.
       */
      loadMoreActionId?: string;
      loadMoreLabel?: string;
      minSelectedByAction?: Array<{
        actionId: string;
        min: number;
      }>;
    });

export type AgentPendingAction = {
  toolName: string;
  title: string;
  mode?: "approval" | "review";
  confirmLabel: string;
  cancelLabel: string;
  description?: string;
  fields: AgentPendingField[];
  actions?: AgentPendingActionButton[];
  defaultActionId?: string;
  cancelActionId?: string;
};

export type AgentConfirmationResolution = {
  approved: boolean;
  actionId?: string;
  data?: unknown;
};

export type AgentInheritedApproval = {
  sourceToolName: string;
  sourceActionId: string;
  sourceMode?: "approval" | "review";
};

export type ToolSpec = {
  name: string;
  description: string;
  inputSchema: object;
  mutability: "read" | "write";
  requiresConfirmation: boolean;
  /**
   * Model-visible tools are advertised to agent/model runtimes and MCP
   * clients. Internal tools stay registered so plugin-owned migration
   * delegates and review-card workflows can still invoke them.
   */
  exposure?: "model" | "internal";
  /**
   * Advanced tools remain model-visible when exposure is "model", but get
   * stricter policy/trace treatment because they can touch local files,
   * shell commands, or direct Zotero scripts.
   */
  tier?: "normal" | "advanced";
};

export type AgentEvent =
  | {
      type: "provider_event";
      providerType?: string;
      sessionId?: string;
      payload?: Record<string, unknown>;
      ts?: number;
    }
  | { type: "status"; text: string }
  | {
      type: "reasoning";
      round: number;
      stepId?: string;
      stepLabel?: string;
      summary?: string;
      details?: string;
    }
  | ({ type: "usage"; round: number } & UsageStats)
  | { type: "tool_call"; callId: string; name: string; args: unknown }
  | {
      type: "tool_result";
      callId: string;
      name: string;
      ok: boolean;
      content: unknown;
      artifacts?: AgentToolArtifact[];
    }
  | {
      type: "tool_error";
      callId: string;
      name: string;
      error: string;
      round: number;
    }
  | {
      type: "confirmation_required";
      requestId: string;
      action: AgentPendingAction;
    }
  | {
      type: "confirmation_resolved";
      requestId: string;
      approved: boolean;
      actionId?: string;
      data?: unknown;
    }
  | { type: "message_delta"; text: string }
  | { type: "message_rollback"; length: number; text: string }
  | {
      type: "codex_progress";
      itemId: string;
      text: string;
      status?: "running" | "completed";
    }
  | {
      type: "codex_tool_activity";
      itemId: string;
      phase: "started" | "completed";
      toolName?: string;
      toolLabel?: string;
      serverName?: string;
      args?: unknown;
      ok?: boolean;
      text?: string;
      codeBlock?: string;
    }
  | {
      type: "usage";
      inputTokens: number;
      outputTokens: number;
      cacheCreationInputTokens?: number;
      cacheReadInputTokens?: number;
      contextTokens: number;
      contextWindow?: number;
      contextWindowIsAuthoritative?: boolean;
      percentage?: number;
      sessionId?: string;
      model?: string;
    }
  | { type: "context_compacted"; automatic?: boolean }
  | { type: "fallback"; reason: string }
  | { type: "final"; text: string };

export type AgentRunStatus = "running" | "completed" | "failed" | "cancelled";

export type AgentRunRecord = {
  runId: string;
  conversationKey: number;
  mode: "agent";
  model?: string;
  status: AgentRunStatus;
  createdAt: number;
  completedAt?: number;
  finalText?: string;
};

export type AgentRunEventRecord = {
  runId: string;
  seq: number;
  eventType: AgentEvent["type"];
  payload: AgentEvent;
  createdAt: number;
};

export type AgentToolCall = {
  id: string;
  name: string;
  arguments: unknown;
};

export type AgentTraceDetailKind = "text" | "code" | "json" | "url";

export type AgentTraceDetail = {
  label: string;
  value: string;
  kind?: AgentTraceDetailKind;
};

export type AgentTraceChip = {
  icon?: string;
  iconName?: string;
  label: string;
  title?: string;
  detail?: AgentTraceDetail;
  details?: AgentTraceDetail[];
};

export type AgentTraceRequestSummary = {
  selectedTexts: string[];
  paperTitles: string[];
  fileNames: string[];
  screenshotCount: number;
};

export type AgentModelCapabilities = {
  streaming: boolean;
  toolCalls: boolean;
  contentInputs?: AgentContentInputCapabilities;
  /** Compatibility alias: true when any non-text content input is available. */
  multimodal: boolean;
  /** Native upload/file-reference support, not inline document-block support. */
  fileInputs: boolean;
  reasoning: boolean;
};

export type AgentContentInputCapabilities = {
  images: boolean;
  pdfDocuments: boolean;
  nativeFiles: boolean;
};

export type AgentModelContentPart =
  | { type: "text"; text: string }
  | {
      type: "image_url";
      image_url: { url: string; detail?: "low" | "high" | "auto" };
    }
  | {
      type: "file_ref";
      file_ref: {
        name: string;
        mimeType: string;
        storedPath: string;
        contentHash?: string;
      };
    };

export type AgentSystemMessage = {
  role: "system";
  content: string | AgentModelContentPart[];
  cachePolicy?: "stable-prefix";
};

export type AgentUserMessage = {
  role: "user";
  content: string | AgentModelContentPart[];
};

export type AgentAssistantMessage = {
  role: "assistant";
  content: string | AgentModelContentPart[];
  reasoning_content?: string;
  tool_calls?: AgentToolCall[];
};

export type AgentToolMessage = {
  role: "tool";
  content: string;
  tool_call_id: string;
  name: string;
};

export type AgentModelMessage =
  | AgentSystemMessage
  | AgentUserMessage
  | AgentAssistantMessage
  | AgentToolMessage;

export type AgentModelStep =
  | {
      kind: "final";
      text: string;
      assistantMessage?: AgentAssistantMessage;
    }
  | {
      kind: "tool_calls";
      calls: AgentToolCall[];
      assistantMessage: AgentAssistantMessage;
    };

export type AgentRuntimeRequest = AgentRequest & {
  item?: Zotero.Item | null;
  history?: ChatMessage[];
  authMode?: ModelProviderAuthMode;
  claudeEffortLevel?: "low" | "medium" | "high" | "xhigh" | "max";
  systemPrompt?: string;
  /** Optional user-defined instructions injected between persona and tool guidance */
  customInstructions?: string;
  modelProviderLabel?: string;
  libraryID?: number;
  activeNoteContext?: ActiveNoteContext;
  metadata?: Record<string, unknown>;
  contextCache?: ContextCachePlan;
};

export type AgentAttachmentReadableVia =
  | "read_attachment"
  | "paper_read"
  | "unsupported";

export type AgentAttachmentType =
  | "pdf"
  | "markdown"
  | "html"
  | "txt"
  | "docx"
  | "unsupported";

export type AgentAttachmentResource = {
  lifecycleState: "available";
  parentItemId: number;
  parentTitle: string;
  contextItemId: number;
  title: string;
  contentType: string;
  attachmentType: AgentAttachmentType;
  readableVia: AgentAttachmentReadableVia;
  contentSourceMode?: PaperContentSourceMode;
  isPrimary?: boolean;
};

export type AgentAttachmentResourceSummary = {
  scope: "selected-collection";
  collectionId: number;
  libraryID: number;
  collectionName: string;
  parentItemCount: number;
  attachmentCounts: Partial<Record<AgentAttachmentType, number>>;
};

export type AgentRuntimeOutcome =
  | {
      kind: "completed";
      runId: string;
      text: string;
      usedFallback: false;
    }
  | {
      kind: "fallback";
      runId: string;
      reason: string;
      usedFallback: true;
    };

export type AgentToolArtifact =
  | {
      kind: "image";
      mimeType: string;
      storedPath: string;
      contentHash?: string;
      title?: string;
      pageIndex?: number;
      pageLabel?: string;
      paperContext?: PaperContextRef;
    }
  | {
      kind: "file_ref";
      mimeType: string;
      storedPath: string;
      name: string;
      contentHash?: string;
      title?: string;
      paperContext?: PaperContextRef;
    };

export type AgentToolResult = {
  callId: string;
  name: string;
  ok: boolean;
  content: unknown;
  artifacts?: AgentToolArtifact[];
};

export type AgentToolReviewResolution =
  | {
      kind: "deliver";
      toolMessageContent?: unknown;
      followupMessages?: AgentModelMessage[];
    }
  | {
      kind: "stop";
      finalText: string;
    }
  | {
      kind: "invoke_tool";
      call: {
        name: string;
        arguments: unknown;
        inheritedApproval?: AgentInheritedApproval;
      };
      terminalText?:
        | {
            onSuccess: string;
            onDenied: string;
            onError: string;
          }
        | undefined;
    };

export type AgentToolExecutionOutput<TResult = unknown> =
  | TResult
  | {
      content: TResult;
      artifacts?: AgentToolArtifact[];
    };

export type AgentToolContext = {
  request: AgentRuntimeRequest;
  item: Zotero.Item | null;
  currentAnswerText: string;
  modelName: string;
  modelProviderLabel?: string;
  resourceSignature?: string;
};

export type AgentToolInputValidation<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

export type AgentToolGuidance = {
  matches: (request: AgentRuntimeRequest) => boolean;
  instruction: string;
};

export type AgentToolPresentationSummaryInput = {
  label: string;
  args?: unknown;
  content?: unknown;
  request?: AgentTraceRequestSummary;
};

export type AgentToolPresentationSummary =
  | string
  | ((input: AgentToolPresentationSummaryInput) => string | null);

/**
 * A single result card rendered below a tool's success row in the agent trace.
 * This path is display-only. Interactive review/approval flows should use
 * `createPendingAction` or `createResultReviewAction` instead.
 */
export type AgentToolResultCard = {
  title: string;
  subtitle?: string;
  body?: string;
  badges?: string[];
  href?: string;
  /**
   * Optional identifier shown for context. Result-card rendering is read-only;
   * use review cards for any import workflow.
   */
  importIdentifier?: string;
};

export type AgentToolPresentation = {
  label?: string;
  summaries?: {
    onCall?: AgentToolPresentationSummary;
    onPending?: AgentToolPresentationSummary;
    onApproved?: AgentToolPresentationSummary;
    onDenied?: AgentToolPresentationSummary;
    onSuccess?: AgentToolPresentationSummary;
    onEmpty?: AgentToolPresentationSummary;
    onError?: AgentToolPresentationSummary;
  };
  buildChips?: (params: {
    args: unknown;
    request?: AgentTraceRequestSummary;
  }) => AgentTraceChip[];
  /**
   * When provided, the agent trace renders a read-only card list below the
   * tool's success row. Return `null` or an empty array to suppress cards.
   */
  buildResultCards?: (content: unknown) => AgentToolResultCard[] | null;
};

export type AgentToolDefinition<TInput = unknown, TResult = unknown> = {
  spec: ToolSpec;
  isAvailable?: (request: AgentRuntimeRequest) => boolean;
  guidance?: AgentToolGuidance;
  presentation?: AgentToolPresentation;
  validate: (args: unknown) => AgentToolInputValidation<TInput>;
  execute: (
    input: TInput,
    context: AgentToolContext,
  ) => Promise<AgentToolExecutionOutput<TResult>>;
  shouldRequireConfirmation?: (
    input: TInput,
    context: AgentToolContext,
  ) => boolean | Promise<boolean>;
  acceptInheritedApproval?: (
    input: TInput,
    approval: AgentInheritedApproval,
    context: AgentToolContext,
  ) => boolean | Promise<boolean>;
  createPendingAction?: (
    input: TInput,
    context: AgentToolContext,
  ) => AgentPendingAction | Promise<AgentPendingAction>;
  applyConfirmation?: (
    input: TInput,
    resolutionData: unknown,
    context: AgentToolContext,
  ) => AgentToolInputValidation<TInput>;
  buildFollowupMessage?: (
    result: AgentToolResult,
    context: AgentToolContext,
  ) => Promise<AgentModelMessage | null>;
  createResultReviewAction?: (
    input: TInput,
    result: AgentToolResult,
    context: AgentToolContext,
  ) => AgentPendingAction | null | Promise<AgentPendingAction | null>;
  resolveResultReview?: (
    input: TInput,
    result: AgentToolResult,
    resolution: AgentConfirmationResolution,
    context: AgentToolContext,
  ) => AgentToolReviewResolution | Promise<AgentToolReviewResolution>;
};

export type PreparedToolExecutionResult = {
  tool: AgentToolDefinition<any, any>;
  input: unknown;
  result: AgentToolResult;
};

export type PreparedToolExecutionOptions = {
  inheritedApproval?: AgentInheritedApproval;
  forceConfirmation?: boolean;
};

export type PreparedToolExecution =
  | {
      kind: "result";
      execution: PreparedToolExecutionResult;
    }
  | {
      kind: "confirmation";
      requestId: string;
      action: AgentPendingAction;
      execute: (
        resolutionData?: unknown,
      ) => Promise<PreparedToolExecutionResult>;
      deny: (resolutionData?: unknown) => PreparedToolExecutionResult;
    };
