import * as fs from 'node:fs';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { createTarball } from '../utils/archive.js';
import {
  readManifestFromPackageDir,
  readVersionFromPackageDir,
  validateResolvedManifest,
} from '../utils/package-store.js';

export interface IPackCommandOptions {
  readonly cwd?: string;
  readonly outDir?: string;
  readonly buildFirst?: boolean;
}

export interface IPackCommandResult {
  readonly packageName: string;
  readonly version: string;
  readonly tarballPath: string;
  readonly sourceDir: string;
}

function packageFileName(packageName: string, version: string): string {
  const normalized = packageName.replace(/^@/, '').split('/').join('-');
  return `${normalized}-${version}.tar.gz`;
}

function runBuild(sourceDir: string): void {
  const result = spawnSync('npm', ['run', 'build'], {
    cwd: sourceDir,
    encoding: 'utf8',
    windowsHide: true,
  });
  if (result.status !== 0) {
    throw new Error(`Build failed: ${result.stderr || result.stdout || `exit ${result.status ?? 'unknown'}`}`);
  }
}

export async function packCommand(options: IPackCommandOptions = {}): Promise<IPackCommandResult> {
  const sourceDir = path.resolve(options.cwd ?? process.cwd());
  if (!fs.existsSync(sourceDir)) {
    throw new Error(`Package directory does not exist: ${sourceDir}`);
  }

  const { packageName, manifest } = readManifestFromPackageDir(sourceDir);
  validateResolvedManifest(packageName, manifest);
  const version = readVersionFromPackageDir(sourceDir);

  if (options.buildFirst) {
    runBuild(sourceDir);
  }

  const outDir = path.resolve(sourceDir, options.outDir ?? '.mx-pack');
  fs.mkdirSync(outDir, { recursive: true });
  const tarballPath = path.join(outDir, packageFileName(packageName, version));
  createTarball(sourceDir, tarballPath);

  return {
    packageName,
    version,
    tarballPath,
    sourceDir,
  };
}
