/**
 * MCPEngine Studio — Deploy Orchestrator
 *
 * Main entry point for the deployment pipeline.
 * Takes a DeployTarget + config, routes to the correct target,
 * and yields progress events via async generator.
 */

import type {
  DeployTarget,
  DeployConfig,
  DeployResult,
  ServerBundle,
} from '@mcpengine/ai-pipeline/types';
import { deployToMCPEngine } from './targets/mcpengine';
import { deployAsDownload } from './targets/download';

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

export interface DeployEvent {
  type: 'progress' | 'log' | 'complete' | 'error';
  step?: DeployStep;
  message: string;
  percent: number;
  level: 'info' | 'success' | 'warning' | 'error';
  timestamp: string;
  result?: DeployResult;
}

export type DeployStep = 'build' | 'test' | 'package' | 'deploy' | 'verify';

const STEP_ORDER: DeployStep[] = ['build', 'test', 'package', 'deploy', 'verify'];

// Map internal steps from targets to canonical DeploySteps
function mapStep(raw: string): DeployStep {
  const mapping: Record<string, DeployStep> = {
    compile: 'build',
    build: 'build',
    test: 'test',
    package: 'package',
    deploy: 'deploy',
    upload: 'deploy',
    verify: 'verify',
  };
  return mapping[raw] ?? 'build';
}

// ---------------------------------------------------------------------------
// Orchestrator
// ---------------------------------------------------------------------------

export async function* deploy(
  bundle: ServerBundle,
  config: DeployConfig,
): AsyncGenerator<DeployEvent, DeployResult, void> {
  const ts = () => new Date().toISOString();

  // ── Pre-flight ─────────────────────────────────────────────────────────
  yield {
    type: 'log',
    message: `Starting deployment to ${config.target}…`,
    percent: 0,
    level: 'info',
    timestamp: ts(),
  };

  yield {
    type: 'progress',
    step: 'build',
    message: 'Initializing build pipeline…',
    percent: 5,
    level: 'info',
    timestamp: ts(),
  };

  // ── Build step (validate bundle) ───────────────────────────────────────
  if (!bundle.files.length) {
    yield {
      type: 'error',
      step: 'build',
      message: 'Server bundle is empty — nothing to deploy',
      percent: 0,
      level: 'error',
      timestamp: ts(),
    };
    throw new Error('Empty server bundle');
  }

  yield {
    type: 'log',
    message: `Bundle: ${bundle.files.length} files, ${bundle.toolCount} tools, entry: ${bundle.entryPoint}`,
    percent: 8,
    level: 'info',
    timestamp: ts(),
  };

  // ── Test step (placeholder — real tests come from test pipeline) ──────
  yield {
    type: 'progress',
    step: 'test',
    message: 'Running pre-deploy checks…',
    percent: 15,
    level: 'info',
    timestamp: ts(),
  };

  // Quick static checks
  const hasEntry = bundle.files.some((f) => f.path === bundle.entryPoint);
  if (!hasEntry) {
    yield {
      type: 'error',
      step: 'test',
      message: `Entry point "${bundle.entryPoint}" not found in bundle`,
      percent: 15,
      level: 'error',
      timestamp: ts(),
    };
    throw new Error('Missing entry point');
  }

  yield {
    type: 'progress',
    step: 'test',
    message: 'Pre-deploy checks passed',
    percent: 20,
    level: 'success',
    timestamp: ts(),
  };

  // ── Route to target ────────────────────────────────────────────────────
  let innerGen: AsyncGenerator<any, DeployResult, void>;

  switch (config.target) {
    case 'mcpengine':
    case 'cloudflare':
      innerGen = deployToMCPEngine(
        config.slug ?? 'mcp-server',
        bundle,
        config,
      );
      break;

    case 'download':
      innerGen = deployAsDownload(bundle, config.slug);
      break;

    case 'npm':
      // TODO: npm publish pipeline
      yield {
        type: 'log',
        message: 'npm deployment is not yet implemented — falling back to download',
        percent: 25,
        level: 'warning',
        timestamp: ts(),
      };
      innerGen = deployAsDownload(bundle, config.slug);
      break;

    case 'docker':
      // TODO: Docker build/push pipeline
      yield {
        type: 'log',
        message: 'Docker deployment is not yet implemented — falling back to download',
        percent: 25,
        level: 'warning',
        timestamp: ts(),
      };
      innerGen = deployAsDownload(bundle, config.slug);
      break;

    default:
      throw new Error(`Unknown deploy target: ${config.target}`);
  }

  // ── Stream inner target events ─────────────────────────────────────────
  let result: DeployResult | undefined;

  while (true) {
    const { value, done } = await innerGen.next();

    if (done) {
      result = value;
      break;
    }

    // Forward progress events with normalized step names
    yield {
      type: 'progress',
      step: mapStep(value.step),
      message: value.message,
      percent: Math.min(20 + Math.round(value.percent * 0.75), 95),
      level: value.level,
      timestamp: ts(),
    };

    yield {
      type: 'log',
      message: value.message,
      percent: Math.min(20 + Math.round(value.percent * 0.75), 95),
      level: value.level,
      timestamp: ts(),
    };
  }

  if (!result) {
    throw new Error('Deploy target did not return a result');
  }

  // ── Complete ───────────────────────────────────────────────────────────
  yield {
    type: 'complete',
    step: 'verify',
    message: `Deployment complete! ${result.url ?? ''}`,
    percent: 100,
    level: 'success',
    timestamp: ts(),
    result,
  };

  return result;
}

// Re-exports for convenience
export { deployToMCPEngine } from './targets/mcpengine';
export { deployAsDownload } from './targets/download';
export { compileServer, compileWithMeta } from './compiler';
export { generateWorkerScript } from './worker-template';
export { STEP_ORDER };
export type { DeployTarget, DeployConfig, DeployResult };
