/**
 * Shared Utilities for Issue Tracker Integration
 *
 * @module cli/helpers/issue-tracker/utils
 */
import * as fs from 'fs';
import * as path from 'path';
import chalk from 'chalk';
import { execFileNoThrowSync } from '../../../utils/execFileNoThrow.js';
import { isClaudeCliAvailable as isClaudeCliAvailableDetector } from '../../../utils/claude-cli-detector.js';
/**
 * Detect default tracker based on project structure
 *
 * @param projectPath - Path to project root (ONLY checks this directory, not parents)
 * @returns Object with tracker and whether it was actually detected
 */
export function detectDefaultTracker(projectPath) {
    // CRITICAL: Only check project directory, NOT parent directories
    const gitConfigPath = path.join(projectPath, '.git', 'config');
    if (fs.existsSync(gitConfigPath)) {
        try {
            const config = fs.readFileSync(gitConfigPath, 'utf-8');
            // Actually detected from .git/config
            if (config.includes('github.com'))
                return { tracker: 'github', detected: true };
            if (config.includes('dev.azure.com') || config.includes('visualstudio.com'))
                return { tracker: 'ado', detected: true };
            // Bitbucket not supported yet
            if (config.includes('bitbucket.org'))
                return { tracker: 'github', detected: false }; // Fallback, not truly detected
        }
        catch (error) {
            // Ignore errors, use default
        }
    }
    // No .git/config found in project directory - return default (NOT detected)
    return { tracker: 'github', detected: false };
}
/**
 * Check if gh CLI is available
 *
 * @returns True if gh CLI is installed and in PATH
 */
export function isGhCliAvailable() {
    return execFileNoThrowSync('gh', ['--version']).success;
}
/**
 * Check if Claude CLI is available
 *
 * Uses the robust detector from claude-cli-detector.ts
 * This ensures consistent detection logic across the entire codebase.
 *
 * @returns True if claude CLI is installed and in PATH
 */
export function isClaudeCliAvailable() {
    return isClaudeCliAvailableDetector();
}
/**
 * Check if a Claude plugin is already installed
 *
 * @param pluginName - Name of the plugin to check
 * @returns True if installed, false otherwise
 */
export function isPluginInstalled(pluginName) {
    const homeDir = process.env.HOME || process.env.USERPROFILE || '';
    const pluginsFilePath = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
    if (!fs.existsSync(pluginsFilePath)) {
        return false;
    }
    try {
        const installedPlugins = JSON.parse(fs.readFileSync(pluginsFilePath, 'utf-8'));
        const plugins = installedPlugins?.plugins;
        if (!plugins) {
            return false;
        }
        // Check if plugin exists (stored as "pluginName@marketplaceName" keys)
        return Object.keys(plugins).some(key => key.split('@')[0] === pluginName);
    }
    catch {
        return false;
    }
}
/**
 * Install tracker plugin via Claude CLI
 *
 * @param tracker - Tracker type
 * @returns Object with success status and optional error message
 */
export function installTrackerPlugin(tracker) {
    if (tracker === 'none')
        return { success: true };
    const pluginName = `specweave-${tracker}`;
    // Check if already installed first
    if (isPluginInstalled(pluginName)) {
        return {
            success: true,
            alreadyInstalled: true
        };
    }
    try {
        const result = execFileNoThrowSync('claude', [
            'plugin',
            'install',
            pluginName // NO @marketplace suffix - Claude Code resolves from registered marketplaces
        ]);
        if (result.success) {
            return { success: true };
        }
        else {
            // Check if error is because plugin is already installed
            const errorOutput = (result.stderr || result.stdout || '').toLowerCase();
            if (errorOutput.includes('already installed')) {
                return {
                    success: true,
                    alreadyInstalled: true
                };
            }
            // Return detailed error for debugging
            const error = result.stderr || result.stdout || 'Unknown error';
            return { success: false, error };
        }
    }
    catch (error) {
        return { success: false, error: error.message || 'Failed to execute claude command' };
    }
}
/**
 * Sleep for specified milliseconds
 *
 * @param ms - Milliseconds to sleep
 */
export function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
/** Base delay for exponential backoff (1 second) */
const BACKOFF_BASE_MS = 1000;
/**
 * Exponential backoff for rate limiting (1s, 2s, 4s, 8s, 16s...)
 *
 * @param attempt - Current attempt number (0-indexed)
 * @returns Milliseconds to wait
 */
export function getBackoffDelay(attempt) {
    return BACKOFF_BASE_MS * Math.pow(2, attempt);
}
/**
 * Retry an async operation with exponential backoff
 *
 * @param fn - Function to retry
 * @param maxRetries - Maximum number of retries
 * @returns Result of function
 */
export async function retryWithBackoff(fn, maxRetries = 3) {
    let lastError = null;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
            return await fn();
        }
        catch (error) {
            lastError = error;
            // Check if it's a rate limit error
            if (error.name === 'RateLimitError') {
                const rateLimitError = error;
                const resetTime = rateLimitError.rateLimitInfo.reset.getTime();
                const now = Date.now();
                const waitMs = Math.max(0, resetTime - now);
                console.log(chalk.yellow(`\n⏳ Rate limit exceeded. Waiting ${Math.ceil(waitMs / 1000)}s...`));
                await sleep(waitMs + 1000); // Add 1s buffer
                continue;
            }
            // For other errors, use exponential backoff
            if (attempt < maxRetries) {
                const delay = getBackoffDelay(attempt);
                console.log(chalk.yellow(`\n⏳ Retry ${attempt + 1}/${maxRetries} after ${delay}ms...`));
                await sleep(delay);
            }
        }
    }
    throw lastError || new Error('Operation failed after retries');
}
/**
 * Parse rate limit headers from fetch response
 *
 * @param headers - Response headers
 * @returns Rate limit info or null
 */
export function parseRateLimitHeaders(headers) {
    const limit = headers.get('x-ratelimit-limit');
    const remaining = headers.get('x-ratelimit-remaining');
    const reset = headers.get('x-ratelimit-reset');
    if (!limit || !remaining || !reset) {
        return null;
    }
    return {
        limit: parseInt(limit, 10),
        remaining: parseInt(remaining, 10),
        reset: new Date(parseInt(reset, 10) * 1000)
    };
}
/**
 * Check if response is rate limited
 *
 * @param response - Fetch response
 * @returns Rate limit info if rate limited, null otherwise
 */
export function checkRateLimit(response) {
    if (response.status === 429 || response.status === 403) {
        return parseRateLimitHeaders(response.headers);
    }
    return null;
}
/**
 * Validate URL format
 *
 * @param url - URL to validate
 * @returns True if valid URL
 */
export function isValidUrl(url) {
    try {
        new URL(url);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Validate email format
 *
 * @param email - Email to validate
 * @returns True if valid email
 */
export function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
}
/**
 * Mask sensitive string (show first 4 and last 4 characters)
 *
 * @param value - String to mask
 * @returns Masked string
 */
export function maskSensitiveValue(value) {
    if (value.length <= 8) {
        return '*'.repeat(value.length);
    }
    const first4 = value.substring(0, 4);
    const last4 = value.substring(value.length - 4);
    const masked = '*'.repeat(value.length - 8);
    return `${first4}${masked}${last4}`;
}
/**
 * Get plugin name for tracker
 *
 * @param tracker - Tracker type
 * @returns Plugin name
 */
export function getPluginName(tracker) {
    if (tracker === 'none')
        return '';
    return `specweave-${tracker}`;
}
/**
 * Get tracker display name
 *
 * @param tracker - Tracker type
 * @returns Display name
 */
export function getTrackerDisplayName(tracker) {
    switch (tracker) {
        case 'github':
            return 'GitHub Issues';
        case 'jira':
            return 'Jira';
        case 'ado':
            return 'Azure DevOps';
        case 'none':
            return 'None';
    }
}
//# sourceMappingURL=utils.js.map