// Planu — Multi-app, monorepo, and API contract detection: core logic
import { readdir } from 'node:fs/promises';
import { join, basename } from 'node:path';
import { fileExists, hasAnyFile, collectDependencies } from '../detection-utils.js';
import { detectLanguage, detectLanguageVersion } from '../language-detector.js';
import { detectFramework, detectOrm, detectDatabase } from '../framework-detector.js';
import { detectArchitecture } from '../architecture-detector.js';
import { detectDeepIOS, detectDeepAndroid, getMobilePlatformFromFramework, getMobileFrameworkFromName, } from '../mobile-detector.js';
import { detectHypermedia } from '../detectors/hypermedia-detector.js';
import { detectTemplateEngine } from '../detectors/template-engine-detector.js';
import { detectOtel } from '../detectors/otel-detector.js';
import { runSupplyChainChecks } from '../supply-chain/index.js';
import { detectLlm } from '../detectors/llm-detector.js';
import { detectLibraries } from '../detectors/library-detector.js';
import { inferAppType } from './app-type.js';
/**
 * Check if the project is a monorepo by looking for workspace config files.
 */
export async function isMonorepoProject(projectPath) {
    const monorepoFiles = ['turbo.json', 'nx.json', 'lerna.json', 'pnpm-workspace.yaml'];
    for (const f of monorepoFiles) {
        if (await fileExists(projectPath, f)) {
            return true;
        }
    }
    // Check package.json workspaces
    try {
        const { readFile } = await import('node:fs/promises');
        const pkgJson = JSON.parse(await readFile(join(projectPath, 'package.json'), 'utf-8'));
        if (pkgJson.workspaces) {
            return true;
        }
    }
    catch {
        // No package.json
    }
    // Check for Cargo workspace
    try {
        const { readFile } = await import('node:fs/promises');
        const cargoToml = await readFile(join(projectPath, 'Cargo.toml'), 'utf-8');
        if (cargoToml.includes('[workspace]')) {
            return true;
        }
    }
    catch {
        // No Cargo.toml
    }
    // Check for Go workspace
    if (await fileExists(projectPath, 'go.work')) {
        return true;
    }
    return false;
}
/**
 * Detect multiple apps within a project (monorepo or multi-app structure).
 */
export async function detectApps(projectPath, signatures, archConfig) {
    const apps = [];
    const isMonorepo = await isMonorepoProject(projectPath);
    if (isMonorepo) {
        // Scan apps/, packages/, and services/ directories
        for (const dir of ['apps', 'packages', 'services']) {
            const dirPath = join(projectPath, dir);
            try {
                const entries = await readdir(dirPath, { withFileTypes: true });
                for (const entry of entries) {
                    if (entry.isDirectory()) {
                        const appPath = join(dirPath, entry.name);
                        const app = await analyzeAppDir(appPath, entry.name, signatures, archConfig);
                        if (app) {
                            apps.push(app);
                        }
                    }
                }
            }
            catch {
                // Directory doesn't exist
            }
        }
    }
    // If no apps found, treat root as a single app
    if (apps.length === 0) {
        const rootApp = await analyzeAppDir(projectPath, basename(projectPath), signatures, archConfig);
        if (rootApp) {
            apps.push(rootApp);
        }
    }
    return apps;
}
/**
 * Analyze an individual app directory to determine its stack.
 */
async function analyzeAppDir(appPath, name, signatures, archConfig) {
    const language = await detectLanguage(appPath, signatures);
    const framework = await detectFramework(appPath, signatures);
    const appType = inferAppType(framework, appPath);
    const orm = await detectOrm(appPath);
    const database = await detectDatabase(appPath, signatures);
    let appArch;
    if (archConfig) {
        const archResult = await detectArchitecture(appPath, archConfig);
        appArch = archResult.primary;
    }
    // SPEC-013: Enrich mobile apps with deep detection
    const mobilePlatform = framework ? getMobilePlatformFromFramework(framework) : null;
    const mobileFramework = framework ? getMobileFrameworkFromName(framework) : null;
    let mobileCapabilities;
    let mobileDeployTarget;
    if (mobilePlatform === 'ios') {
        const iosInfo = await detectDeepIOS(appPath);
        mobileCapabilities = iosInfo.capabilities.length > 0 ? iosInfo.capabilities : undefined;
        mobileDeployTarget = iosInfo.deploymentTarget ?? undefined;
    }
    else if (mobilePlatform === 'android') {
        const androidInfo = await detectDeepAndroid(appPath);
        mobileCapabilities = androidInfo.capabilities.length > 0 ? androidInfo.capabilities : undefined;
        mobileDeployTarget = androidInfo.minSdk !== null ? `minSdk ${androidInfo.minSdk}` : undefined;
    }
    return {
        name,
        path: appPath,
        type: appType,
        language,
        framework,
        database,
        orm: orm ?? undefined,
        architecture: appArch,
        mobilePlatform: mobilePlatform ?? undefined,
        mobileFramework: mobileFramework ?? undefined,
        mobileCapabilities,
        mobileDeployTarget,
    };
}
/**
 * Detect API contracts between apps (OpenAPI, GraphQL, tRPC, gRPC).
 */
export async function detectApiContracts(projectPath) {
    const contracts = [];
    // OpenAPI
    for (const file of ['openapi.yaml', 'openapi.json', 'swagger.yaml', 'swagger.json']) {
        if (await fileExists(projectPath, file)) {
            contracts.push({ type: 'openapi', path: file, endpoints: [] });
        }
    }
    // GraphQL
    if (await hasAnyFile(projectPath, ['schema.graphql', 'schema.gql', '**/*.graphql'])) {
        contracts.push({ type: 'graphql', endpoints: [] });
    }
    // tRPC
    const deps = await collectDependencies(projectPath);
    if (deps.has('@trpc/server') || deps.has('@trpc/client')) {
        contracts.push({ type: 'trpc', endpoints: [] });
    }
    // gRPC
    if (await hasAnyFile(projectPath, ['**/*.proto'])) {
        contracts.push({ type: 'grpc', endpoints: [] });
    }
    return contracts;
}
export { detectLanguageVersion };
/**
 * SPEC-044: Detect OpenTelemetry for a project.
 * Returns OtelDetectionResult to be stored in knowledge.otelDetection.
 */
export async function detectOtelForProject(projectPath) {
    return detectOtel(projectPath);
}
/**
 * SPEC-046: Run supply chain security checks for a project.
 * Returns SupplyChainReport to be stored in knowledge.supplyChain.
 */
export async function detectSupplyChainForProject(projectPath, isGovernmentOrEnterprise) {
    return runSupplyChainChecks(projectPath, isGovernmentOrEnterprise);
}
/**
 * SPEC-043: Detect hypermedia framework and template engine for a project.
 * Returns enriched HypermediaDetectionResult with templateEngine from the template detector.
 */
export async function detectHypermediaForProject(projectPath) {
    const [hypermediaResult, templateEngine] = await Promise.all([
        detectHypermedia(projectPath),
        detectTemplateEngine(projectPath),
    ]);
    const result = { ...hypermediaResult };
    if (templateEngine !== null) {
        result.templateEngine = templateEngine;
        // If template engine is detected and no bundler → server-side rendering is confirmed
        if (!result.hasBundler) {
            result.renderingMode = 'server-side';
        }
        // Alpine.js is a complement (not SPA) when template engine is present
        // (jsComplement stays 'alpine' if already detected, else stays 'none')
    }
    return result;
}
/**
 * SPEC-045: Detect LLM integration for a project.
 * Returns LlmDetectionResult to be stored in knowledge.llmDetection.
 */
export async function detectLlmForProject(projectPath) {
    return detectLlm(projectPath);
}
/**
 * SPEC-047: Detect local-first architecture signals for a project.
 * Returns LocalFirstDetectionResult to be stored in knowledge.localFirst.
 */
export async function detectLocalFirstForProject(projectPath) {
    const { detectLocalFirst } = await import('../local-first/detector.js');
    return detectLocalFirst(projectPath);
}
/**
 * SPEC-004 tech-debt: Detect key libraries for Python and Node.js ecosystems.
 * Returns DetectedLibraries to be stored in knowledge.detectedLibraries.
 */
export async function detectLibrariesForProject(projectPath) {
    return detectLibraries(projectPath);
}
//# sourceMappingURL=core.js.map