import { exec } from 'child_process';
import { promisify } from 'util';
import { join } from 'path';
import type { Service } from '../services/types.js';
import { createDeployment, updateDeployment } from './repository.js';
import { productId } from '../../lib/config.js';
import { childLogger } from '../../lib/logger.js';

const execAsync = promisify(exec);
const log = childLogger('deployments/orchestrator');

export async function triggerDeployment(service: Service, triggeredBy: string): Promise<string> {
  // Create deployment record
  const deployment = await createDeployment({
    serviceId: service.id,
    version: 'pending',
    triggeredBy,
    productId,
  });

  const deploymentId = deployment.id;

  // Trigger bash script asynchronously
  runDeploymentScript(service, deploymentId).catch(error => {
    log.error({ err: error, deploymentId, serviceId: service.id }, 'background deployment failed');
  });

  return deploymentId;
}

async function runDeploymentScript(service: Service, deploymentId: string) {
  const scriptDir = join(process.cwd(), '../../'); // Go to bytelyst-devops-tools root
  const scriptPath = join(scriptDir, service.scriptPath);

  let finalStatus: 'success' | 'failed' = 'failed';
  let logs = '';
  let version: string | undefined;

  try {
    const { stdout, stderr } = await execAsync(`bash ${scriptPath}`, {
      cwd: scriptDir,
      timeout: 300000, // 5 minutes
      env: {
        ...process.env,
        // Add any deployment-specific env vars if needed
      },
    });

    logs = `STDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`;
    finalStatus = 'success';
    version = extractVersion(stdout + stderr) || 'unknown';

    // Update service status
    const { getServiceById, updateService } = await import('../services/repository.js');
    const svc = await getServiceById(service.id);
    if (svc) {
      await updateService(service.id, {
        status: 'up',
        lastDeployedAt: new Date().toISOString(),
        version: version || svc.version,
      });
    }
  } catch (error: any) {
    logs = error instanceof Error
      ? `ERROR: ${error.message}\n\n${(error as any).stdout ? `STDOUT:\n${(error as any).stdout}\n\n` : ''}${(error as any).stderr ? `STDERR:\n${(error as any).stderr}` : ''}`
      : String(error);

    // Update service status to down
    const { getServiceById, updateService } = await import('../services/repository.js');
    const svc = await getServiceById(service.id);
    if (svc) {
      await updateService(service.id, {
        status: 'down',
      });
    }
  } finally {
    // Always write final status — ensures the deployment never gets stuck in 'running'
    try {
      await updateDeployment(deploymentId, {
        status: finalStatus,
        logs,
        completedAt: new Date().toISOString(),
        ...(version ? { version } : {}),
      });
    } catch (updateError) {
      log.error(
        { err: updateError, deploymentId, finalStatus },
        'failed to persist final deployment status',
      );
    }
  }
}

function extractVersion(logs: string): string | null {
  // Try multiple version patterns
  const patterns = [
    /version[:\s]+([0-9.]+)/i,
    /v([0-9.]+\.[0-9.]+)/,
    /deployed.*?([0-9]+\.[0-9]+)/i,
  ];

  for (const pattern of patterns) {
    const match = logs.match(pattern);
    if (match && match[1]) {
      return match[1];
    }
  }

  return null;
}
