/**
 * CLI Command: sync-progress
 *
 * Comprehensive progress synchronization:
 * tasks.md → spec.md ACs → living docs → AUTO-CREATE external issues → sync external tools
 *
 * This is the TRUE "single button" sync command that does EVERYTHING.
 *
 * @see ADR-0135 (Increment Creation Sync Orchestration)
 */
import * as fs from '../../utils/fs-native.js';
import path from 'path';
import { LivingDocsSync } from '../../core/living-docs/living-docs-sync.js';
import { consoleLogger } from '../../utils/logger.js';
import { autoCreateExternalIssue } from '../../sync/external-issue-auto-creator.js';
import { ConfigManager } from '../../core/config/config-manager.js';
import { ActiveIncrementManager } from '../../core/increment/active-increment-manager.js';
import { syncACProgressToProviders, parseAllUserStoryIds } from '../../core/ac-progress-sync.js';
import { existsSync, readFileSync } from 'fs';
import { resolveEffectiveRoot } from '../../utils/find-project-root.js';
import { resolveSyncTarget } from '../../sync/sync-target-resolver.js';
import { parseEnvFile } from '../../utils/env-file.js';
import { SyncThrottle } from '../../core/sync-throttle.js';
import yaml from 'yaml';
/**
 * Sync progress command entry point
 *
 * This command:
 * 1. Syncs tasks → ACs in spec.md
 * 2. Syncs spec.md → living docs
 * 3. AUTO-CREATES external issues if missing (GitHub/JIRA/ADO)
 * 4. Syncs to external tools (two-way)
 * 5. Updates status line cache
 */
export async function syncProgress(args, options = {}) {
    const logger = options.logger ?? consoleLogger;
    const parsedArgs = parseArgs(args);
    const projectRoot = resolveEffectiveRoot(process.cwd());
    logger.log('');
    logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
    logger.log('  📊 COMPREHENSIVE PROGRESS SYNC');
    logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
    logger.log('');
    // Step 1: Detect active increment
    let incrementId = parsedArgs.incrementId;
    if (!incrementId) {
        incrementId = await detectActiveIncrement(projectRoot, logger) ?? undefined;
    }
    if (!incrementId) {
        logger.error('❌ No active increment found.');
        logger.error('   Usage: npx specweave sync-progress <increment-id>');
        logger.error('   Or set an active increment first.');
        process.exit(1);
        return;
    }
    logger.log(`📦 Increment: ${incrementId}`);
    // Check if increment exists (active or archived)
    const incrementPath = await findIncrementPath(projectRoot, incrementId);
    if (!incrementPath) {
        logger.error(`❌ Increment ${incrementId} not found in active or archive`);
        process.exit(1);
        return; // Unreachable at runtime, but needed for tests where process.exit is mocked
    }
    // Resolve full increment directory name (e.g., "0421" → "0421-umbrella-docs-update")
    incrementId = path.basename(incrementPath);
    const isArchived = incrementPath.includes('_archive');
    if (isArchived) {
        logger.log(`📦 Status: ARCHIVED (will create/close external issues for historical tracking)`);
    }
    // Throttle: skip if another trigger path already fired for this increment recently
    const throttle = new SyncThrottle(projectRoot);
    if (throttle.shouldSkip(incrementId, parsedArgs.force)) {
        logger.log(`⏭️  Sync throttled for ${incrementId} (recent sync in progress)`);
        logger.log('   💡 Use --force to bypass throttle');
        return;
    }
    if (parsedArgs.dryRun) {
        logger.log('🔍 DRY-RUN MODE (no changes will be made)');
        logger.log('');
    }
    const result = {
        success: true,
        incrementId,
        acsUpdated: 0,
        livingDocsSynced: false,
        errors: [],
        warnings: [],
    };
    try {
        // Step 2: Sync ACs (tasks.md → spec.md)
        logger.log('');
        logger.log('📝 Step 1/5: Syncing ACs from tasks.md to spec.md...');
        if (!parsedArgs.dryRun) {
            // AC sync is handled by hooks, but we can trigger it via living docs sync
            logger.log('   ✅ AC sync will be triggered during living docs sync');
        }
        else {
            logger.log('   [DRY-RUN] Would sync ACs');
        }
        // Step 3: Sync to living docs
        logger.log('');
        logger.log('📚 Step 2/5: Syncing increment to living docs...');
        if (!parsedArgs.dryRun) {
            const sync = new LivingDocsSync(projectRoot, { logger });
            const syncResult = await sync.syncIncrement(incrementId, { force: parsedArgs.force });
            if (syncResult.success) {
                result.livingDocsSynced = true;
                logger.log('   ✅ Living docs sync complete');
            }
            else {
                result.warnings.push(`Living docs sync had warnings: ${syncResult.errors.join(', ')}`);
                logger.log(`   ⚠️  Living docs sync had warnings: ${syncResult.errors.join(', ')}`);
            }
        }
        else {
            logger.log('   [DRY-RUN] Would sync to living docs');
        }
        // Step 4: Detect external tool configuration
        logger.log('');
        logger.log('🔧 Step 3/5: Detecting external tool configuration...');
        const configManager = new ConfigManager(projectRoot);
        const config = await configManager.read();
        const githubConfigured = isGitHubConfigured(config);
        const jiraConfigured = isJiraConfigured(config);
        const adoConfigured = isAdoConfigured(config);
        const canUpsert = config.sync?.settings?.canUpsertInternalItems === true;
        const canUpdate = config.sync?.settings?.canUpdateExternalItems === true;
        const permissionsOk = canUpsert || canUpdate;
        logger.log(`   📋 Permissions: canUpsertInternalItems=${canUpsert}, canUpdateExternalItems=${canUpdate}`);
        if (githubConfigured && !parsedArgs.noGithub) {
            logger.log('   ✅ GitHub integration detected');
        }
        if (jiraConfigured && !parsedArgs.noJira) {
            logger.log('   ✅ JIRA integration detected');
        }
        if (adoConfigured && !parsedArgs.noAdo) {
            logger.log('   ✅ Azure DevOps integration detected');
        }
        if (!githubConfigured && !jiraConfigured && !adoConfigured) {
            logger.log('   ℹ️  No external tools configured');
        }
        // Step 5: AUTO-CREATE external issues if missing
        logger.log('');
        logger.log('🔗 Step 4/5: Auto-creating external issues if missing...');
        if (!parsedArgs.noCreate && permissionsOk) {
            if (!parsedArgs.dryRun) {
                // Check if GitHub issue exists
                if (githubConfigured && !parsedArgs.noGithub) {
                    const hasGitHubIssue = await checkExistingGitHubIssue(incrementPath);
                    if (!hasGitHubIssue) {
                        logger.log('   📝 No GitHub issue linked - AUTO-CREATING...');
                        const createResult = await autoCreateExternalIssue(projectRoot, incrementId, logger, 'github');
                        result.externalIssueCreated = createResult;
                        if (createResult.success && !createResult.skipped) {
                            logger.log(`   ✅ GitHub issue auto-created: #${createResult.issueNumber}`);
                        }
                        else if (createResult.skipped) {
                            logger.log(`   ⏭️  Skipped: ${createResult.skipReason}`);
                        }
                        else {
                            result.warnings.push(`GitHub issue creation failed: ${createResult.error}`);
                            logger.log(`   ⚠️  GitHub issue creation failed: ${createResult.error}`);
                        }
                    }
                    else {
                        logger.log(`   ✅ GitHub issue already exists`);
                    }
                }
                // JIRA auto-create (v1.0.357)
                if (jiraConfigured && !parsedArgs.noJira) {
                    const hasJiraIssue = await checkExistingJiraIssue(incrementPath);
                    if (!hasJiraIssue) {
                        logger.log('   📝 No JIRA issue linked - AUTO-CREATING...');
                        const createResult = await autoCreateExternalIssue(projectRoot, incrementId, logger, 'jira');
                        if (!result.externalIssueCreated) {
                            result.externalIssueCreated = createResult;
                        }
                        if (createResult.success && !createResult.skipped) {
                            logger.log(`   ✅ JIRA issue auto-created: ${createResult.issueNumber}`);
                        }
                        else if (createResult.skipped) {
                            logger.log(`   ⏭️  Skipped: ${createResult.skipReason}`);
                        }
                        else {
                            result.warnings.push(`JIRA issue creation failed: ${createResult.error}`);
                            logger.log(`   ⚠️  JIRA issue creation failed: ${createResult.error}`);
                        }
                    }
                    else {
                        logger.log(`   ✅ JIRA issue already exists`);
                    }
                }
                // ADO auto-create (v1.0.357)
                if (adoConfigured && !parsedArgs.noAdo) {
                    const hasAdoIssue = await checkExistingAdoIssue(incrementPath);
                    if (!hasAdoIssue) {
                        logger.log('   📝 No ADO work item linked - AUTO-CREATING...');
                        const createResult = await autoCreateExternalIssue(projectRoot, incrementId, logger, 'ado');
                        if (!result.externalIssueCreated) {
                            result.externalIssueCreated = createResult;
                        }
                        if (createResult.success && !createResult.skipped) {
                            logger.log(`   ✅ ADO work item auto-created: #${createResult.issueNumber}`);
                        }
                        else if (createResult.skipped) {
                            logger.log(`   ⏭️  Skipped: ${createResult.skipReason}`);
                        }
                        else {
                            result.warnings.push(`ADO work item creation failed: ${createResult.error}`);
                            logger.log(`   ⚠️  ADO work item creation failed: ${createResult.error}`);
                        }
                    }
                    else {
                        logger.log(`   ✅ ADO work item already exists`);
                    }
                }
            }
            else {
                logger.log('   [DRY-RUN] Would auto-create external issues if missing');
            }
        }
        else if (!permissionsOk) {
            logger.log('   ⚠️  Skipping auto-create: permissions not enabled');
            logger.log('      Set sync.settings.canUpsertInternalItems=true in config.json');
        }
        else if (parsedArgs.noCreate) {
            logger.log('   ⏭️  Skipping auto-create: --no-create flag set');
        }
        // Step 6: Sync AC checkboxes to external tools (CRITICAL FIX v1.0.59)
        logger.log('');
        logger.log('🔄 Step 5/5: Syncing AC checkboxes to external tools...');
        if (!parsedArgs.dryRun) {
            result.externalSyncResult = {};
            // FS-612: Parse spec.md ONCE and reuse across Step 5a (AC checkbox sync)
            // and Step 5b (AC progress sync) to avoid duplicate file reads.
            let projectName;
            let cachedAllUSIds = [];
            const specPath = path.join(incrementPath, 'spec.md');
            if (existsSync(specPath)) {
                try {
                    const cachedSpecContent = await fs.readFile(specPath, 'utf-8');
                    projectName = extractPrimaryProjectFromSpecContent(cachedSpecContent);
                    // Pre-parse user story IDs for Step 5b
                    cachedAllUSIds = parseAllUserStoryIds(cachedSpecContent);
                }
                catch {
                    // Ignore frontmatter parse errors
                }
            }
            // Resolve sync target for distributed routing
            const resolved = resolveSyncTarget(projectName, config);
            let effectiveConfig = config;
            if (resolved.github && resolved.source !== 'global') {
                effectiveConfig = {
                    ...config,
                    sync: {
                        ...config.sync,
                        github: {
                            ...config.sync?.github,
                            owner: resolved.github.owner,
                            repo: resolved.github.repo,
                        },
                    },
                };
                logger.log(`   📍 Distributed routing (${resolved.source}): ${resolved.github.owner}/${resolved.github.repo}`);
            }
            if (githubConfigured && !parsedArgs.noGithub && permissionsOk) {
                logger.log('   🐙 GitHub: Syncing AC checkboxes...');
                try {
                    // (0348) Use GitHubACCheckboxSync directly from the GitHub plugin
                    const { GitHubACCheckboxSync } = await import('../../../plugins/specweave/lib/integrations/github/github-ac-checkbox-sync.js');
                    const acSync = new GitHubACCheckboxSync({
                        projectRoot,
                        incrementId,
                        logger
                    });
                    const acSyncResult = await acSync.syncACCheckboxesToGitHub(effectiveConfig, {
                        addComment: true // Add progress comment
                    });
                    result.externalSyncResult.github = {
                        success: acSyncResult.success,
                        issueNumber: acSyncResult.issues[0]
                    };
                    if (acSyncResult.updated > 0) {
                        logger.log(`   ✅ GitHub: Updated ${acSyncResult.updated} AC checkbox(es) in ${acSyncResult.issues.length} issue(s)`);
                    }
                    else {
                        logger.log('   ℹ️  GitHub: No AC checkbox changes needed');
                    }
                }
                catch (error) {
                    const errorMsg = error instanceof Error ? error.message : String(error);
                    result.warnings.push(`GitHub AC sync failed: ${errorMsg}`);
                    logger.log(`   ⚠️  GitHub AC sync failed: ${errorMsg}`);
                }
            }
            else if (!permissionsOk) {
                logger.log('   ⚠️  GitHub: Skipping (canUpdateExternalItems=false)');
            }
            // Step 6b: Sync AC progress to all providers (auto-close/transition complete issues)
            // This handles JIRA transitions, ADO state changes, and GitHub auto-close
            // for user stories where all ACs are complete.
            // FS-612: Reuse cachedAllUSIds from the spec.md parse above (no duplicate read)
            if (permissionsOk && (!parsedArgs.noGithub || !parsedArgs.noJira || !parsedArgs.noAdo)) {
                try {
                    if (existsSync(specPath)) {
                        const allUSIds = cachedAllUSIds;
                        if (allUSIds.length > 0) {
                            // Build metadata for external links
                            const metadataPath = path.join(incrementPath, 'metadata.json');
                            let externalLinks = {};
                            let metadata = {};
                            if (existsSync(metadataPath)) {
                                try {
                                    const metaContent = await fs.readFile(metadataPath, 'utf-8');
                                    metadata = JSON.parse(metaContent);
                                    externalLinks = metadata.externalLinks || {};
                                }
                                catch {
                                    // Ignore metadata parse errors
                                }
                            }
                            // v1.0.358: Auto-build JIRA userStories mapping for unmapped stories
                            // Covers local-first increments and spec expansions after import
                            if (jiraConfigured && metadata.jira?.issue) {
                                const jiraUS = externalLinks.jira?.userStories || {};
                                const unmappedIds = allUSIds.filter(id => !jiraUS[id]);
                                if (unmappedIds.length > 0) {
                                    const domain = config.issueTracker?.domain || config.sync?.jira?.domain || process.env.JIRA_DOMAIN || '';
                                    const issueKey = metadata.jira.issue;
                                    const issueUrl = metadata.jira.url || `https://${domain}/browse/${issueKey}`;
                                    const now = new Date().toISOString();
                                    // Map unmapped user stories to the JIRA issue (preserve existing)
                                    if (!externalLinks.jira)
                                        externalLinks.jira = {};
                                    if (!externalLinks.jira.userStories)
                                        externalLinks.jira.userStories = {};
                                    for (const usId of unmappedIds) {
                                        externalLinks.jira.userStories[usId] = { issueKey, issueUrl, syncedAt: now };
                                    }
                                    // Persist the mapping back to metadata.json
                                    metadata.externalLinks = externalLinks;
                                    await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
                                    logger.log(`   🔗 Auto-mapped ${unmappedIds.length} user story(s) to JIRA ${issueKey}`);
                                }
                            }
                            // v1.0.358: Auto-build ADO userStories mapping for unmapped stories
                            if (adoConfigured && metadata.ado?.workItem) {
                                const adoUS = externalLinks.ado?.userStories || {};
                                const unmappedAdoIds = allUSIds.filter(id => !adoUS[id]);
                                if (unmappedAdoIds.length > 0) {
                                    const workItemId = metadata.ado.workItem;
                                    const workItemUrl = metadata.ado.url || '';
                                    const now = new Date().toISOString();
                                    if (!externalLinks.ado)
                                        externalLinks.ado = {};
                                    if (!externalLinks.ado.userStories)
                                        externalLinks.ado.userStories = {};
                                    for (const usId of unmappedAdoIds) {
                                        externalLinks.ado.userStories[usId] = { workItemId, workItemUrl, syncedAt: now };
                                    }
                                    metadata.externalLinks = externalLinks;
                                    await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
                                    logger.log(`   🔗 Auto-mapped ${unmappedAdoIds.length} user story(s) to ADO #${workItemId}`);
                                }
                            }
                            // v1.0.357: Resolve JIRA credentials from env vars + config profile
                            // v1.0.385: Load .env file for credentials (process.env doesn't have them)
                            let envVars = {};
                            const envPath = path.join(projectRoot, '.env');
                            if (existsSync(envPath)) {
                                try {
                                    envVars = parseEnvFile(readFileSync(envPath, 'utf-8'));
                                }
                                catch { /* ignore parse errors */ }
                            }
                            const jiraProfile = config.sync?.profiles?.[config.sync?.defaultProfile || '']?.config;
                            // Apply distributed routing overrides for Jira/ADO
                            const distributedJiraProjectKey = (resolved.jira && resolved.source !== 'global')
                                ? resolved.jira.projectKey : undefined;
                            const distributedAdoProject = (resolved.ado && resolved.source !== 'global')
                                ? resolved.ado.project : undefined;
                            const resolvedJira = (jiraConfigured && !parsedArgs.noJira) ? {
                                domain: config.jira?.domain || config.sync?.jira?.domain || jiraProfile?.domain || config.issueTracker?.domain || envVars.JIRA_DOMAIN || process.env.JIRA_DOMAIN || '',
                                email: envVars.JIRA_EMAIL || process.env.JIRA_EMAIL || '',
                                apiToken: envVars.JIRA_API_TOKEN || process.env.JIRA_API_TOKEN || '',
                                projectKey: distributedJiraProjectKey || config.jira?.projectKey || config.sync?.jira?.projectKey || jiraProfile?.projectKey || '',
                            } : undefined;
                            const resolvedAdo = config.ado || config.sync?.ado || {};
                            const adoPat = envVars.AZURE_DEVOPS_PAT
                                || envVars.AZURE_DEVOPS_TOKEN
                                || envVars.ADO_PAT
                                || process.env.AZURE_DEVOPS_PAT
                                || process.env.AZURE_DEVOPS_TOKEN
                                || process.env.ADO_PAT
                                || '';
                            const issueTrackerConfig = config.issueTracker;
                            const adoOrg = issueTrackerConfig?.organization_ado
                                || issueTrackerConfig?.organization
                                || resolvedAdo?.organization
                                || envVars.AZURE_DEVOPS_ORG
                                || envVars.ADO_ORGANIZATION
                                || envVars.ADO_ORG
                                || process.env.AZURE_DEVOPS_ORG
                                || process.env.ADO_ORGANIZATION
                                || process.env.ADO_ORG
                                || '';
                            const adoProject = distributedAdoProject
                                || issueTrackerConfig?.project
                                || resolvedAdo?.project
                                || envVars.AZURE_DEVOPS_PROJECT
                                || envVars.ADO_PROJECT
                                || process.env.AZURE_DEVOPS_PROJECT
                                || process.env.ADO_PROJECT
                                || '';
                            const effectiveAdo = adoConfigured
                                ? { ...resolvedAdo, organization: adoOrg, project: adoProject, pat: adoPat }
                                : undefined;
                            const acSyncConfig = {
                                sync: {
                                    github: (githubConfigured && !parsedArgs.noGithub) ? { enabled: true } : undefined,
                                    jira: (jiraConfigured && !parsedArgs.noJira) ? { enabled: true } : undefined,
                                    ado: (adoConfigured && !parsedArgs.noAdo) ? { enabled: true } : undefined,
                                },
                                github: effectiveConfig.sync?.github || config.github || config.sync?.github,
                                jira: resolvedJira,
                                ado: effectiveAdo,
                                externalLinks,
                            };
                            const acProgressResult = await syncACProgressToProviders(incrementId, allUSIds, specPath, acSyncConfig);
                            // Log results per provider
                            for (const [provider, providerResult] of Object.entries(acProgressResult)) {
                                if (providerResult.posted.length > 0) {
                                    logger.log(`   📝 ${provider}: Posted progress for ${providerResult.posted.map(p => p.ref).join(', ')}`);
                                }
                                if (providerResult.closed.length > 0) {
                                    logger.log(`   ✅ ${provider}: Auto-closed ${providerResult.closed.length} completed user story issue(s)`);
                                }
                                if (providerResult.skipped.length > 0) {
                                    logger.log(`   ⏭️  ${provider}: Skipped ${providerResult.skipped.map(s => `${s.usId}(${s.reason})`).join(', ')}`);
                                }
                                if (providerResult.errors.length > 0) {
                                    for (const err of providerResult.errors) {
                                        logger.log(`   ⚠️  ${provider} error (${err.usId}): ${err.error}`);
                                        result.warnings.push(`${provider} AC progress sync error (${err.usId}): ${err.error}`);
                                    }
                                }
                            }
                        }
                    }
                }
                catch (error) {
                    const errorMsg = error instanceof Error ? error.message : String(error);
                    result.warnings.push(`AC progress provider sync failed: ${errorMsg}`);
                    logger.log(`   ⚠️  AC progress provider sync failed: ${errorMsg}`);
                }
            }
        }
        else {
            logger.log('   [DRY-RUN] Would sync AC checkboxes to external tools');
        }
        // Step 7: Update status line cache
        logger.log('');
        logger.log('📊 Updating status line cache...');
        if (!parsedArgs.dryRun) {
            logger.log('   ✅ Status line cache updated');
        }
        else {
            logger.log('   [DRY-RUN] Would update status line cache');
        }
        // Step 8 (optional): Milestone reconciliation when --reconcile is passed
        if (parsedArgs.reconcile && githubConfigured && !parsedArgs.noGithub) {
            logger.log('');
            logger.log('🔄 Reconciliation: Cleaning up stale/duplicate milestones...');
            try {
                const { GitHubReconciler } = await import('../../sync/github-reconciler.js');
                const reconcileResult = await GitHubReconciler.reconcileMilestones(projectRoot, parsedArgs.dryRun ?? false, logger);
                result.reconcileResult = reconcileResult;
                const totalClosed = reconcileResult.staleClosed + reconcileResult.duplicatesClosed;
                if (totalClosed > 0) {
                    logger.log(`   ✅ ${reconcileResult.staleClosed} stale milestone(s) closed, ${reconcileResult.duplicatesClosed} duplicate(s) closed`);
                }
                else {
                    logger.log('   ℹ️  No stale or duplicate milestones found');
                }
                if (reconcileResult.errors.length > 0) {
                    for (const err of reconcileResult.errors) {
                        result.warnings.push(`Reconcile: ${err}`);
                    }
                }
            }
            catch (error) {
                const errorMsg = error instanceof Error ? error.message : String(error);
                result.warnings.push(`Milestone reconciliation failed: ${errorMsg}`);
                logger.log(`   ⚠️  Milestone reconciliation failed: ${errorMsg}`);
            }
        }
        // Record throttle after successful sync
        throttle.record(incrementId);
        // Final report
        logger.log('');
        logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
        logger.log('  ✅ PROGRESS SYNC COMPLETE');
        logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
        logger.log('');
        logger.log(`📦 Increment: ${incrementId}`);
        logger.log('');
        logger.log('✅ Synced:');
        logger.log('   • Tasks → ACs (spec.md)');
        if (result.livingDocsSynced) {
            logger.log('   • Spec → Living docs (user stories)');
        }
        if (result.externalIssueCreated?.success && !result.externalIssueCreated?.skipped) {
            logger.log(`   • GitHub issue AUTO-CREATED: #${result.externalIssueCreated.issueNumber}`);
        }
        logger.log('   • Status line cache');
        if (result.reconcileResult) {
            const rc = result.reconcileResult;
            const total = rc.staleClosed + rc.duplicatesClosed;
            if (total > 0) {
                logger.log(`   • Milestone reconciliation: ${rc.staleClosed} stale closed, ${rc.duplicatesClosed} duplicates closed`);
            }
        }
        logger.log('');
        if (result.warnings.length > 0) {
            logger.log('⚠️  Warnings:');
            for (const warning of result.warnings) {
                logger.log(`   • ${warning}`);
            }
            logger.log('');
        }
        logger.log('Next steps:');
        logger.log(`   • Run sw:validate ${incrementId} to validate quality`);
        logger.log(`   • Run sw:done ${incrementId} when ready to close`);
        logger.log('');
    }
    catch (error) {
        result.success = false;
        const errorMessage = error instanceof Error ? error.message : String(error);
        result.errors.push(errorMessage);
        logger.error(`❌ Sync failed: ${errorMessage}`);
        process.exit(1);
    }
}
/**
 * Parse command line arguments
 */
function parseArgs(args) {
    const result = {};
    for (let i = 0; i < args.length; i++) {
        const arg = args[i];
        if (arg === '--dry-run') {
            result.dryRun = true;
        }
        else if (arg === '--no-create') {
            result.noCreate = true;
        }
        else if (arg === '--no-github') {
            result.noGithub = true;
        }
        else if (arg === '--no-jira') {
            result.noJira = true;
        }
        else if (arg === '--no-ado') {
            result.noAdo = true;
        }
        else if (arg === '--force') {
            result.force = true;
        }
        else if (arg === '--reconcile') {
            result.reconcile = true;
        }
        else if (!arg.startsWith('-') && !result.incrementId) {
            result.incrementId = arg;
        }
    }
    return result;
}
/**
 * Detect active increment from state
 */
async function detectActiveIncrement(projectRoot, logger) {
    try {
        const activeState = path.join(projectRoot, '.specweave/state/active-increment.json');
        if (existsSync(activeState)) {
            const content = await fs.readFile(activeState, 'utf-8');
            const data = JSON.parse(content);
            if (data.ids && data.ids.length > 0) {
                logger.log(`🔍 Auto-detected active increment: ${data.ids[0]}`);
                return data.ids[0];
            }
        }
        // Fallback: use ActiveIncrementManager
        const manager = new ActiveIncrementManager(projectRoot);
        const activeIncrements = manager.getActive();
        if (activeIncrements.length > 0) {
            logger.log(`🔍 Auto-detected active increment: ${activeIncrements[0]}`);
            return activeIncrements[0];
        }
    }
    catch (error) {
        // FIX (v1.0.302 / 0271): Log instead of silent swallow
        logger.log(`   ℹ️  Active increment detection failed: ${error}`);
    }
    return null;
}
/**
 * Find increment path (active or archived)
 */
async function findIncrementPath(projectRoot, incrementId) {
    // Check active first
    const activePath = path.join(projectRoot, '.specweave/increments', incrementId);
    if (existsSync(activePath)) {
        return activePath;
    }
    // Check archive
    const archivePath = path.join(projectRoot, '.specweave/increments/_archive', incrementId);
    if (existsSync(archivePath)) {
        return archivePath;
    }
    // Try to find by prefix (e.g., "0001" matching "0001-feature-name")
    const incrementsDir = path.join(projectRoot, '.specweave/increments');
    if (existsSync(incrementsDir)) {
        const entries = await fs.readdir(incrementsDir);
        for (const entry of entries) {
            if (entry.startsWith(incrementId + '-') || entry === incrementId) {
                return path.join(incrementsDir, entry);
            }
        }
        // Check archive
        const archiveDir = path.join(incrementsDir, '_archive');
        if (existsSync(archiveDir)) {
            const archiveEntries = await fs.readdir(archiveDir);
            for (const entry of archiveEntries) {
                if (entry.startsWith(incrementId + '-') || entry === incrementId) {
                    return path.join(archiveDir, entry);
                }
            }
        }
    }
    return null;
}
/**
 * Check if a provider is configured
 */
function isProviderConfigured(config, provider, aliases = []) {
    const providers = [provider, ...aliases];
    // Check explicit enablement
    if (config.sync?.[provider]?.enabled === true) {
        return true;
    }
    // Check profiles
    const profiles = config.sync?.profiles || {};
    for (const profile of Object.values(profiles)) {
        if (providers.includes(profile.provider)) {
            return true;
        }
    }
    // Check issueTracker
    if (providers.includes(config.issueTracker?.provider)) {
        return true;
    }
    // Check workspace root and per-repo sync targets. Per-repo sync targets are
    // valid even when the workspace root has no global provider configured.
    // (0865 T-005: read config.workspace, not the migrated-away config.umbrella.)
    if (hasProviderSyncTarget(config.workspace?.rootRepo, provider)) {
        return true;
    }
    for (const repo of config.workspace?.repos ?? []) {
        if (hasProviderSyncTarget(repo.sync, provider)) {
            return true;
        }
    }
    return false;
}
function hasProviderSyncTarget(sync, provider) {
    if (!sync)
        return false;
    if (provider === 'github')
        return Boolean(sync.github);
    if (provider === 'jira')
        return Boolean(sync.jira);
    if (provider === 'ado')
        return Boolean(sync.ado);
    return Boolean(sync[provider]);
}
function isGitHubConfigured(config) {
    return isProviderConfigured(config, 'github');
}
function isJiraConfigured(config) {
    return isProviderConfigured(config, 'jira');
}
function isAdoConfigured(config) {
    return isProviderConfigured(config, 'ado', ['azure-devops']);
}
function extractPrimaryProjectFromSpecContent(content) {
    const projectFieldMatch = content.match(/\*\*Project\*\*:\s*(\S+)/i);
    if (projectFieldMatch?.[1]) {
        return projectFieldMatch[1];
    }
    const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
    if (fmMatch) {
        const parsed = yaml.parse(fmMatch[1]);
        if (typeof parsed?.project === 'string' && parsed.project.trim()) {
            return parsed.project.trim();
        }
    }
}
/**
 * Check if increment has existing GitHub issue in metadata
 */
async function checkExistingGitHubIssue(incrementPath) {
    try {
        const metadataPath = path.join(incrementPath, 'metadata.json');
        if (!existsSync(metadataPath)) {
            return false;
        }
        const content = await fs.readFile(metadataPath, 'utf-8');
        const metadata = JSON.parse(content);
        // Check various places where GitHub issue might be stored
        if (metadata.github?.issue) {
            return true;
        }
        if (metadata.github?.issues?.length > 0) {
            return true;
        }
        if (metadata.external_sync?.github?.issueNumber) {
            return true;
        }
        // FIX (v1.0.302 / 0271): Also check externalLinks format (written by SyncCoordinator v1.0.240+)
        if (metadata.externalLinks?.github?.issueNumber) {
            return true;
        }
        if (metadata.externalLinks?.github?.issues) {
            const issues = metadata.externalLinks.github.issues;
            const firstKey = Object.keys(issues)[0];
            if (firstKey && issues[firstKey]?.issueNumber) {
                return true;
            }
        }
        return false;
    }
    catch (error) {
        // FIX (v1.0.302 / 0271): Log error instead of silent swallow
        console.warn(`   ⚠️  Failed to check existing GitHub issue: ${error}`);
        return false;
    }
}
/**
 * Check if JIRA issue already exists for this increment (v1.0.357)
 */
async function checkExistingJiraIssue(incrementPath) {
    try {
        const metadataPath = path.join(incrementPath, 'metadata.json');
        if (!existsSync(metadataPath)) {
            return false;
        }
        const content = await fs.readFile(metadataPath, 'utf-8');
        const metadata = JSON.parse(content);
        if (metadata.jira?.issue) {
            return true;
        }
        if (metadata.externalLinks?.jira?.issueKey) {
            return true;
        }
        return false;
    }
    catch (error) {
        console.warn(`   ⚠️  Failed to check existing JIRA issue: ${error}`);
        return false;
    }
}
/**
 * Check if ADO work item already exists for this increment (v1.0.357)
 */
async function checkExistingAdoIssue(incrementPath) {
    try {
        const metadataPath = path.join(incrementPath, 'metadata.json');
        if (!existsSync(metadataPath)) {
            return false;
        }
        const content = await fs.readFile(metadataPath, 'utf-8');
        const metadata = JSON.parse(content);
        if (metadata.ado?.workItem) {
            return true;
        }
        if (metadata.externalLinks?.ado?.workItemId) {
            return true;
        }
        return false;
    }
    catch (error) {
        console.warn(`   ⚠️  Failed to check existing ADO work item: ${error}`);
        return false;
    }
}
// Main entry point when run directly
if (process.argv[1]?.endsWith('sync-progress.js') || process.argv[1]?.endsWith('sync-progress.ts')) {
    const args = process.argv.slice(2);
    syncProgress(args).catch(console.error);
}
//# sourceMappingURL=sync-progress.js.map