import { resolve } from '../../lib/server-container.js';
import { SdlcLifecycle, PrStatus } from '../../../../../packages/core/src/domain/generated/output.js';
import { deriveNodeState } from '../../components/common/feature-node/derive-feature-state.js';
import { getWorkflowDefaults } from '../actions/get-workflow-defaults.js';
import { getSettings } from '../../../../../packages/core/src/infrastructure/services/settings.service.js';
const LIFECYCLE_TO_STATUS = {
    [SdlcLifecycle.Started]: 'pending',
    [SdlcLifecycle.Analyze]: 'in-progress',
    [SdlcLifecycle.Requirements]: 'action-needed',
    [SdlcLifecycle.Research]: 'in-progress',
    [SdlcLifecycle.Planning]: 'in-progress',
    [SdlcLifecycle.Implementation]: 'in-progress',
    [SdlcLifecycle.Review]: 'action-needed',
    [SdlcLifecycle.Maintain]: 'done',
    [SdlcLifecycle.Blocked]: 'blocked',
    [SdlcLifecycle.Pending]: 'pending',
    [SdlcLifecycle.Deleting]: 'blocked',
    [SdlcLifecycle.AwaitingUpstream]: 'action-needed',
    [SdlcLifecycle.Archived]: 'done',
};
function lifecycleToStatus(lifecycle) {
    return LIFECYCLE_TO_STATUS[lifecycle] ?? 'pending';
}
const APP_STATUS_TO_FEATURE_STATUS = {
    ready: 'done',
    building: 'in-progress',
    interrupted: 'action-needed',
    failed: 'error',
};
const APP_STATUS_LIFECYCLE_LABEL = {
    ready: 'Application',
    building: 'Application · Building',
    interrupted: 'Application · Interrupted',
    failed: 'Application · Failed',
};
function applicationToTreeRow(app, repoByPath) {
    const repo = repoByPath.get(app.repositoryPath);
    const repoName = repo?.name ?? app.repositoryPath.split(/[/\\]/).pop() ?? app.repositoryPath;
    return {
        id: `app-${app.id}`,
        name: app.name,
        status: APP_STATUS_TO_FEATURE_STATUS[app.effectiveStatus],
        lifecycle: APP_STATUS_LIFECYCLE_LABEL[app.effectiveStatus],
        branch: '',
        repositoryName: repoName,
        remoteUrl: repo?.remoteUrl,
        _repositoryPath: app.repositoryPath,
        _repositoryId: repo?.id,
        _isApplication: true,
        _applicationId: app.id,
        _applicationCloudUrl: app.cloudDeploymentUrl ?? undefined,
    };
}
/**
 * Build a lookup of the latest agent run per feature ID.
 * ListAgentRunsUseCase returns runs sorted by createdAt desc,
 * so the first run per featureId is the latest.
 */
function buildLatestAgentRunMap(agentRuns) {
    const map = new Map();
    for (const run of agentRuns) {
        if (run.featureId && !map.has(run.featureId)) {
            map.set(run.featureId, run);
        }
    }
    return map;
}
export async function getFeatureTreeData() {
    const listFeatures = resolve('ListFeaturesUseCase');
    const listRepos = resolve('ListRepositoriesUseCase');
    const listAgentRuns = resolve('ListAgentRunsUseCase');
    const [features, repositories, agentRuns, workflowDefaults, applications, initialDeployments] = await Promise.all([
        listFeatures.execute({ includeArchived: true }),
        listRepos.execute(),
        listAgentRuns.execute(),
        getWorkflowDefaults().catch(() => undefined),
        resolveListApplications().catch(() => []),
        resolveListDeployments().catch(() => []),
    ]);
    const repoByPath = new Map();
    for (const repo of repositories) {
        repoByPath.set(repo.path, { id: repo.id, name: repo.name, remoteUrl: repo.remoteUrl });
    }
    const latestRunByFeature = buildLatestAgentRunMap(agentRuns);
    // Build a set of feature IDs that are parents (have children)
    const parentIds = new Set();
    for (const feature of features) {
        if (feature.parentId) {
            parentIds.add(feature.parentId);
        }
    }
    const featureRows = features.map((feature) => {
        const repo = repoByPath.get(feature.repositoryPath);
        const latestRun = latestRunByFeature.get(feature.id);
        return {
            id: feature.id,
            name: feature.name,
            status: lifecycleToStatus(feature.lifecycle),
            lifecycle: feature.lifecycle,
            branch: feature.branch,
            repositoryName: repo?.name ?? feature.repositoryPath.split('/').pop() ?? feature.repositoryPath,
            remoteUrl: repo?.remoteUrl,
            _repositoryPath: feature.repositoryPath,
            _repositoryId: repo?.id,
            parentId: feature.parentId ?? undefined,
            nodeState: deriveNodeState(feature, latestRun),
            hasChildren: parentIds.has(feature.id),
            hasOpenPr: feature.pr?.status === PrStatus.Open,
        };
    });
    const applicationRows = applications.map((app) => applicationToTreeRow(app, repoByPath));
    const inventoryRows = [...applicationRows, ...featureRows];
    const repos = repositories.map((repo) => ({
        name: repo.name,
        remoteUrl: repo.remoteUrl,
    }));
    const settings = getSettings();
    const featureOptions = features
        .map((f) => ({ id: f.id, name: f.name }))
        .filter((f) => f.id && !f.id.startsWith('#'));
    const repositoryOptions = repositories.map((r) => ({
        id: r.id,
        name: r.name,
        path: r.path,
    }));
    const createData = {
        featureOptions,
        repositoryOptions,
        workflowDefaults,
        currentAgentType: settings.agent.type,
        currentModel: settings.models.default,
    };
    return {
        features: featureRows,
        inventoryRows,
        repos,
        createData,
        applications,
        initialDeployments,
    };
}
/**
 * Resolve and execute ListApplicationsUseCase. Wrapped so the call site
 * can swallow registration errors (e.g. when the use case isn't bound
 * in tests) without losing the rest of the data.
 */
async function resolveListApplications() {
    const useCase = resolve('ListApplicationsUseCase');
    return useCase.execute();
}
async function resolveListDeployments() {
    const useCase = resolve('ListDeploymentsUseCase');
    return useCase.execute();
}
