import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { spawn } from 'child_process';

export interface CommitWorkspaceOptions {
  workspacePath: string;
  branchName: string;
  commitMessage: string;
  ignoreEntries?: string[];
}

export interface CommitWorkspaceResult {
  changed: boolean;
  committed: boolean;
  pushed: boolean;
}

interface GitResult {
  code: number;
  stdout: string;
  stderr: string;
}

function runGit(workspacePath: string, args: string[], extraEnv?: Record<string, string>): Promise<GitResult> {
  return new Promise((resolve) => {
    const child = spawn('git', args, {
      cwd: workspacePath,
      stdio: ['ignore', 'pipe', 'pipe'],
      timeout: 30_000,
      env: { ...process.env, ...extraEnv },
    });
    let stdout = '';
    let stderr = '';
    child.stdout.on('data', (d: Buffer) => { stdout += d.toString(); });
    child.stderr.on('data', (d: Buffer) => { stderr += d.toString(); });
    child.on('error', () => resolve({ code: -1, stdout, stderr }));
    child.on('close', (code) => resolve({ code: code ?? -1, stdout, stderr }));
  });
}

export async function ensureWorkspaceGitRepo(workspacePath: string): Promise<void> {
  if (existsSync(join(workspacePath, '.git'))) return;
  mkdirSync(workspacePath, { recursive: true });
  const init = await runGit(workspacePath, ['init', '--initial-branch=main']);
  if (init.code !== 0) {
    throw new Error(`git init failed: ${init.stderr.slice(0, 200)}`);
  }
}

export async function commitWorkspaceChanges(options: CommitWorkspaceOptions): Promise<CommitWorkspaceResult> {
  const ignoreEntries = options.ignoreEntries ?? ['input/', 'logs/'];
  await ensureWorkspaceGitRepo(options.workspacePath);

  const status = await runGit(options.workspacePath, ['status', '--porcelain']);
  if (status.stdout.trim() === '') {
    return { changed: false, committed: false, pushed: false };
  }

  const checkout = await runGit(options.workspacePath, ['checkout', '-b', options.branchName]);
  if (checkout.code !== 0) {
    await runGit(options.workspacePath, ['checkout', options.branchName]);
  }

  const excludePath = join(options.workspacePath, '.git', 'info', 'exclude');
  let excludeContent = '';
  try {
    excludeContent = readFileSync(excludePath, 'utf-8');
  } catch {
    // ignore
  }
  const missingEntries = ignoreEntries.filter(entry => !excludeContent.includes(entry));
  if (missingEntries.length > 0) {
    const nextContent = excludeContent.trimEnd() + '\n' + missingEntries.join('\n') + '\n';
    writeFileSync(excludePath, nextContent);
  }

  await runGit(options.workspacePath, ['add', '-A']);
  const commit = await runGit(options.workspacePath, [
    '-c', 'user.name=Agent Bot',
    '-c', 'user.email=maestro@noreply',
    'commit', '-m', options.commitMessage,
  ]);
  if (commit.code !== 0) {
    return { changed: true, committed: false, pushed: false };
  }

  return { changed: true, committed: true, pushed: false };
}
