/**
 * Docker Compose Generator
 *
 * Generates per-preview docker-compose.yml files (no Caddy sidecar — the
 * shared gateway handles routing). Services connect to the external
 * `cw-preview-net` network for gateway access and an internal bridge
 * network for inter-service communication.
 */

import yaml from 'js-yaml';
import type { PreviewConfig, PreviewServiceConfig } from './types.js';
import { PREVIEW_LABELS, GATEWAY_NETWORK } from './types.js';

export interface ComposeGeneratorOptions {
  projectPath: string;
  deploymentId: string;
  labels: Record<string, string>;
  mode: 'preview' | 'dev';
}

interface ComposeService {
  build?: { context: string; dockerfile: string } | string;
  image?: string;
  environment?: Record<string, string>;
  volumes?: string[];
  labels?: Record<string, string>;
  networks?: string[];
  depends_on?: string[];
  container_name?: string;
  command?: string;
  working_dir?: string;
}

interface ComposeFile {
  services: Record<string, ComposeService>;
  networks: Record<string, { driver?: string; external?: boolean }>;
}

/**
 * Generate a Docker Compose YAML string for a preview deployment.
 *
 * Structure:
 * - User-defined services with build contexts (no published ports)
 * - Public services connect to both cw-preview-net and internal network
 * - Internal services connect only to internal network
 * - Container names: cw-preview-<id>-<service> for DNS resolution on shared network
 * - Dev mode: uses image + volumes + command instead of build
 */
export function generateComposeFile(
  config: PreviewConfig,
  opts: ComposeGeneratorOptions,
): string {
  const compose: ComposeFile = {
    services: {},
    networks: {
      [GATEWAY_NETWORK]: { external: true },
      internal: { driver: 'bridge' },
    },
  };

  for (const [name, svc] of Object.entries(config.services)) {
    const containerName = `cw-preview-${opts.deploymentId}-${name}`;
    const service: ComposeService = {
      container_name: containerName,
      labels: { ...opts.labels },
      networks: svc.internal
        ? ['internal']
        : [GATEWAY_NETWORK, 'internal'],
    };

    if (opts.mode === 'dev' && svc.dev) {
      // Dev mode: use dev image + mount worktree
      service.image = svc.dev.image;
      service.working_dir = svc.dev.workdir ?? '/app';
      service.volumes = [
        `${opts.projectPath}:${svc.dev.workdir ?? '/app'}`,
        // Anonymous volume for node_modules to avoid host overwrite
        `${svc.dev.workdir ?? '/app'}/node_modules`,
      ];
      if (svc.dev.command) {
        service.command = svc.dev.command;
      }
    } else {
      // Preview mode: build from source
      if (svc.build) {
        if (typeof svc.build === 'string') {
          service.build = {
            context: opts.projectPath,
            dockerfile: svc.build === '.' ? 'Dockerfile' : svc.build,
          };
        } else {
          service.build = {
            context: svc.build.context.startsWith('/')
              ? svc.build.context
              : `${opts.projectPath}/${svc.build.context}`,
            dockerfile: svc.build.dockerfile,
          };
        }
      } else if (svc.image) {
        service.image = svc.image;
      }
    }

    // Environment
    if (svc.env && Object.keys(svc.env).length > 0) {
      service.environment = svc.env;
    }

    // Volumes (from config, not dev overrides)
    if (opts.mode !== 'dev' && svc.volumes && svc.volumes.length > 0) {
      service.volumes = svc.volumes;
    }

    compose.services[name] = service;
  }

  return yaml.dump(compose, { lineWidth: 120, noRefs: true });
}

/**
 * Generate compose labels for a preview deployment.
 */
export function generateLabels(opts: {
  initiativeId: string;
  phaseId?: string;
  projectId: string;
  branch: string;
  gatewayPort: number;
  previewId: string;
  mode: 'preview' | 'dev';
  agentId?: string;
}): Record<string, string> {
  const labels: Record<string, string> = {
    [PREVIEW_LABELS.preview]: 'true',
    [PREVIEW_LABELS.initiativeId]: opts.initiativeId,
    [PREVIEW_LABELS.projectId]: opts.projectId,
    [PREVIEW_LABELS.branch]: opts.branch,
    [PREVIEW_LABELS.port]: String(opts.gatewayPort),
    [PREVIEW_LABELS.previewId]: opts.previewId,
    [PREVIEW_LABELS.mode]: opts.mode,
  };

  if (opts.phaseId) {
    labels[PREVIEW_LABELS.phaseId] = opts.phaseId;
  }

  if (opts.agentId) {
    labels[PREVIEW_LABELS.agentId] = opts.agentId;
  }

  return labels;
}
