import { z } from '@frontmcp/lazy-zod';

import { type RawZodShape } from './zod-utils';

/**
 * Transport protocol types (excludes non-transport intents like 'delete-session')
 */
export type TransportProtocolType = 'legacy-sse' | 'sse' | 'streamable-http' | 'stateful-http' | 'stateless-http';

/**
 * Known AI platform types that can be detected from client info.
 * Used for platform-specific rendering and behavior customization.
 */
export type AIPlatformType =
  | 'openai' // ChatGPT, OpenAI API clients
  | 'claude' // Claude Desktop, Claude API clients
  | 'gemini' // Google Gemini clients
  | 'cursor' // Cursor IDE
  | 'continue' // Continue.dev
  | 'cody' // Sourcegraph Cody
  | 'generic-mcp' // Generic MCP client
  | 'ext-apps' // MCP Apps spec compliant clients (io.modelcontextprotocol/ui extension)
  | 'unknown'; // Unknown client

/**
 * Zod schema for AIPlatformType validation
 */
export const aiPlatformTypeSchema = z.enum([
  'openai',
  'claude',
  'gemini',
  'cursor',
  'continue',
  'cody',
  'generic-mcp',
  'ext-apps',
  'unknown',
]);

/**
 * Decoded JWT payload (if any) or empty object
 */
export type UserClaim = {
  iss: string;
  sid?: string;
  sub: string;
  exp?: number;
  iat?: number;
  aud?: string | string[];
  email?: string;
  username?: string;
  preferred_username?: string;
  name?: string;
  picture?: string;
};
export const userClaimSchema = z
  .object({
    iss: z.string(),
    sid: z.string().optional(),
    sub: z.string(),
    exp: z.number().optional(),
    iat: z.number().optional(),
    aud: z.union([z.string(), z.array(z.string())]).optional(),
    email: z.string().optional(),
    username: z.string().optional(),
    preferred_username: z.string().optional(),
    name: z.string().optional(),
    picture: z.string().optional(),
  } satisfies RawZodShape<UserClaim>)
  .passthrough();

export type SessionIdPayload = {
  /* The actual node id that handle the transport session */
  nodeId: string;
  /* The signature of the token used to create the session */
  authSig: string;
  /* The unique id of the session */
  uuid: string;
  /* The timestamp of the session creation */
  iat: number;
  /* The protocol used in existing transport - optional for stateless mode */
  protocol?: TransportProtocolType;
  /* True if session was created in public mode (anonymous access) */
  isPublic?: boolean;
  /* The detected AI platform type from MCP initialize clientInfo */
  platformType?: AIPlatformType;
  /* The MCP client application name (from initialize clientInfo) */
  clientName?: string;
  /* The MCP client version (from initialize clientInfo) */
  clientVersion?: string;
  /* Whether the client supports MCP elicitation (from initialize capabilities) */
  supportsElicitation?: boolean;
  /**
   * Whether this session is in skills-only mode.
   * When true, tools/list returns empty array but skills/search and skills/load work normally.
   * This is useful for planner agents that only need skill information.
   */
  skillsOnlyMode?: boolean;
};
export const sessionIdPayloadSchema = z.object({
  nodeId: z.string(),
  authSig: z.string(),
  uuid: z.string().uuid(),
  iat: z.number(),
  protocol: z.enum(['legacy-sse', 'sse', 'streamable-http', 'stateful-http', 'stateless-http']).optional(),
  isPublic: z.boolean().optional(),
  platformType: aiPlatformTypeSchema.optional(),
  clientName: z.string().optional(),
  clientVersion: z.string().optional(),
  supportsElicitation: z.boolean().optional(),
  skillsOnlyMode: z.boolean().optional(),
} satisfies RawZodShape<SessionIdPayload>);

export interface Authorization {
  token: string;
  user: UserClaim;
  session?: {
    id: string;
    /** Payload may be undefined when session validation failed but ID is passed for transport lookup */
    payload?: SessionIdPayload;
  };
}

export const sessionIdSchema = z.object({
  id: z.string(),
  /** Payload is optional - may be undefined when session validation failed but ID is passed for transport lookup */
  payload: sessionIdPayloadSchema.optional(),
} satisfies RawZodShape<{ id: string; payload?: SessionIdPayload }>);

export const authorizationSchema = z.object({
  token: z.string(),
  session: sessionIdSchema.optional(),
  user: userClaimSchema,
} satisfies RawZodShape<Authorization>);
