import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { execFileSync } from 'node:child_process';

export interface LocalRepo {
  path: string;
  cleanup: () => void;
}

export interface TestRepo {
  path: string;
  repoName: string;
  branch: string;
  cleanup: () => void;
}

/**
 * Create a local git repository in a temporary directory.
 * Use this for tests that don't need a remote (GitHub) repository.
 */
export function createLocalRepo(): LocalRepo {
  const repoPath = mkdtempSync(join(tmpdir(), 'takt-e2e-'));
  execFileSync('git', ['init'], { cwd: repoPath, stdio: 'pipe' });
  execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd: repoPath, stdio: 'pipe' });
  execFileSync('git', ['config', 'user.name', 'Test'], { cwd: repoPath, stdio: 'pipe' });
  writeFileSync(join(repoPath, 'README.md'), '# test\n');
  execFileSync('git', ['add', '.'], { cwd: repoPath, stdio: 'pipe' });
  execFileSync('git', ['commit', '-m', 'init'], { cwd: repoPath, stdio: 'pipe' });
  return {
    path: repoPath,
    cleanup: () => {
      rmSync(repoPath, { recursive: true, force: true });
    },
  };
}

export interface CreateTestRepoOptions {
  /** Skip creating a test branch (stay on default branch). Use for pipeline tests. */
  skipBranch?: boolean;
}

function getGitHubUser(): string {
  const user = execFileSync('gh', ['api', 'user', '--jq', '.login'], {
    encoding: 'utf-8',
  }).trim();

  if (!user) {
    throw new Error(
      'Failed to get GitHub user. Make sure `gh` CLI is authenticated.',
    );
  }

  return user;
}

/**
 * Clone the takt-testing repository and create a test branch.
 *
 * Cleanup order (important):
 *   1. Delete remote branch (requires local directory to exist)
 *   2. Close any PRs created during the test
 *   3. Delete local directory
 */
export function createTestRepo(options?: CreateTestRepoOptions): TestRepo {
  const user = getGitHubUser();
  const repoName = `${user}/takt-testing`;

  // Verify repository exists
  try {
    execFileSync('gh', ['repo', 'view', repoName], {
      encoding: 'utf-8',
      stdio: 'pipe',
    });
  } catch {
    throw new Error(
      `Repository "${repoName}" not found. Please create it first:\n` +
        `  gh repo create takt-testing --private --add-readme`,
    );
  }

  // Clone to temporary directory
  const repoPath = mkdtempSync(join(tmpdir(), 'takt-e2e-repo-'));
  execFileSync('gh', ['repo', 'clone', repoName, repoPath], {
    stdio: 'pipe',
  });

  // Create test branch (unless skipped for pipeline tests)
  const testBranch = options?.skipBranch
    ? undefined
    : `e2e-test-${Date.now()}`;
  if (testBranch) {
    execFileSync('git', ['checkout', '-b', testBranch], {
      cwd: repoPath,
      stdio: 'pipe',
    });
  }

  const currentBranch = testBranch
    ?? execFileSync('git', ['branch', '--show-current'], {
      cwd: repoPath,
      encoding: 'utf-8',
    }).trim();

  return {
    path: repoPath,
    repoName,
    branch: currentBranch,
    cleanup: () => {
      if (testBranch) {
        // 1. Delete remote branch (best-effort)
        try {
          execFileSync(
            'git',
            ['push', 'origin', '--delete', testBranch],
            { cwd: repoPath, stdio: 'pipe' },
          );
        } catch {
          // Branch may not have been pushed; ignore
        }

        // 2. Close any PRs from this branch (best-effort)
        try {
          const prList = execFileSync(
            'gh',
            ['pr', 'list', '--head', testBranch, '--repo', repoName, '--json', 'number', '--jq', '.[].number'],
            { encoding: 'utf-8', stdio: 'pipe' },
          ).trim();

          for (const prNumber of prList.split('\n').filter(Boolean)) {
            execFileSync(
              'gh',
              ['pr', 'close', prNumber, '--repo', repoName, '--delete-branch'],
              { stdio: 'pipe' },
            );
          }
        } catch {
          // No PRs or already closed; ignore
        }
      } else {
        // Pipeline mode: clean up takt-created PRs (best-effort)
        try {
          const prNumbers = execFileSync(
            'gh',
            ['pr', 'list', '--state', 'open', '--repo', repoName, '--json', 'number', '--jq', '.[].number'],
            { encoding: 'utf-8', stdio: 'pipe' },
          ).trim();

          for (const prNumber of prNumbers.split('\n').filter(Boolean)) {
            execFileSync(
              'gh',
              ['pr', 'close', prNumber, '--repo', repoName, '--delete-branch'],
              { stdio: 'pipe' },
            );
          }
        } catch {
          // ignore
        }
      }

      // Delete local directory last
      try {
        rmSync(repoPath, { recursive: true, force: true });
      } catch {
        // Best-effort cleanup
      }
    },
  };
}
