import { exec } from 'child_process';
import { promisify } from 'util';
import { request as httpRequest } from 'http';
import { logger } from '../utils/logger';
import { env } from '../config/env';

const execAsync = promisify(exec);

interface DockerContainerStatus {
  running: boolean;
  status: string;
  exitCode?: number;
  error?: string;
}

export interface DockerContainerInfo {
  name: string;
  label: string;
  running: boolean;
  status: string;
}

/**
 * Make a request to the Docker Engine API via the socket proxy (HTTP).
 */
function dockerRequest(
  method: string,
  path: string,
): Promise<{ statusCode: number; body: string }> {
  return new Promise((resolve, reject) => {
    const url = new URL(path, env.DOCKER_PROXY_URL);

    const options = {
      hostname: url.hostname,
      port: url.port,
      path: url.pathname + (url.search || ''),
      method,
    };

    const req = httpRequest(options, (res) => {
      const chunks: Buffer[] = [];
      res.on('data', (chunk: Buffer) => chunks.push(chunk));
      res.on('end', () => {
        resolve({
          statusCode: res.statusCode || 0,
          body: Buffer.concat(chunks).toString(),
        });
      });
    });

    req.setTimeout(5000, () => {
      req.destroy(new Error('Docker proxy request timed out'));
    });
    req.on('error', reject);
    req.end();
  });
}

/**
 * Check if a container is running via Docker socket proxy.
 */
async function getContainerStatus(containerName: string): Promise<DockerContainerStatus> {
  try {
    const response = await dockerRequest('GET', `/containers/${containerName}/json`);

    if (response.statusCode === 404) {
      return { running: false, status: 'not_found', error: 'Container not found' };
    }

    if (response.statusCode !== 200) {
      return { running: false, status: 'error', error: response.body };
    }

    const data = JSON.parse(response.body);
    return {
      running: data.State?.Running === true,
      status: data.State?.Status || 'unknown',
      exitCode: data.State?.ExitCode,
    };
  } catch (err) {
    logger.error(`Failed to check container status: ${containerName}`, err);
    return { running: false, status: 'error', error: (err as Error).message };
  }
}

/**
 * Derive a human-readable label from Docker Compose metadata.
 * Prefers compose service name, falls back to container name.
 */
function deriveLabel(container: Record<string, unknown>): string {
  const labels = (container.Labels || {}) as Record<string, string>;
  // Docker Compose v2 sets this label on every container
  const service = labels['com.docker.compose.service'];
  if (service) {
    // Capitalize and clean up: "media-api" → "Media API", "v2-postgres" → "V2 Postgres"
    return service
      .replace(/[-_]/g, ' ')
      .replace(/\b\w/g, (c) => c.toUpperCase())
      .replace(/\bV2\b/i, 'V2')
      .replace(/\bApi\b/, 'API')
      .replace(/\bDb\b/, 'DB');
  }
  // Fallback: strip common prefixes from container name
  const names = (container.Names || []) as string[];
  const name = names[0]?.replace(/^\//, '') || 'unknown';
  return name;
}

/**
 * List all containers in the configured Docker network with their statuses.
 * Eliminates the need for a hardcoded container name list.
 */
async function listNetworkContainers(): Promise<DockerContainerInfo[]> {
  try {
    const network = env.DOCKER_NETWORK_NAME;
    const filterJson = JSON.stringify({ network: [network] });
    const path = `/containers/json?all=true&filters=${encodeURIComponent(filterJson)}`;
    const response = await dockerRequest('GET', path);

    if (response.statusCode !== 200) {
      logger.error(`Failed to list containers: HTTP ${response.statusCode}`);
      return [];
    }

    const containers = JSON.parse(response.body) as Record<string, unknown>[];
    return containers.map((c) => {
      const names = (c.Names || []) as string[];
      const name = names[0]?.replace(/^\//, '') || 'unknown';
      const state = (c.State as string) || 'unknown';
      return {
        name,
        label: deriveLabel(c),
        running: state === 'running',
        status: state,
      };
    });
  } catch (err) {
    logger.error('Failed to list network containers:', err);
    return [];
  }
}

/**
 * Restart/recreate a container using docker compose.
 * This ensures environment variables are picked up.
 */
async function restartContainer(serviceName: string): Promise<{ success: boolean; output: string }> {
  try {
    // Use docker compose to recreate the container with updated env vars
    const { stdout, stderr } = await execAsync(`docker compose up -d ${serviceName}`, {
      cwd: '/app', // Assuming docker-compose.yml is in /app
      timeout: 30000, // 30 second timeout
    });

    const output = stdout + stderr;
    logger.info(`Container restart output: ${output}`);

    return { success: true, output };
  } catch (err) {
    const error = err as { stdout?: string; stderr?: string; message: string };
    const output = (error.stdout || '') + (error.stderr || '') + error.message;
    logger.error(`Failed to restart container: ${serviceName}`, err);
    return { success: false, output };
  }
}

export const dockerService = {
  getContainerStatus,
  listNetworkContainers,
  restartContainer,
};
