// Designer Service — streams MCP app UI design via Claude

import Anthropic from '@anthropic-ai/sdk';
import { getSkill } from '../skills/loader';
import type {
  PipelineEvent,
  ToolDefinition,
  AppPattern,
  AppBundle,
} from '../types';

const MODEL = 'claude-sonnet-4-5-20250514';
const MAX_TOKENS = 8192;

export interface AppDesignConfig {
  name: string;
  pattern: AppPattern;
  widgets: string[];
}

/**
 * Design an MCP app UI for the given tools and configuration.
 * Streams progress and yields the final AppBundle.
 */
export async function* designApp(
  tools: ToolDefinition[],
  appConfig: AppDesignConfig
): AsyncGenerator<PipelineEvent> {
  const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
  const systemPrompt = getSkill('designer');

  yield {
    type: 'design:progress',
    app: appConfig.name,
    percent: 0,
  };

  const toolDescriptions = tools.map((t) => ({
    name: t.name,
    description: t.description,
    inputSchema: t.inputSchema,
    outputSchema: t.outputSchema,
  }));

  try {
    const stream = client.messages.stream({
      model: MODEL,
      max_tokens: MAX_TOKENS,
      system: systemPrompt,
      messages: [
        {
          role: 'user',
          content: `Design an MCP App with the following requirements:

## App Configuration
- **Name**: ${appConfig.name}
- **Pattern**: ${appConfig.pattern}
- **Widgets**: ${appConfig.widgets.join(', ')}

## Available MCP Tools
\`\`\`json
${JSON.stringify(toolDescriptions, null, 2)}
\`\`\`

## Requirements
1. Generate a single self-contained HTML file with embedded CSS and JavaScript
2. Use the "${appConfig.pattern}" layout pattern
3. Include the following widgets: ${appConfig.widgets.join(', ')}
4. Each widget should bind to the appropriate MCP tool(s) via window.mcpRequest()
5. The app must call tools via the MCP Apps bridge: window.mcpRequest(toolName, params)
6. Include responsive design, clean modern UI (Tailwind CDN is fine)
7. Include loading states, error handling, and empty states
8. Wrap the complete HTML in a \`\`\`html code block

## Tool Binding Format
For each widget, specify which tool it uses as a data-mcp-tool attribute.

Generate the complete app now.`,
        },
      ],
    });

    let fullText = '';

    for await (const event of stream) {
      if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
        fullText += event.delta.text;

        const percent = Math.min(85, Math.floor((fullText.length / 3000) * 85));
        // Yield progress at intervals
        if (percent % 10 < 2) {
          yield {
            type: 'design:progress',
            app: appConfig.name,
            percent,
          };
        }
      }
    }

    const finalMessage = await stream.finalMessage();

    yield { type: 'design:progress', app: appConfig.name, percent: 90 };

    // Extract the HTML from the response
    const html = extractHtml(fullText);
    const toolBindings = extractToolBindings(fullText, tools);

    if (html) {
      const bundle: AppBundle = {
        id: crypto.randomUUID(),
        name: appConfig.name,
        pattern: appConfig.pattern,
        html,
        toolBindings,
      };

      yield { type: 'design:complete', bundle };
    } else {
      yield {
        type: 'error',
        message: 'Failed to extract HTML app from Claude response',
        recoverable: true,
      };
    }
  } catch (error) {
    const msg = error instanceof Error ? error.message : String(error);
    yield {
      type: 'error',
      message: `App design failed: ${msg}`,
      recoverable: error instanceof Anthropic.RateLimitError,
    };
  }
}

function extractHtml(text: string): string | null {
  const htmlMatch = text.match(/```html\s*\n([\s\S]*?)\n```/);
  return htmlMatch ? htmlMatch[1].trim() : null;
}

function extractToolBindings(
  text: string,
  tools: ToolDefinition[]
): Record<string, string> {
  const bindings: Record<string, string> = {};

  // Look for data-mcp-tool attributes or mcpRequest calls
  for (const tool of tools) {
    if (text.includes(tool.name)) {
      // Map widget/component name to tool name
      const widgetMatch = text.match(
        new RegExp(`(?:data-mcp-tool|mcpRequest)\\s*[=(]\\s*['"]${tool.name}['"]`, 'i')
      );
      if (widgetMatch) {
        bindings[tool.name] = tool.name;
      }
    }
  }

  // Fallback: just map all mentioned tools
  if (Object.keys(bindings).length === 0) {
    for (const tool of tools) {
      if (text.includes(tool.name)) {
        bindings[tool.name] = tool.name;
      }
    }
  }

  return bindings;
}
