/**
 * Import Coordinator
 *
 * Orchestrates multi-platform imports from GitHub, JIRA, and Azure DevOps.
 * Handles parallel execution, progress tracking, and result aggregation.
 */
import { consoleLogger as logger } from '../utils/logger.js';
import { GitHubImporter } from './github-importer.js';
import { JiraImporter } from './jira-importer.js';
import { ADOImporter } from './ado-importer.js';
import { updateSyncMetadata } from '../sync/sync-metadata.js';
import { RateLimiter } from './rate-limiter.js';
import { getGitHubAuthFromProject } from '../utils/auth-helpers.js';
/**
 * Import Coordinator
 */
export class ImportCoordinator {
    constructor(config) {
        this.importers = new Map();
        /** Multi-repo importers keyed by "github:owner/repo" */
        this.githubRepoImporters = new Map();
        /** Multi-project ADO importers keyed by "ado:projectName" */
        this.adoProjectImporters = new Map();
        /** Multi-project JIRA importers keyed by "jira:projectKey" */
        this.jiraProjectImporters = new Map();
        this.rateLimiter = null;
        this.importStartTime = 0;
        this.config = {
            enableSyncMetadata: true,
            enableRateLimiting: true,
            projectRoot: process.cwd(),
            ...config,
        };
        this.projectRoot = this.config.projectRoot;
        // Initialize rate limiter if enabled
        if (this.config.enableRateLimiting) {
            this.rateLimiter = new RateLimiter();
        }
        this.initializeImporters();
    }
    /**
     * Initialize importers based on configuration
     */
    initializeImporters() {
        // Multi-repo GitHub support (preferred over single repo)
        if (this.config.githubRepositories && this.config.githubRepositories.length > 0) {
            // CRITICAL FIX (2025-11-26): Use getGitHubAuthFromProject to load from .env file
            // Bug: process.env.GITHUB_TOKEN is empty unless dotenv is explicitly loaded
            const auth = getGitHubAuthFromProject(this.projectRoot);
            const token = this.config.githubToken || this.config.github?.token || auth.token;
            // DIAGNOSTIC (v1.0.7): Log token resolution for debugging
            const tokenSource = this.config.githubToken ? 'coordinatorConfig.githubToken' :
                this.config.github?.token ? 'coordinatorConfig.github.token' :
                    auth.token ? `getGitHubAuthFromProject(${auth.source})` : 'none';
            const tokenPrefix = token ? token.slice(0, 8) + '...' : 'none';
            logger.debug(`ImportCoordinator token: ${tokenPrefix} (from ${tokenSource})`);
            logger.debug(`projectRoot: ${this.projectRoot}`);
            for (const repo of this.config.githubRepositories) {
                try {
                    const importer = new GitHubImporter(repo.owner, repo.repo, token);
                    const key = `github:${repo.owner}/${repo.repo}`;
                    this.githubRepoImporters.set(key, importer);
                }
                catch (error) {
                    console.warn(`Failed to initialize GitHub importer for ${repo.owner}/${repo.repo}: ${error.message}`);
                }
            }
            // If we have multi-repo, don't create single repo importer
            if (this.githubRepoImporters.size > 0) {
                // Mark that we have GitHub configured (for platform detection)
                // We'll handle multi-repo in importAll()
            }
        }
        else if (this.config.github) {
            // Single repo GitHub importer (backwards compatible)
            try {
                const importer = new GitHubImporter(this.config.github.owner, this.config.github.repo, this.config.github.token);
                this.importers.set('github', importer);
            }
            catch (error) {
                console.warn(`Failed to initialize GitHub importer: ${error.message}`);
            }
        }
        // JIRA importer - 1-level structure: JIRA Project → SpecWeave Project (v0.35.3+)
        if (this.config.jira) {
            // Check for multi-project configuration
            const projectMappings = this.config.jira.projectMappings;
            if (projectMappings && projectMappings.length > 0) {
                // CRITICAL SIMPLIFICATION (v0.35.3): One importer per JIRA project
                // Folder structure: AAC/FS-XXX/ (no board level)
                // JIRA boards are views, not organizational structure!
                for (const mapping of projectMappings) {
                    try {
                        const importer = new JiraImporter(this.config.jira.host, this.config.jira.email, this.config.jira.apiToken, mapping.projectKey // Fetch all issues from this project
                        );
                        const key = `jira:${mapping.projectKey}`;
                        this.jiraProjectImporters.set(key, importer);
                    }
                    catch (error) {
                        console.warn(`Failed to initialize JIRA importer for ${mapping.projectKey}: ${error.message}`);
                    }
                }
                // If we have multi-project importers, don't create single project importer
                if (this.jiraProjectImporters.size > 0) {
                    // Project-level mode active - will be handled in importAll()
                }
            }
            else {
                // Single project mode (backwards compatible)
                try {
                    const importer = new JiraImporter(this.config.jira.host, this.config.jira.email, this.config.jira.apiToken);
                    this.importers.set('jira', importer);
                }
                catch (error) {
                    console.warn(`Failed to initialize JIRA importer: ${error.message}`);
                }
            }
        }
        // Azure DevOps importer - supports multi-project mode (similar to GitHub multi-repo)
        if (this.config.ado) {
            // Check for multi-project configuration (projectMappings with multiple projects)
            const projectMappings = this.config.ado.projectMappings;
            if (projectMappings && projectMappings.length > 0) {
                // Multi-project mode: create one importer per project
                for (const mapping of projectMappings) {
                    try {
                        const importer = new ADOImporter(this.config.ado.orgUrl, mapping.projectName, this.config.ado.pat);
                        const key = `ado:${mapping.projectName}`;
                        this.adoProjectImporters.set(key, importer);
                    }
                    catch (error) {
                        console.warn(`Failed to initialize ADO importer for ${mapping.projectName}: ${error.message}`);
                    }
                }
                // If we have multi-project importers, don't create single project importer
                if (this.adoProjectImporters.size > 0) {
                    // Multi-project mode active - will be handled in importAll()
                }
            }
            else {
                // Single project mode (backwards compatible)
                try {
                    const importer = new ADOImporter(this.config.ado.orgUrl, this.config.ado.project, this.config.ado.pat);
                    this.importers.set('ado', importer);
                }
                catch (error) {
                    console.warn(`Failed to initialize ADO importer: ${error.message}`);
                }
            }
        }
        if (this.importers.size === 0 && this.githubRepoImporters.size === 0 && this.adoProjectImporters.size === 0 && this.jiraProjectImporters.size === 0) {
            throw new Error('No importers configured. Provide at least one platform configuration.');
        }
    }
    /**
     * Import from all configured platforms
     */
    async importAll() {
        this.importStartTime = Date.now();
        const parallel = this.config.parallel !== false; // Default to true
        if (parallel) {
            return this.importParallel();
        }
        else {
            return this.importSequential();
        }
    }
    /**
     * Import from platforms in parallel
     */
    async importParallel() {
        const promises = [];
        // Add standard platform importers
        for (const [platform, importer] of this.importers.entries()) {
            promises.push(this.importFromPlatform(platform, importer));
        }
        // Add multi-repo GitHub importers
        for (const [key, importer] of this.githubRepoImporters.entries()) {
            const sourceRepo = key.replace('github:', '');
            promises.push(this.importFromGitHubRepo(importer, sourceRepo));
        }
        // Add multi-project ADO importers
        for (const [key, importer] of this.adoProjectImporters.entries()) {
            const projectName = key.replace('ado:', '');
            promises.push(this.importFromAdoProject(importer, projectName));
        }
        // Add multi-project JIRA importers
        for (const [key, importer] of this.jiraProjectImporters.entries()) {
            const projectKey = key.replace('jira:', '');
            promises.push(this.importFromJiraProject(importer, projectKey));
        }
        const results = await Promise.allSettled(promises);
        return this.aggregateResults(results);
    }
    /**
     * Import from platforms sequentially
     */
    async importSequential() {
        const results = [];
        // Standard platform importers
        for (const [platform, importer] of this.importers.entries()) {
            try {
                const result = await this.importFromPlatform(platform, importer);
                results.push({ status: 'fulfilled', value: result });
            }
            catch (error) {
                results.push({
                    status: 'rejected',
                    reason: error,
                });
            }
        }
        // Multi-repo GitHub importers (sequentially to avoid rate limits)
        for (const [key, importer] of this.githubRepoImporters.entries()) {
            const sourceRepo = key.replace('github:', '');
            try {
                const result = await this.importFromGitHubRepo(importer, sourceRepo);
                results.push({ status: 'fulfilled', value: result });
            }
            catch (error) {
                results.push({
                    status: 'rejected',
                    reason: error,
                });
            }
        }
        // Multi-project ADO importers (sequentially to avoid rate limits)
        for (const [key, importer] of this.adoProjectImporters.entries()) {
            const projectName = key.replace('ado:', '');
            try {
                const result = await this.importFromAdoProject(importer, projectName);
                results.push({ status: 'fulfilled', value: result });
            }
            catch (error) {
                results.push({
                    status: 'rejected',
                    reason: error,
                });
            }
        }
        // Multi-project JIRA importers (sequentially to avoid rate limits)
        for (const [key, importer] of this.jiraProjectImporters.entries()) {
            const projectKey = key.replace('jira:', '');
            try {
                const result = await this.importFromJiraProject(importer, projectKey);
                results.push({ status: 'fulfilled', value: result });
            }
            catch (error) {
                results.push({
                    status: 'rejected',
                    reason: error,
                });
            }
        }
        return this.aggregateResults(results);
    }
    /**
     * Import from a single GitHub repo with source tagging
     */
    async importFromGitHubRepo(importer, sourceRepo) {
        const errors = [];
        const items = [];
        let totalEstimate;
        let pageNumber = 0;
        try {
            // Use pagination for progress tracking
            for await (const page of importer.paginate(this.config.importConfig)) {
                pageNumber++;
                // Tag each item with source repo
                for (const item of page) {
                    item.sourceRepo = sourceRepo;
                }
                items.push(...page);
                // Calculate progress info
                const elapsed = (Date.now() - this.importStartTime) / 1000;
                const rate = elapsed > 0 ? items.length / elapsed : 0;
                const eta = totalEstimate && rate > 0 ? (totalEstimate - items.length) / rate : undefined;
                // Report enhanced progress
                if (this.config.onProgressEnhanced) {
                    this.config.onProgressEnhanced({
                        platform: 'github',
                        current: items.length,
                        total: totalEstimate,
                        percentage: totalEstimate ? Math.round((items.length / totalEstimate) * 100) : undefined,
                        rate: Math.round(rate * 10) / 10,
                        eta: eta ? Math.round(eta) : undefined,
                        sourceRepo,
                        page: pageNumber,
                    });
                }
                // Legacy progress callback
                if (this.config.onProgress) {
                    this.config.onProgress(`github (${sourceRepo})`, items.length, totalEstimate);
                }
            }
            // Update sync metadata if enabled (always write on success, even with 0 items)
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: items.length,
                    lastSyncResult: errors.length > 0 ? 'partial' : 'success',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'github', metadata);
                }
                catch (error) {
                    errors.push(`Failed to update sync metadata: ${error.message}`);
                }
            }
        }
        catch (error) {
            errors.push(error.message || String(error));
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: 0,
                    lastSyncResult: 'failed',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'github', metadata);
                }
                catch {
                    // Ignore
                }
            }
        }
        return {
            count: items.length,
            items,
            errors,
            platform: 'github',
            totalEstimate,
            sourceRepo,
        };
    }
    /**
     * Import from a single ADO project with source tagging (multi-project support)
     */
    async importFromAdoProject(importer, projectName) {
        const errors = [];
        const items = [];
        let totalEstimate;
        let pageNumber = 0;
        try {
            // Use pagination for progress tracking
            for await (const page of importer.paginate(this.config.importConfig)) {
                pageNumber++;
                // Tag each item with source project (adoProjectName is already set by ADOImporter)
                // Just push items - they already have adoProjectName from the importer
                items.push(...page);
                // Calculate progress info
                const elapsed = (Date.now() - this.importStartTime) / 1000;
                const rate = elapsed > 0 ? items.length / elapsed : 0;
                const eta = totalEstimate && rate > 0 ? (totalEstimate - items.length) / rate : undefined;
                // Report enhanced progress
                if (this.config.onProgressEnhanced) {
                    this.config.onProgressEnhanced({
                        platform: 'ado',
                        current: items.length,
                        total: totalEstimate,
                        percentage: totalEstimate ? Math.round((items.length / totalEstimate) * 100) : undefined,
                        rate: Math.round(rate * 10) / 10,
                        eta: eta ? Math.round(eta) : undefined,
                        sourceRepo: projectName, // Use sourceRepo field for project name
                        page: pageNumber,
                    });
                }
                // Legacy progress callback
                if (this.config.onProgress) {
                    this.config.onProgress(`ado (${projectName})`, items.length, totalEstimate);
                }
            }
            // Update sync metadata if enabled (always write on success, even with 0 items)
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: items.length,
                    lastSyncResult: errors.length > 0 ? 'partial' : 'success',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'ado', metadata);
                }
                catch (error) {
                    errors.push(`Failed to update sync metadata: ${error.message}`);
                }
            }
        }
        catch (error) {
            errors.push(error.message || String(error));
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: 0,
                    lastSyncResult: 'failed',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'ado', metadata);
                }
                catch {
                    // Ignore
                }
            }
        }
        return {
            count: items.length,
            items,
            errors,
            platform: 'ado',
            totalEstimate,
            sourceRepo: projectName,
        };
    }
    /**
     * Import from a single JIRA project with source tagging
     * MIRRORS importFromAdoProject for JIRA multi-project support.
     *
     * @param importer - JiraImporter instance configured for this project
     * @param projectKey - JIRA project key (e.g., "PROJ")
     */
    async importFromJiraProject(importer, projectKey) {
        const errors = [];
        const items = [];
        let totalEstimate;
        let pageNumber = 0;
        try {
            // Use pagination for progress tracking
            for await (const page of importer.paginate(this.config.importConfig)) {
                pageNumber++;
                // Tag each item with source project
                for (const item of page) {
                    item.jiraProjectKey = projectKey;
                }
                items.push(...page);
                // Calculate progress info
                const elapsed = (Date.now() - this.importStartTime) / 1000;
                const rate = elapsed > 0 ? items.length / elapsed : 0;
                const eta = totalEstimate && rate > 0 ? (totalEstimate - items.length) / rate : undefined;
                // Report enhanced progress
                if (this.config.onProgressEnhanced) {
                    this.config.onProgressEnhanced({
                        platform: 'jira',
                        current: items.length,
                        total: totalEstimate,
                        percentage: totalEstimate ? Math.round((items.length / totalEstimate) * 100) : undefined,
                        rate: Math.round(rate * 10) / 10,
                        eta: eta ? Math.round(eta) : undefined,
                        sourceRepo: projectKey, // Use sourceRepo field for project key
                        page: pageNumber,
                    });
                }
                // Legacy progress callback
                if (this.config.onProgress) {
                    this.config.onProgress(`jira (${projectKey})`, items.length, totalEstimate);
                }
            }
            // Update sync metadata if enabled (always write on success, even with 0 items)
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: items.length,
                    lastSyncResult: errors.length > 0 ? 'partial' : 'success',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'jira', metadata);
                }
                catch (error) {
                    errors.push(`Failed to update sync metadata: ${error.message}`);
                }
            }
        }
        catch (error) {
            errors.push(error.message || String(error));
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: 0,
                    lastSyncResult: 'failed',
                };
                try {
                    updateSyncMetadata(this.projectRoot, 'jira', metadata);
                }
                catch {
                    // Ignore
                }
            }
        }
        return {
            count: items.length,
            items,
            errors,
            platform: 'jira',
            totalEstimate,
            sourceRepo: projectKey,
        };
    }
    /**
     * Import from a single platform with progress tracking
     */
    async importFromPlatform(platform, importer) {
        const errors = [];
        const items = [];
        try {
            // Use pagination for progress tracking
            for await (const page of importer.paginate(this.config.importConfig)) {
                items.push(...page);
                // Report progress
                if (this.config.onProgress) {
                    this.config.onProgress(platform, items.length);
                }
            }
            // Update sync metadata if enabled (always write on success, even with 0 items)
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: items.length,
                    lastSyncResult: errors.length > 0 ? 'partial' : 'success',
                };
                try {
                    updateSyncMetadata(this.projectRoot, platform, metadata);
                }
                catch (error) {
                    // Log metadata update error but don't fail the import
                    errors.push(`Failed to update sync metadata: ${error.message}`);
                }
            }
        }
        catch (error) {
            errors.push(error.message || String(error));
            // Update sync metadata with failed status
            if (this.config.enableSyncMetadata) {
                const metadata = {
                    lastImport: new Date().toISOString(),
                    lastImportCount: 0,
                    lastSyncResult: 'failed',
                };
                try {
                    updateSyncMetadata(this.projectRoot, platform, metadata);
                }
                catch {
                    // Ignore metadata update errors on failed imports
                }
            }
        }
        return {
            count: items.length,
            items,
            errors,
            platform: importer.platform,
        };
    }
    /**
     * Aggregate results from all platforms
     */
    aggregateResults(results) {
        const allResults = [];
        const allItems = [];
        const errors = {};
        const platforms = [];
        for (const result of results) {
            if (result.status === 'fulfilled') {
                const importResult = result.value;
                allResults.push(importResult);
                allItems.push(...importResult.items);
                platforms.push(importResult.platform);
                if (importResult.errors.length > 0) {
                    errors[importResult.platform] = importResult.errors;
                }
            }
            else {
                // Rejected promise
                const error = result.reason;
                const errorMsg = error?.message || String(error);
                // Try to determine which platform failed
                const platformMatch = errorMsg.match(/(GitHub|JIRA|ADO)/i);
                const platform = platformMatch ? platformMatch[1].toLowerCase() : 'unknown';
                errors[platform] = errors[platform] || [];
                errors[platform].push(errorMsg);
            }
        }
        return {
            results: allResults,
            totalCount: allItems.length,
            allItems,
            errors,
            platforms,
        };
    }
    /**
     * Import from a specific platform only
     */
    async importFrom(platform) {
        // Handle multi-repo GitHub case
        if (platform === 'github' && this.githubRepoImporters.size > 0) {
            const results = [];
            for (const [key, importer] of this.githubRepoImporters.entries()) {
                const sourceRepo = key.replace('github:', '');
                const result = await this.importFromGitHubRepo(importer, sourceRepo);
                results.push(result);
            }
            // Aggregate all results into single ImportResult
            return {
                count: results.reduce((sum, r) => sum + r.count, 0),
                items: results.flatMap(r => r.items),
                errors: results.flatMap(r => r.errors),
                platform: 'github',
            };
        }
        // Handle multi-project ADO case
        if (platform === 'ado' && this.adoProjectImporters.size > 0) {
            const results = [];
            for (const [key, importer] of this.adoProjectImporters.entries()) {
                const projectName = key.replace('ado:', '');
                const result = await this.importFromAdoProject(importer, projectName);
                results.push(result);
            }
            // Aggregate all results into single ImportResult
            return {
                count: results.reduce((sum, r) => sum + r.count, 0),
                items: results.flatMap(r => r.items),
                errors: results.flatMap(r => r.errors),
                platform: 'ado',
            };
        }
        // Single-repo/project fallback
        const importer = this.importers.get(platform);
        if (!importer) {
            throw new Error(`${platform} importer not configured`);
        }
        return this.importFromPlatform(platform, importer);
    }
    /**
     * Get list of configured platforms
     */
    getConfiguredPlatforms() {
        const platforms = Array.from(this.importers.keys());
        // Add 'github' if multi-repo importers are configured
        if (this.githubRepoImporters.size > 0 && !platforms.includes('github')) {
            platforms.push('github');
        }
        // Add 'ado' if multi-project importers are configured
        if (this.adoProjectImporters.size > 0 && !platforms.includes('ado')) {
            platforms.push('ado');
        }
        // Add 'jira' if multi-project importers are configured
        if (this.jiraProjectImporters.size > 0 && !platforms.includes('jira')) {
            platforms.push('jira');
        }
        return platforms;
    }
    /**
     * Check if a platform is configured
     */
    isPlatformConfigured(platform) {
        if (this.importers.has(platform))
            return true;
        const multiImporterMap = {
            'github': this.githubRepoImporters,
            'ado': this.adoProjectImporters,
            'jira': this.jiraProjectImporters,
        };
        return (multiImporterMap[platform]?.size || 0) > 0;
    }
    /**
     * Get list of configured GitHub repositories (for multi-repo support)
     */
    getConfiguredGitHubRepos() {
        return this.extractKeysWithPrefix(this.githubRepoImporters, 'github:');
    }
    /**
     * Get list of configured ADO projects (for multi-project support)
     */
    getConfiguredAdoProjects() {
        return this.extractKeysWithPrefix(this.adoProjectImporters, 'ado:');
    }
    /**
     * Get list of configured JIRA projects (for multi-project support)
     */
    getConfiguredJiraProjects() {
        return this.extractKeysWithPrefix(this.jiraProjectImporters, 'jira:');
    }
    extractKeysWithPrefix(map, prefix) {
        return Array.from(map.keys()).map(key => key.replace(prefix, ''));
    }
}
/**
 * Resolve which workspace repo an external import source maps to.
 *
 * Matches by:
 * - GitHub: owner/repo against workspace.repos[].sync.github
 * - JIRA: project key against workspace.repos[].sync.jira.projectKey
 * - ADO: project name against workspace.repos[].sync.ado.project
 *
 * Returns workspace.name when no match found (with a warning logged).
 *
 * @param workspace - WorkspaceConfig from config.json
 * @param platform - Import source platform
 * @param sourceId - Source identifier (e.g., "acme/fe" for GitHub, "BE" for JIRA)
 * @returns The matching workspace repo id, or workspace.name as fallback
 */
export function resolveProjectFromWorkspace(workspace, platform, sourceId) {
    if (!workspace)
        return 'unknown';
    for (const repo of workspace.repos) {
        const sync = repo.sync;
        if (!sync)
            continue;
        if (platform === 'github' && sync.github) {
            const ghKey = `${sync.github.owner}/${sync.github.repo}`;
            if (ghKey === sourceId)
                return repo.id;
        }
        if (platform === 'jira' && sync.jira) {
            if (sync.jira.projectKey === sourceId)
                return repo.id;
        }
        if (platform === 'ado' && sync.ado) {
            if (sync.ado.project === sourceId)
                return repo.id;
        }
    }
    // No match — use workspace name as fallback
    logger.warn(`No workspace repo matches import source "${sourceId}" (${platform}). Using workspace.name "${workspace.name}" as projectId.`);
    return workspace.name;
}
//# sourceMappingURL=import-coordinator.js.map