/**
 * MCPEngine Studio — Deploy as Download
 *
 * Creates a zip-ready directory structure with all generated files,
 * package.json, tsconfig, README, Dockerfile, and .env.example.
 */

import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import type {
  ServerBundle,
  DeployResult,
  GeneratedFile,
} from '@mcpengine/ai-pipeline/types';

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface DownloadProgress {
  step: string;
  message: string;
  percent: number;
  level: 'info' | 'success' | 'warning' | 'error';
}

// ---------------------------------------------------------------------------
// Template files
// ---------------------------------------------------------------------------

function generateReadme(bundle: ServerBundle, slug: string): string {
  const toolFiles = bundle.files.filter((f) => f.path.includes('tools/'));
  const toolList = toolFiles
    .map((f) => `- \`${f.path.replace(/^.*tools\//, '').replace(/\.ts$/, '')}\``)
    .join('\n');

  return `# ${slug} — MCP Server

Generated by [MCPEngine Studio](https://mcpengine.ai)

## Quick Start

\`\`\`bash
# Install dependencies
npm install

# Set up environment variables
cp .env.example .env
# Edit .env with your API keys

# Build
npm run build

# Start the server
npm start
\`\`\`

## Tools (${bundle.toolCount})

${toolList || '_(No tools found)_'}

## Development

\`\`\`bash
# Run in development mode with auto-reload
npm run dev

# Run tests
npm test

# Type check
npm run typecheck
\`\`\`

## Docker

\`\`\`bash
# Build the Docker image
docker build -t ${slug}-mcp .

# Run the container
docker run -p 3000:3000 --env-file .env ${slug}-mcp
\`\`\`

## Add to Claude Desktop

Add this to your Claude Desktop config (\`~/Library/Application Support/Claude/claude_desktop_config.json\`):

\`\`\`json
{
  "mcpServers": {
    "${slug}": {
      "command": "node",
      "args": ["${slug}/dist/index.js"],
      "env": {}
    }
  }
}
\`\`\`

## License

MIT
`;
}

function generateDockerfile(slug: string): string {
  return `# ${slug} MCP Server
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\
  CMD wget -q --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/index.js"]
`;
}

function generateDockerignore(): string {
  return `node_modules
dist
.env
.git
.gitignore
*.md
Dockerfile
.dockerignore
.mcpengine-output
`;
}

function generateEnvExample(bundle: ServerBundle): string {
  const lines = [
    '# MCPEngine Server Environment Variables',
    '# Copy this file to .env and fill in your values',
    '',
    '# Server Configuration',
    'PORT=3000',
    'NODE_ENV=production',
    '',
    '# API Authentication',
    'API_KEY=your_api_key_here',
    'API_BASE_URL=https://api.example.com',
    '',
    '# Optional: OAuth2 (if required by the target API)',
    '# OAUTH_CLIENT_ID=',
    '# OAUTH_CLIENT_SECRET=',
    '# OAUTH_TOKEN_URL=',
    '',
  ];
  return lines.join('\n');
}

function generateTsConfig(): string {
  return JSON.stringify(
    {
      compilerOptions: {
        target: 'ES2022',
        module: 'NodeNext',
        moduleResolution: 'NodeNext',
        lib: ['ES2022'],
        outDir: './dist',
        rootDir: './src',
        strict: true,
        esModuleInterop: true,
        skipLibCheck: true,
        forceConsistentCasingInFileNames: true,
        resolveJsonModule: true,
        declaration: true,
        declarationMap: true,
        sourceMap: true,
      },
      include: ['src/**/*'],
      exclude: ['node_modules', 'dist'],
    },
    null,
    2,
  );
}

function generatePackageJson(bundle: ServerBundle, slug: string): string {
  const pkg = {
    name: `@mcpengine/${slug}`,
    version: '1.0.0',
    description: `MCP Server generated by MCPEngine Studio`,
    type: 'module',
    main: 'dist/index.js',
    types: 'dist/index.d.ts',
    scripts: {
      build: 'tsc',
      dev: 'tsx watch src/index.ts',
      start: 'node dist/index.js',
      typecheck: 'tsc --noEmit',
      test: 'vitest run',
    },
    dependencies: {
      '@modelcontextprotocol/sdk': '^1.0.0',
      zod: '^3.23.0',
    },
    devDependencies: {
      typescript: '^5.5.0',
      tsx: '^4.16.0',
      vitest: '^2.0.0',
      '@types/node': '^20.14.0',
    },
    engines: {
      node: '>=20',
    },
    license: 'MIT',
    // Merge with bundle's package.json if present
    ...(typeof bundle.packageJson === 'object' ? bundle.packageJson : {}),
  };

  // Ensure name/version stay correct
  pkg.name = `@mcpengine/${slug}`;

  return JSON.stringify(pkg, null, 2);
}

function generateGitignore(): string {
  return `node_modules/
dist/
.env
.env.local
*.log
.DS_Store
.mcpengine-output/
`;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

export async function* deployAsDownload(
  bundle: ServerBundle,
  slug?: string,
): AsyncGenerator<DownloadProgress, DeployResult, void> {
  const serverSlug = slug ?? 'mcp-server';
  const deployId = crypto.randomUUID();
  const startedAt = new Date().toISOString();
  const logs: string[] = [];

  const log = (msg: string) => {
    logs.push(`[${new Date().toISOString()}] ${msg}`);
  };

  // ── Step 1: Create temp directory ──────────────────────────────────────
  yield {
    step: 'package',
    message: 'Creating project structure…',
    percent: 10,
    level: 'info',
  };

  const tmpDir = path.join(os.tmpdir(), `mcpengine-download-${deployId}`);
  const projectDir = path.join(tmpDir, serverSlug);
  const srcDir = path.join(projectDir, 'src');

  await fs.mkdir(srcDir, { recursive: true });
  log(`Created project directory: ${projectDir}`);

  // ── Step 2: Write source files ─────────────────────────────────────────
  yield {
    step: 'package',
    message: `Writing ${bundle.files.length} source files…`,
    percent: 30,
    level: 'info',
  };

  for (const file of bundle.files) {
    const filePath = path.join(srcDir, file.path);
    await fs.mkdir(path.dirname(filePath), { recursive: true });
    await fs.writeFile(filePath, file.content, 'utf-8');
    log(`Wrote ${file.path}`);
  }

  yield {
    step: 'package',
    message: `${bundle.files.length} source files written`,
    percent: 50,
    level: 'success',
  };

  // ── Step 3: Write config / meta files ──────────────────────────────────
  yield {
    step: 'package',
    message: 'Generating project configuration…',
    percent: 60,
    level: 'info',
  };

  const configFiles: { name: string; content: string }[] = [
    { name: 'package.json', content: generatePackageJson(bundle, serverSlug) },
    { name: 'tsconfig.json', content: generateTsConfig() },
    { name: 'README.md', content: generateReadme(bundle, serverSlug) },
    { name: 'Dockerfile', content: generateDockerfile(serverSlug) },
    { name: '.dockerignore', content: generateDockerignore() },
    { name: '.env.example', content: generateEnvExample(bundle) },
    { name: '.gitignore', content: generateGitignore() },
  ];

  for (const cf of configFiles) {
    await fs.writeFile(path.join(projectDir, cf.name), cf.content, 'utf-8');
    log(`Wrote ${cf.name}`);
  }

  yield {
    step: 'package',
    message: 'Project configuration complete',
    percent: 80,
    level: 'success',
  };

  // ── Step 4: Calculate stats ────────────────────────────────────────────
  yield {
    step: 'verify',
    message: 'Verifying download package…',
    percent: 90,
    level: 'info',
  };

  const totalFiles = bundle.files.length + configFiles.length;
  log(`Download package ready: ${totalFiles} files in ${projectDir}`);

  yield {
    step: 'verify',
    message: `Package ready (${totalFiles} files)`,
    percent: 100,
    level: 'success',
  };

  // ── Return result ──────────────────────────────────────────────────────
  return {
    id: deployId,
    target: 'download',
    status: 'live',
    url: projectDir,
    endpoint: projectDir,
    logs,
    createdAt: startedAt,
  };
}
