/**
 * GitFlow Provider — GitHub / Azure DevOps PR abstraction.
 * Self-contained: only uses node built-ins.
 */

import { execFile } from 'child_process';
import type { GitProvider, AzureDevOpsInfo, PRInfo, PROptions } from './types.js';

export function detectProvider(remoteUrl: string): GitProvider {
  if (!remoteUrl) return 'unknown';
  if (remoteUrl.includes('dev.azure.com') || remoteUrl.includes('visualstudio.com')) return 'azuredevops';
  if (remoteUrl.includes('github.com')) return 'github';
  return 'unknown';
}

export function parseAzureDevOpsUrl(url: string): AzureDevOpsInfo | null {
  const httpsMatch = url.match(/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)/);
  if (httpsMatch) return { org: httpsMatch[1], project: httpsMatch[2], repo: httpsMatch[3] };

  const sshMatch = url.match(/ssh\.dev\.azure\.com:v3\/([^/]+)\/([^/]+)\/([^/]+)/);
  if (sshMatch) return { org: sshMatch[1], project: sshMatch[2], repo: sshMatch[3] };

  return null;
}

const IS_WIN = process.platform === 'win32';

// On Windows, `az`/`gh` are `.cmd` shims that execFile cannot launch without a
// shell. Quote each argument so cmd.exe receives it intact (spaces, parens, …).
function quoteWinArg(arg: string): string {
  if (arg === '') return '""';
  return /[\s"&|<>()^%!,;=]/.test(arg) ? '"' + arg.replace(/"/g, '""') + '"' : arg;
}

async function isToolAvailable(tool: string): Promise<boolean> {
  return new Promise((resolve) => {
    if (IS_WIN) {
      execFile(`${tool} --version`, [], { encoding: 'utf-8', timeout: 5000, shell: true }, (error) => resolve(!error));
    } else {
      execFile(tool, ['--version'], { encoding: 'utf-8', timeout: 5000 }, (error) => resolve(!error));
    }
  });
}

async function execTool(command: string, args: string[], cwd?: string): Promise<{ stdout: string; exitCode: number }> {
  return new Promise((resolve) => {
    const opts = { cwd, encoding: 'utf-8' as const, timeout: 60_000, maxBuffer: 10 * 1024 * 1024 };
    if (IS_WIN) {
      const cmdline = [command, ...args.map(quoteWinArg)].join(' ');
      execFile(cmdline, [], { ...opts, shell: true }, (error, stdout) => {
        resolve({ stdout: (stdout || '').trim(), exitCode: error ? 1 : 0 });
      });
    } else {
      execFile(command, args, opts, (error, stdout) => {
        resolve({ stdout: (stdout || '').trim(), exitCode: error ? 1 : 0 });
      });
    }
  });
}

export async function createPR(opts: PROptions, cwd?: string): Promise<{ success: boolean; pr?: PRInfo; error?: string }> {
  if (opts.provider === 'github') {
    if (!(await isToolAvailable('gh'))) {
      return { success: false, error: 'GitHub CLI (gh) not found. Install: https://cli.github.com' };
    }
    const args = ['pr', 'create', '--title', opts.title, '--body', opts.body, '--base', opts.target, '--head', opts.source];
    if (opts.draft) args.push('--draft');

    const result = await execTool('gh', args, cwd);
    if (result.exitCode !== 0) return { success: false, error: result.stdout };

    const url = result.stdout;
    const numberMatch = url.match(/\/pull\/(\d+)/);
    return {
      success: true,
      pr: { number: numberMatch ? parseInt(numberMatch[1], 10) : 0, url, title: opts.title, state: 'open', source: opts.source, target: opts.target },
    };
  }

  if (opts.provider === 'azuredevops') {
    if (!(await isToolAvailable('az'))) {
      return { success: false, error: 'Azure CLI (az) not found. Install: https://aka.ms/installazurecli' };
    }
    const args = ['repos', 'pr', 'create', '--title', opts.title, '--description', opts.body, '--source-branch', opts.source, '--target-branch', opts.target, '--output', 'json'];
    if (opts.draft) args.push('--draft', 'true');

    const result = await execTool('az', args, cwd);
    if (result.exitCode !== 0) return { success: false, error: result.stdout };

    try {
      const pr = JSON.parse(result.stdout);
      return {
        success: true,
        pr: { number: pr.pullRequestId, url: pr.url || '', title: opts.title, state: pr.status || 'active', source: opts.source, target: opts.target },
      };
    } catch {
      return { success: false, error: 'Failed to parse Azure DevOps PR response' };
    }
  }

  return { success: false, error: `Unsupported provider: ${opts.provider}` };
}

export async function findPR(branch: string, provider: GitProvider, cwd?: string): Promise<PRInfo | null> {
  if (provider === 'github') {
    const result = await execTool('gh', ['pr', 'view', branch, '--json', 'number,url,title,state,headRefName,baseRefName'], cwd);
    if (result.exitCode !== 0) return null;
    try {
      const pr = JSON.parse(result.stdout);
      return { number: pr.number, url: pr.url, title: pr.title, state: pr.state, source: pr.headRefName, target: pr.baseRefName };
    } catch { return null; }
  }

  if (provider === 'azuredevops') {
    const result = await execTool('az', ['repos', 'pr', 'list', '--source-branch', branch, '--output', 'json', '--top', '1'], cwd);
    if (result.exitCode !== 0) return null;
    try {
      const prs = JSON.parse(result.stdout);
      if (!Array.isArray(prs) || prs.length === 0) return null;
      const pr = prs[0];
      return { number: pr.pullRequestId, url: pr.url || '', title: pr.title, state: pr.status, source: pr.sourceRefName?.replace('refs/heads/', '') || branch, target: pr.targetRefName?.replace('refs/heads/', '') || '' };
    } catch { return null; }
  }

  return null;
}

export async function mergePR(
  prNumber: number,
  provider: GitProvider,
  strategy: 'squash' | 'merge',
  cwd?: string,
): Promise<{ success: boolean; error?: string }> {
  if (provider === 'github') {
    const strategyFlag = strategy === 'squash' ? '--squash' : '--merge';
    const result = await execTool('gh', ['pr', 'merge', String(prNumber), strategyFlag, '--delete-branch'], cwd);
    return { success: result.exitCode === 0, error: result.exitCode !== 0 ? result.stdout : undefined };
  }

  if (provider === 'azuredevops') {
    const squash = strategy === 'squash' ? 'true' : 'false';
    const result = await execTool('az', ['repos', 'pr', 'update', '--id', String(prNumber), '--status', 'completed', '--squash', squash, '--delete-source-branch', 'true', '--output', 'json'], cwd);
    return { success: result.exitCode === 0, error: result.exitCode !== 0 ? result.stdout : undefined };
  }

  return { success: false, error: `Unsupported provider: ${provider}` };
}
