/**
 * Tool registry with annotations, safety tiers, and pack management.
 * Supports MCP tool annotations spec + custom metadata for lazy loading.
 */

import { z } from 'zod';

// Safety tiers — controls confirmation behavior
export type SafetyTier = 'green' | 'yellow' | 'red';

// Tool annotation metadata
export interface ToolAnnotations {
  /** Tool only reads data, no side effects */
  readOnlyHint?: boolean;
  /** Tool may perform destructive actions (delete, release) */
  destructiveHint?: boolean;
  /** Tool is idempotent — safe to retry */
  idempotentHint?: boolean;
  /** Tool may cost real money (Twilio charges) */
  costHint?: boolean;
  /** Human-readable cost estimate */
  estimatedCost?: string;
}

// Our extended tool metadata
export interface ToolMeta {
  name: string;
  description: string;
  category: string;
  pack: string;
  safety: SafetyTier;
  annotations: ToolAnnotations;
  inputSchema: z.ZodType<any>;
  _meta?: {
    labels: {
      category: string;
      access: "read" | "write" | "delete";
      complexity: "simple" | "complex" | "batch";
    };
  };
  isApp?: boolean;
  /** Tier 1 = always loaded, Tier 2 = lazy loaded */
  tier: 1 | 2;
}

export interface RegisteredTool extends ToolMeta {
  handler: (params: any) => Promise<ToolResult>;
}

export interface ToolResult {
  content: Array<{
    type: 'text' | 'resource';
    text?: string;
    resource?: {
      uri: string;
      mimeType: string;
      text: string;
    };
  }>;
  isError?: boolean;
  structuredContent?: {
    type: 'html';
    html: string;
  };
}

/**
 * Central registry for all tools across all packs.
 */
export class ToolRegistry {
  private tools = new Map<string, RegisteredTool>();
  private activePacks = new Set<string>();
  private onToolsChanged?: () => void;

  /** Set callback for when tools list changes (lazy loading) */
  setOnToolsChanged(callback: () => void) {
    this.onToolsChanged = callback;
  }

  /** Register a single tool */
  register(tool: RegisteredTool): void {
    this.tools.set(tool.name, tool);
  }

  /** Register all tools from a pack */
  registerPack(packName: string, tools: RegisteredTool[]): void {
    for (const tool of tools) {
      this.tools.set(tool.name, { ...tool, pack: packName });
    }
    this.activePacks.add(packName);
  }

  /** Activate a lazy-loaded pack and notify client */
  activatePack(packName: string, tools: RegisteredTool[]): void {
    if (this.activePacks.has(packName)) return; // Already loaded
    this.registerPack(packName, tools);
    this.onToolsChanged?.();
  }

  /** Deactivate a pack (unload tools) */
  deactivatePack(packName: string): void {
    if (!this.activePacks.has(packName)) return;
    for (const [name, tool] of this.tools) {
      if (tool.pack === packName) {
        this.tools.delete(name);
      }
    }
    this.activePacks.delete(packName);
    this.onToolsChanged?.();
  }

  /** Get a tool by name */
  get(name: string): RegisteredTool | undefined {
    return this.tools.get(name);
  }

  /** Get all currently registered tools (for tools/list response) */
  getAll(): RegisteredTool[] {
    return Array.from(this.tools.values());
  }

  /** Get tools for a specific pack */
  getByPack(packName: string): RegisteredTool[] {
    return Array.from(this.tools.values()).filter(t => t.pack === packName);
  }

  /** Get all active pack names */
  getActivePacks(): string[] {
    return Array.from(this.activePacks);
  }

  /** Check if a pack is loaded */
  isPackActive(packName: string): boolean {
    return this.activePacks.has(packName);
  }

  /** Convert to MCP tools/list format */
  toMCPToolsList(): Array<{
    name: string;
    description: string;
    inputSchema: any;
    _meta?: any;
    annotations?: Record<string, any>;
  }> {
    return this.getAll().map(tool => {
      const schema = tool.inputSchema instanceof z.ZodType
        ? zodToJsonSchema(tool.inputSchema)
        : tool.inputSchema;

      return {
        name: tool.name,
        description: tool.description,
        inputSchema: schema,
        _meta: tool._meta,
        annotations: {
          ...tool.annotations,
          category: tool.category,
          safety: tool.safety,
        },
      };
    });
  }
}

/** Convert Zod schema to JSON Schema (simplified) */
function zodToJsonSchema(schema: z.ZodType<any>): Record<string, any> {
  if (schema instanceof z.ZodObject) {
    const shape = schema.shape;
    const properties: Record<string, any> = {};
    const required: string[] = [];

    for (const [key, value] of Object.entries(shape)) {
      const zodValue = value as z.ZodType<any>;
      properties[key] = zodFieldToJsonSchema(zodValue);
      if (!(zodValue instanceof z.ZodOptional)) {
        required.push(key);
      }
    }

    return {
      type: 'object',
      properties,
      ...(required.length > 0 ? { required } : {}),
    };
  }

  return { type: 'object' };
}

function zodFieldToJsonSchema(field: z.ZodType<any>): Record<string, any> {
  if (field instanceof z.ZodOptional) {
    return zodFieldToJsonSchema(field.unwrap());
  }
  if (field instanceof z.ZodString) {
    return { type: 'string', description: field.description };
  }
  if (field instanceof z.ZodNumber) {
    return { type: 'number', description: field.description };
  }
  if (field instanceof z.ZodBoolean) {
    return { type: 'boolean', description: field.description };
  }
  if (field instanceof z.ZodArray) {
    return { type: 'array', items: zodFieldToJsonSchema(field.element), description: field.description };
  }
  if (field instanceof z.ZodEnum) {
    return { type: 'string', enum: field.options, description: field.description };
  }
  if (field instanceof z.ZodObject) {
    return zodToJsonSchema(field);
  }
  return { type: 'string' };
}
