import type {
  ReadResourceResult,
  CallToolResult,
} from "@modelcontextprotocol/sdk/types.js";
import type { CompleteResourceTemplateCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { ResourceAnnotations } from "./common.js";
import type { ToolAnnotations } from "./tool.js";
import type { AdaptersConfig } from "@mcp-ui/server";
import type { TypedCallToolResult } from "../utils/response-helpers.js";
import type { ClientCapabilityChecker, McpContext } from "./context.js";
import type { UnifiedWidgetMetadata } from "../widgets/adapters/types.js";
import type { z } from "zod";

// UIResourceContent type from MCP-UI
export type UIResourceContent = {
  type: "resource";
  resource: {
    uri: string;
    mimeType: string;
    _meta?: AppsSdkMetadata;
  } & ({ text: string; blob?: never } | { blob: string; text?: never });
};

/**
 * Apps SDK resource metadata fields
 *
 * These fields are set on the resource itself (in resource._meta).
 * They control how the widget is rendered and secured.
 *
 * @note Resource-level metadata for Apps SDK widgets
 * @see https://developers.openai.com/apps-sdk/build/mcp-server
 */
export interface AppsSdkMetadata extends Record<string, unknown> {
  /** Description of the widget for Apps SDK - helps the model understand what's displayed */
  "openai/widgetDescription"?: string;

  /** Content Security Policy for the widget */
  "openai/widgetCSP"?: {
    /** Domains the widget can connect to (for fetch, websocket, etc.) */
    connect_domains?: string[];
    /** Domains the widget can load resources from (scripts, styles, images, fonts) */
    resource_domains?: string[];
    /** Domains allowed for iframe embeds (optional - by default widgets cannot render subframes) */
    frame_domains?: string[];
    /** Domains that can receive openExternal redirects without the safe-link modal (optional) */
    redirect_domains?: string[];
  };

  /** Whether the widget prefers a border in card layout */
  "openai/widgetPrefersBorder"?: boolean;

  /** Whether the widget can initiate tool calls (component-initiated tool access) */
  "openai/widgetAccessible"?: boolean;

  /** Custom subdomain for the widget (e.g., 'chatgpt.com' becomes 'chatgpt-com.web-sandbox.oaiusercontent.com') */
  "openai/widgetDomain"?: string;

  /** Locale for the widget (e.g., 'en-US', 'fr-FR') */
  "openai/locale"?: string;

  /** Status text while tool is invoking */
  "openai/toolInvocation/invoking"?: string;

  /** Status text after tool has invoked */
  "openai/toolInvocation/invoked"?: string;
}

/**
 * Apps SDK tool metadata fields
 *
 * These fields are set on the tool itself (in tool._meta).
 * They connect the tool to its widget template and control invocation behavior.
 *
 * @note Tool-level metadata for Apps SDK integration
 * @see https://developers.openai.com/apps-sdk/build/mcp-server
 */
export interface AppsSdkToolMetadata extends Record<string, unknown> {
  /** URI of the output template resource that will render this tool's output */
  "openai/outputTemplate"?: string;

  /** Status text while tool is invoking */
  "openai/toolInvocation/invoking"?: string;

  /** Status text after tool has invoked */
  "openai/toolInvocation/invoked"?: string;

  /** Whether the widget can initiate tool calls */
  "openai/widgetAccessible"?: boolean;

  /** Whether this tool result can produce a widget */
  "openai/resultCanProduceWidget"?: boolean;
}

/**
 * Enhanced Resource Context that provides access to request context and
 * client capability information.
 *
 * This unified context provides:
 * - `auth` - Authentication info (when OAuth is configured)
 * - `req` - Hono request object
 * - `client` - Client capability checker (name, capabilities, MCP Apps support)
 * - All other Hono Context properties and methods
 *
 * @template HasOAuth - Whether OAuth is configured (affects auth availability)
 */
export type EnhancedResourceContext<HasOAuth extends boolean = false> =
  McpContext<HasOAuth> & { client: ClientCapabilityChecker };

/**
 * Helper interface for bivariant parameter checking on resource callbacks.
 * @internal
 */
interface ReadResourceCallbackBivariant<HasOAuth extends boolean> {
  bivarianceHack(
    ctx: EnhancedResourceContext<HasOAuth>
  ): Promise<CallToolResult | ReadResourceResult | TypedCallToolResult<any>>;
}

/**
 * Callback type for reading a static resource.
 * Supports both CallToolResult (from helpers) and ReadResourceResult (old API).
 *
 * @template HasOAuth - Whether OAuth is configured (affects ctx.auth availability)
 */
export type ReadResourceCallback<HasOAuth extends boolean = false> =
  ReadResourceCallbackBivariant<HasOAuth>["bivarianceHack"];

/**
 * Extract template params type from a resource template definition's schema.
 * Mirrors InferToolInput from tool.ts.
 */
export type InferTemplateParams<T> = T extends { schema: infer S }
  ? S extends z.ZodTypeAny
    ? z.infer<S>
    : Record<string, any>
  : Record<string, any>;

/**
 * Callback type for reading a resource template with parameters.
 * Supports both CallToolResult (from helpers) and ReadResourceResult (old API).
 *
 * Supports multiple callback signatures:
 * - `async () => ...` - No parameters
 * - `async (uri: URL) => ...` - Just URI (required)
 * - `async (uri: URL, params: TParams) => ...` - URI and parameters (both required)
 * - `async (uri: URL, params: TParams, ctx: EnhancedResourceContext) => ...` - All parameters (all required)
 *
 * The implementation checks callback.length to determine which signature to use.
 *
 * @template TParams - Type for URI template parameters (defaults to Record<string, any>)
 * @template HasOAuth - Whether OAuth is configured (affects ctx.auth availability)
 */
export type ReadResourceTemplateCallback<
  TParams extends Record<string, any> = Record<string, any>,
  HasOAuth extends boolean = false,
> =
  | (() => Promise<
      CallToolResult | ReadResourceResult | TypedCallToolResult<any>
    >)
  | ((
      uri: URL
    ) => Promise<
      CallToolResult | ReadResourceResult | TypedCallToolResult<any>
    >)
  | ((
      uri: URL,
      params: TParams
    ) => Promise<
      CallToolResult | ReadResourceResult | TypedCallToolResult<any>
    >)
  | ((
      uri: URL,
      params: TParams,
      ctx: EnhancedResourceContext<HasOAuth>
    ) => Promise<
      CallToolResult | ReadResourceResult | TypedCallToolResult<any>
    >);

/**
 * Complete callback for a resource template
 */
export interface ResourceTemplateCallbacks {
  complete?: {
    [variable: string]: string[] | CompleteResourceTemplateCallback;
  };
}

/**
 * Configuration for a resource template
 */
export interface ResourceTemplateConfig {
  /**
   * URI template with {param} placeholders for dynamic resources.
   *
   * @example "user://{userId}/profile"
   * @example "repo://{owner}/{repo}/file/{path}"
   */
  uriTemplate: string;
  /**
   * Unique name for the template.
   *
   * @example "user-profile"
   */
  name?: string;
  /**
   * MIME type of the resource content (e.g., "text/plain", "application/json").
   *
   * @example "application/json"
   */
  mimeType?: string;
  /**
   * Description of what the resource contains.
   *
   * @example "User profile data for the given userId"
   */
  description?: string;
  /** Complete callback for the resource template */
  callbacks?: ResourceTemplateCallbacks;
}

/**
 * Resource template definition with readCallback (old API)
 */
export interface ResourceTemplateDefinition<
  HasOAuth extends boolean = false,
  TParams extends Record<string, any> = Record<string, any>,
> {
  name: string;
  resourceTemplate: ResourceTemplateConfig;
  title?: string;
  description?: string;
  annotations?: ResourceAnnotations;
  /**
   * Optional Zod schema for URI template parameters. When provided, narrows
   * the `params` argument in the callback to `z.infer<schema>`.
   *
   * @example z.object({ id: z.string() })
   */
  schema?: z.ZodTypeAny;
  readCallback: ReadResourceTemplateCallback<TParams, HasOAuth>;
  _meta?: Record<string, unknown>;
}

/**
 * Resource template definition without readCallback (new API with separate callback parameter)
 */
export interface ResourceTemplateDefinitionWithoutCallback {
  name: string;
  resourceTemplate: ResourceTemplateConfig;
  title?: string;
  description?: string;
  annotations?: ResourceAnnotations;
  /**
   * Optional Zod schema for URI template parameters. When provided, narrows
   * the `params` argument in the callback to `z.infer<schema>`.
   *
   * @example z.object({ id: z.string() })
   */
  schema?: z.ZodTypeAny;
  _meta?: Record<string, unknown>;
}

/**
 * Flat resource template definition with readCallback (new API)
 *
 * Simplified structure where uriTemplate is directly on the definition object
 * instead of nested in a resourceTemplate property.
 */
export interface FlatResourceTemplateDefinition<
  HasOAuth extends boolean = false,
  TParams extends Record<string, any> = Record<string, any>,
> {
  /**
   * Unique identifier for the template.
   *
   * @example "user-profile"
   */
  name: string;
  /**
   * URI template with {param} placeholders.
   *
   * @example "user://{userId}/profile"
   */
  uriTemplate: string;
  /**
   * Human-readable title for the resource.
   *
   * @example "User Profile"
   */
  title?: string;
  /**
   * Description of what the resource contains.
   *
   * @example "User profile data for the given userId"
   */
  description?: string;
  /**
   * MIME type of the resource content.
   *
   * @example "application/json"
   */
  mimeType?: string;
  /** Optional annotations for the resource */
  annotations?: ResourceAnnotations;
  /**
   * Optional Zod schema for URI template parameters. When provided, narrows
   * the `params` argument in the readCallback to `z.infer<schema>`.
   *
   * @example z.object({ id: z.string() })
   */
  schema?: z.ZodTypeAny;
  /** Async callback function that returns the resource content */
  readCallback: ReadResourceTemplateCallback<TParams, HasOAuth>;
  _meta?: Record<string, unknown>;
  /** Complete callback for the resource template */
  callbacks?: ResourceTemplateCallbacks;
}

/**
 * Flat resource template definition without readCallback (new API with separate callback parameter)
 *
 * Simplified structure where uriTemplate is directly on the definition object
 * instead of nested in a resourceTemplate property.
 */
export interface FlatResourceTemplateDefinitionWithoutCallback {
  /**
   * Unique identifier for the template.
   *
   * @example "user-profile"
   */
  name: string;
  /**
   * URI template with {param} placeholders.
   *
   * @example "user://{userId}/profile"
   */
  uriTemplate: string;
  /**
   * Human-readable title for the resource.
   *
   * @example "User Profile"
   */
  title?: string;
  /**
   * Description of what the resource contains.
   *
   * @example "User profile data for the given userId"
   */
  description?: string;
  /**
   * MIME type of the resource content.
   *
   * @example "application/json"
   */
  mimeType?: string;
  /** Optional annotations for the resource */
  annotations?: ResourceAnnotations;
  /**
   * Optional Zod schema for URI template parameters. When provided, narrows
   * the `params` argument in the callback to `z.infer<schema>`.
   *
   * @example z.object({ id: z.string() })
   */
  schema?: z.ZodTypeAny;
  _meta?: Record<string, unknown>;
  /** Complete callback for the resource template */
  callbacks?: ResourceTemplateCallbacks;
}

/**
 * Resource definition with readCallback (old API)
 */
export interface ResourceDefinition<HasOAuth extends boolean = false> {
  /**
   * Unique identifier for the resource.
   *
   * @example "app-settings"
   */
  name: string;
  /**
   * URI pattern for accessing the resource. Use a custom scheme (e.g., config://, app://).
   *
   * @example "config://app-settings"
   * @example "app://greeting"
   */
  uri: string;
  /**
   * Human-readable title for the resource.
   *
   * @example "App Settings"
   */
  title?: string;
  /**
   * Description of what the resource contains.
   *
   * @example "Application configuration and user preferences"
   */
  description?: string;
  /** MIME type of the resource content (required for old API) */
  mimeType: string;
  /** Optional annotations for the resource */
  annotations?: ResourceAnnotations;
  /** Async callback function that returns the resource content */
  readCallback: ReadResourceCallback<HasOAuth>;
  _meta?: Record<string, unknown>;
  /** Complete callback for the resource template */
  callbacks?: ResourceTemplateCallbacks;
}

/**
 * Resource definition without readCallback (new API with separate callback parameter)
 * MIME type is optional when using response helpers - it's inferred from the helper
 */
export interface ResourceDefinitionWithoutCallback {
  /**
   * Unique identifier for the resource.
   *
   * @example "app-settings"
   */
  name: string;
  /**
   * URI pattern for accessing the resource.
   *
   * @example "config://app-settings"
   * @example "app://greeting"
   */
  uri: string;
  /**
   * Human-readable title for the resource.
   *
   * @example "App Settings"
   */
  title?: string;
  /**
   * Description of what the resource contains.
   *
   * @example "Application configuration and user preferences"
   */
  description?: string;
  /**
   * MIME type (optional when using text(), object(), etc.—inferred from the helper).
   *
   * @example "application/json"
   */
  mimeType?: string;
  /** Optional annotations for the resource */
  annotations?: ResourceAnnotations;
  _meta?: Record<string, unknown>;
  /** Complete callback for the resource template */
  callbacks?: ResourceTemplateCallbacks;
}

/**
 * UIResource-specific types
 */
export interface WidgetProps {
  [key: string]: {
    type: "string" | "number" | "boolean" | "object" | "array";
    required?: boolean;
    default?: unknown;
    description?: string;
  };
}

/**
 * Encoding options for UI resources
 */
export type UIEncoding = "text" | "blob";

/**
 * Framework options for Remote DOM resources
 */
export type RemoteDomFramework = "react" | "webcomponents";

/**
 * Base properties shared by all UI resource types
 */
interface BaseUIResourceDefinition {
  /**
   * Unique identifier for the resource/widget.
   *
   * @example "weather-display"
   * @example "product-search-result"
   */
  name: string;
  /**
   * Human-readable title for the widget.
   *
   * @example "Weather Display"
   */
  title?: string;
  /**
   * Description of what the widget does; helps the model understand when to use it.
   *
   * @example "Display weather information for a city"
   */
  description?: string;
  /**
   * Widget properties/parameters configuration.
   * Each key maps to a prop schema with type, required, default, description.
   */
  props?: WidgetProps;
  /**
   * Preferred frame size [width, height].
   *
   * @example ["800px", "600px"]
   */
  size?: [string, string];
  /** Resource annotations for discovery and presentation */
  annotations?: ResourceAnnotations;
  /**
   * Encoding for the resource content (defaults to 'text').
   * Use 'blob' for binary content.
   */
  encoding?: UIEncoding;
  /**
   * Whether to auto-register this widget as an MCP tool (defaults to true).
   * Set to false when the widget is paired with a custom server.tool().
   */
  exposeAsTool?: boolean;
  /** Tool annotations when registered as a tool */
  toolAnnotations?: ToolAnnotations;
  /**
   * For auto-registered widgets: function or helper that generates the tool output (what the model sees).
   * If not provided, defaults to a summary message.
   * @example
   * ```typescript
   * // As a function
   * toolOutput: (params) => text(`Found ${params.count} items`)
   *
   * // As a static helper
   * toolOutput: text('Processing complete')
   *
   * // With object helper
   * toolOutput: (params) => object({ count: params.count })
   * ```
   */
  toolOutput?:
    | ((
        params: Record<string, any>
      ) =>
        | import("@modelcontextprotocol/sdk/types.js").CallToolResult
        | import("../utils/response-helpers.js").TypedCallToolResult<any>)
    | import("@modelcontextprotocol/sdk/types.js").CallToolResult
    | import("../utils/response-helpers.js").TypedCallToolResult<any>;

  _meta?: Record<string, unknown>;
}

/**
 * External URL UI resource - serves widget via iframe (legacy MCP-UI)
 */
export interface ExternalUrlUIResource extends BaseUIResourceDefinition {
  type: "externalUrl";
  /** Widget identifier (e.g., 'kanban-board', 'chart') */
  widget: string;
  /** Adapter configuration */
  adapters?: AdaptersConfig;
  /** Apps SDK metadata fields */
  appsSdkMetadata?: AppsSdkMetadata;
}

/**
 * Raw HTML UI resource - direct HTML content (legacy MCP-UI)
 */
export interface RawHtmlUIResource extends BaseUIResourceDefinition {
  type: "rawHtml";
  /** HTML content to render */
  htmlContent: string;
  /** Adapter configuration */
  adapters?: AdaptersConfig;
  /** Apps SDK metadata fields */
  appsSdkMetadata?: AppsSdkMetadata;
}

/**
 * Remote DOM UI resource - scripted UI components (legacy MCP-UI)
 */
export interface RemoteDomUIResource extends BaseUIResourceDefinition {
  type: "remoteDom";
  /** JavaScript code for remote DOM manipulation */
  script: string;
  /** Framework for remote DOM (defaults to 'react') */
  framework?: RemoteDomFramework;
  /** Adapter configuration */
  adapters?: AdaptersConfig;
  /** Apps SDK metadata fields */
  appsSdkMetadata?: AppsSdkMetadata;
}

/**
 * Apps SDK UI resource - OpenAI Apps SDK compatible widget
 *
 * This type follows the official OpenAI Apps SDK pattern:
 * - Uses text/html+skybridge mime type
 * - Supports component HTML with embedded JS/CSS
 * - Tool returns structuredContent that gets injected as window.openai.toolOutput
 * - Supports CSP, widget domains, and other Apps SDK metadata
 *
 * @see https://developers.openai.com/apps-sdk/build/mcp-server
 * @see https://mcpui.dev/guide/apps-sdk
 */
export interface AppsSdkUIResource extends BaseUIResourceDefinition {
  type: "appsSdk";
  /** HTML template content - the component that will be rendered */
  htmlTemplate: string;
  /** Apps SDK-specific metadata */
  appsSdkMetadata?: AppsSdkMetadata;
}

/**
 * MCP Apps UI resource - Official MCP Apps Extension (SEP-1865) compatible widget
 *
 * This type follows the official MCP Apps Extension pattern:
 * - Uses text/html;profile=mcp-app mime type
 * - Dual-protocol support: works with both ChatGPT and MCP Apps clients
 * - Unified metadata format that adapters transform to protocol-specific formats
 * - Supports MCP Apps CSP, widget preferences, and other metadata
 *
 * @see https://github.com/modelcontextprotocol/ext-apps
 * @see https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/
 */
export interface McpAppsUIResource extends BaseUIResourceDefinition {
  type: "mcpApps";
  /** HTML template content - the component that will be rendered */
  htmlTemplate: string;
  /** Unified metadata that works with both protocols (follows SEP-1865 + ChatGPT extensions) */
  metadata?: UnifiedWidgetMetadata;
  /** Optional: Apps SDK-specific metadata for advanced ChatGPT features */
  appsSdkMetadata?: AppsSdkMetadata;
}

/**
 * Discriminated union of all UI resource types
 */
export type UIResourceDefinition =
  | ExternalUrlUIResource
  | RawHtmlUIResource
  | RemoteDomUIResource
  | AppsSdkUIResource
  | McpAppsUIResource;

export interface WidgetConfig {
  /** Widget directory name */
  name: string;
  /** Absolute path to widget directory */
  path: string;
  /** Widget manifest if present */
  manifest?: WidgetManifest;
  /** Main component file name */
  component?: string;
}

export interface WidgetManifest {
  /**
   * Unique widget identifier (must match directory/file name).
   *
   * @example "weather-display"
   */
  name: string;
  /**
   * Human-readable title.
   *
   * @example "Weather Display"
   */
  title?: string;
  /**
   * Description of what the widget displays.
   *
   * @example "Display weather information for a city"
   */
  description?: string;
  /**
   * Semantic version of the widget.
   *
   * @example "1.0.0"
   */
  version?: string;
  /** Widget props schema (type, required, default per prop) */
  props?: WidgetProps;
  /**
   * Preferred frame size [width, height].
   *
   * @example ["800px", "600px"]
   */
  size?: [string, string];
  /** Asset paths for scripts, styles, main entry */
  assets?: {
    main?: string;
    scripts?: string[];
    styles?: string[];
  };
}

export interface DiscoverWidgetsOptions {
  /**
   * Path to widgets directory.
   * Defaults to dist/resources/mcp-use/widgets.
   *
   * @example "./resources/widgets"
   */
  path?: string;
  /**
   * Automatically register widgets that don't have manifests.
   * Defaults to false.
   */
  autoRegister?: boolean;
  /**
   * Filter widgets by name pattern (string or RegExp).
   *
   * @example "weather-*"
   */
  filter?: string | RegExp;
}
