import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as fs from 'fs';
import * as path from 'path';

export interface SystemPromptContext {
  tenantId: string;
  userId?: string;
  userEmail?: string;
  serverContext?: string;
  skills?: string[];
  riskBoundary?: string;
  additionalInstructions?: string;
}

/**
 * Builds system prompts for Claude CLI sessions.
 * Generates and optionally saves system prompt files that provide
 * context about the tenant, server environment, and operational boundaries.
 */
@Injectable()
export class SystemPromptBuilder {
  private readonly logger = new Logger(SystemPromptBuilder.name);

  constructor(private readonly configService: ConfigService) {}

  /**
   * Builds a system prompt string from the given context.
   */
  build(context: SystemPromptContext): string {
    const parts: string[] = [];

    // Base instruction
    parts.push(
      'You are iAgent, an AI-powered server cluster operations assistant built on IT0. ' +
        'You help users manage server infrastructure, deploy AI agents, and automate operations safely and efficiently.\n\n' +
        '## OpenClaw Agent Deployment\n' +
        'You can deploy real OpenClaw (小龙虾) AI agent instances for users. OpenClaw is an open-source autonomous AI agent.\n' +
        'Use the internal API (no auth, direct call). curl is NOT available — use wget:\n' +
        '  Create:  wget -q -O- --post-data=\'{"name":"<name>","userId":"<userId>","usePool":true}\' --header="Content-Type: application/json" http://localhost:3002/api/v1/agent/instances\n' +
        '  List:    wget -q -O- http://localhost:3002/api/v1/agent/instances\n' +
        '  Stop:    wget -q -O- --post-data="" --header="X-HTTP-Method-Override: PUT" http://localhost:3002/api/v1/agent/instances/<id>/stop\n' +
        '  Delete:  wget -q -O- --method=DELETE http://localhost:3002/api/v1/agent/instances/<id>\n\n' +
        'When a user asks to create a OpenClaw / 小龙虾 / personal AI agent / autonomous agent:\n' +
        '1. Ask for a name if not given\n' +
        '2. Use the Current User ID from this prompt as userId\n' +
        '3. Call the create API with Bash and report the result (id, status, containerName)\n\n' +
        '## DingTalk Channel Binding (钉钉接入)\n' +
        'To bind a DingTalk bot to an OpenClaw instance, the user needs a DingTalk app with Stream mode enabled:\n' +
        '  Step 1: Go to https://open.dingtalk.com/developer → "创建应用" → Enterprise Internal App\n' +
        '  Step 2: Enable "机器人" → set message receive mode to "Stream模式"\n' +
        '  Step 3: Copy the AppKey (ClientId) and AppSecret (ClientSecret)\n' +
        '  Step 4: Create/re-deploy the OpenClaw instance with DingTalk creds:\n' +
        '    wget -q -O- --post-data=\'{"name":"<name>","userId":"<userId>","usePool":true,"dingTalkClientId":"<appKey>","dingTalkClientSecret":"<appSecret>"}\' \\\n' +
        '      --header="Content-Type: application/json" http://localhost:3002/api/v1/agent/instances\n' +
        '  Step 5: Tell the user to add the bot to a DingTalk group or send it a direct message — it will respond via OpenClaw AI.',
    );

    // Tenant + user context
    parts.push(`\nTenant: ${context.tenantId}`);
    if (context.userId) {
      parts.push(`Current User ID: ${context.userId}${context.userEmail ? ` (${context.userEmail})` : ''}`);
      parts.push(`When creating OpenClaw instances for the current user, use userId: "${context.userId}"`);
    }

    // Server context (available servers, environment, etc.)
    if (context.serverContext) {
      parts.push(`\nServer Environment:\n${context.serverContext}`);
    }

    // Available skills
    if (context.skills && context.skills.length > 0) {
      parts.push(
        `\nAvailable Skills: ${context.skills.join(', ')}`,
      );
    }

    // Risk boundary
    if (context.riskBoundary) {
      parts.push(`\nRisk Boundary:\n${context.riskBoundary}`);
    } else {
      parts.push(
        '\nRisk Boundary:\n' +
          '- L0 (Read-only): Auto-execute (ls, cat, df, top, etc.)\n' +
          '- L1 (Low-risk write): Confirm before execution\n' +
          '- L2 (High-risk): Require multi-channel approval\n' +
          '- L3 (Forbidden): Block entirely (rm -rf /, mkfs, etc.)',
      );
    }

    // Additional instructions
    if (context.additionalInstructions) {
      parts.push(`\n${context.additionalInstructions}`);
    }

    return parts.join('\n');
  }

  /**
   * Builds the system prompt and saves it to a temporary file.
   * Returns the file path. Useful for the CLI --system-prompt flag
   * when using file-based prompts.
   */
  async buildAndSave(
    context: SystemPromptContext,
    sessionId: string,
  ): Promise<string> {
    const prompt = this.build(context);
    const tmpDir = this.configService.get<string>(
      'SYSTEM_PROMPT_DIR',
      path.join(process.cwd(), '.tmp', 'prompts'),
    );

    if (!fs.existsSync(tmpDir)) {
      fs.mkdirSync(tmpDir, { recursive: true });
    }

    const filePath = path.join(tmpDir, `${sessionId}.md`);
    fs.writeFileSync(filePath, prompt, 'utf-8');

    this.logger.debug(`System prompt saved to ${filePath}`);
    return filePath;
  }

  /**
   * Cleans up a saved system prompt file for a session.
   */
  cleanup(sessionId: string): void {
    const tmpDir = this.configService.get<string>(
      'SYSTEM_PROMPT_DIR',
      path.join(process.cwd(), '.tmp', 'prompts'),
    );
    const filePath = path.join(tmpDir, `${sessionId}.md`);

    if (fs.existsSync(filePath)) {
      fs.unlinkSync(filePath);
      this.logger.debug(`Cleaned up system prompt: ${filePath}`);
    }
  }
}
