/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { existsSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { Messages } from '@salesforce/core';
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
import { select } from '@inquirer/prompts';
import { setupOpenOrchestraIntegration } from '../../integrations/open-orchestra.js';
import { ALL_PROFILES } from '../../profiles/index.js';
import { setupAdvisoryMode } from '../../services/advisory-mode.js';
import { ensureDir, FileWriter } from '../../services/file-writer.js';
import { setupLocalState } from '../../services/local-state.js';
import { classCatalogExists, docIndexExists, generateClassCatalogTemplate, generateDocIndexTemplate, generateProjectKnowledgeTemplate, projectKnowledgeExists, writeClassCatalog, writeDocIndex, writeProjectKnowledge, } from '../../services/project-knowledge.js';
import { generateOfflineRunbook } from '../../services/offline-recovery.js';
import { listSyncableProfiles, syncProfileDocs } from '../../services/docs-sync.js';
import { readWorkspaceConfig } from '../../services/workspace-config.js';
import { setupAgentforce } from '../../setup/agentforce-setup.js';
import { setupClaude } from '../../setup/claude-setup.js';
import { setupCodex } from '../../setup/codex-setup.js';
import { setupCursor } from '../../setup/cursor-setup.js';
import { setupJetBrains } from '../../setup/jetbrains-setup.js';
import { setupVsCode } from '../../setup/vscode-setup.js';
import { setupWindsurf } from '../../setup/windsurf-setup.js';
import { SUPPORTED_TOOLS } from '../../types/index.js';
import { resolveProfiles, resolveTools } from '../../util/command-helpers.js';
import { DOC_SYNC_PROFILES, generateDocSyncSkill, getDemandLoadedSkillFiles, getPromptRegistryFiles, } from '../../generators/skill-generator.js';
import { generateDefaultPlaybooks, PLAYBOOKS_DIR } from '../../generators/playbook-generator.js';
import { generatePermissionsContent } from '../../generators/permissions-content.js';
import { generateEnvAudit } from '../../generators/env-audit-content.js';
import { generateSalesforcePlaywrightUtils } from '../../generators/salesforce-playwright-generator.js';
import { managedBlock } from '../../services/managed-blocks.js';
import { PLUGIN_VERSION } from '../../version.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@jterrats/setup-agents', 'setup-agents.local');
const PROFILE_HELP_VALUE = ALL_PROFILES.map((p) => p.id).join('|');
export default class Local extends SfCommand {
    static summary = messages.getMessage('summary');
    static description = messages.getMessage('description');
    static examples = messages.getMessages('examples');
    static flags = {
        rules: Flags.string({
            summary: messages.getMessage('flags.rules.summary'),
            description: messages.getMessage('flags.rules.description'),
            options: SUPPORTED_TOOLS,
            helpValue: 'cursor|vscode|codex|claude|agentforce|windsurf|jetbrains',
        }),
        profile: Flags.string({
            summary: messages.getMessage('flags.profile.summary'),
            description: messages.getMessage('flags.profile.description'),
            helpValue: PROFILE_HELP_VALUE,
        }),
        force: Flags.boolean({
            char: 'f',
            summary: messages.getMessage('flags.force.summary'),
            description: messages.getMessage('flags.force.description'),
            default: false,
        }),
        'open-orchestra': Flags.boolean({
            summary: messages.getMessage('flags.open-orchestra.summary'),
            description: messages.getMessage('flags.open-orchestra.description'),
            default: false,
        }),
        quick: Flags.boolean({
            summary: messages.getMessage('flags.quick.summary'),
            description: messages.getMessage('flags.quick.description'),
            default: false,
        }),
        advisory: Flags.boolean({
            summary: messages.getMessage('flags.advisory.summary'),
            description: messages.getMessage('flags.advisory.description'),
            default: false,
        }),
        scope: Flags.string({
            summary: messages.getMessage('flags.scope.summary'),
            description: messages.getMessage('flags.scope.description'),
            options: ['project', 'user'],
        }),
    };
    async run() {
        const { flags } = await this.parse(Local);
        const cwd = process.cwd();
        const writer = new FileWriter({
            force: flags.force,
            log: (f) => this.log(messages.getMessage('info.fileCreated', [f])),
            warn: (f) => this.warn(messages.getMessage('warn.fileExists', [f])),
        });
        const workspaceConfig = readWorkspaceConfig(cwd);
        const usingTeamConfig = !flags.profile && workspaceConfig !== null;
        const configuredProfileFlag = flags.profile ?? workspaceConfig?.profiles.join(',');
        const { profiles, usedDefault } = await resolveProfiles(cwd, configuredProfileFlag, messages, (msg) => this.warn(msg));
        const advisoryMode = flags.advisory || workspaceConfig?.mode === 'advisory';
        const tools = await resolveTools(cwd, flags.rules, workspaceConfig?.tools, messages);
        if (usingTeamConfig) {
            this.log(messages.getMessage('info.teamConfig', [profiles.map((p) => p.id).join(', '), tools.join(', ')]));
        }
        const isSalesforceProject = existsSync(join(cwd, 'sfdx-project.json'));
        const ruleOverrides = workspaceConfig?.ruleOverrides;
        const configured = [];
        await Promise.all(tools.map(async (tool) => {
            this.log(messages.getMessage('info.configuring', [tool]));
            const cursorScope = flags.scope;
            await runSetup(tool, {
                cwd,
                profiles,
                writer,
                isSalesforceProject,
                ruleOverrides,
                cursorScope,
                log: (m) => this.log(m),
            });
            configured.push(tool);
        }));
        writeSharedSkillAssets(cwd, profiles, writer);
        const extended = flags.quick
            ? {}
            : await this.runExtendedSetup(cwd, profiles, tools, writer, isSalesforceProject, advisoryMode, flags);
        if (usedDefault)
            this.warn(messages.getMessage('warn.profileDefault'));
        this.log(messages.getMessage('info.done', [configured.join(', ')]));
        return {
            configured,
            profiles: profiles.map((p) => p.id),
            localState: extended.localState,
            advisory: extended.advisory,
            openOrchestra: extended.openOrchestra,
            cwd,
        };
    }
    /** Run all extended setup steps (skipped in --quick mode). */
    async runExtendedSetup(cwd, profiles, tools, writer, isSalesforceProject, advisoryMode, flags) {
        writeDocSyncSkill(cwd, profiles, tools, writer);
        writePlaybooks(cwd, writer);
        writePermissions(cwd, profiles, writer);
        writeEnvAudit(cwd, writer);
        const localState = setupLocalState(cwd, writer);
        writeProjectKnowledgeTemplates(cwd);
        const openOrchestra = flags['open-orchestra'] ? setupOpenOrchestraIntegration(cwd, profiles, writer) : undefined;
        if (openOrchestra?.status === 'not-installed')
            this.warn(openOrchestra.guidance);
        writeSalesforcePlaywrightUtils(cwd, profiles, writer);
        writeOfflineRunbook(cwd, writer);
        await syncProfileDocsIfAvailable(cwd, profiles, (m) => this.log(m));
        if (isSalesforceProject)
            writeGeneratedPrompts(cwd, profiles, writer);
        const advisory = advisoryMode ? setupAdvisoryMode(cwd, profiles, writer) : undefined;
        if (advisoryMode)
            this.log(messages.getMessage('info.advisoryDone', [advisory.path]));
        return { localState, openOrchestra, advisory };
    }
}
const TOOL_HANDLERS = {
    cursor: (ctx) => setupCursor({
        ...ctx,
        promptScope: ctx.cursorScope
            ? () => Promise.resolve(ctx.cursorScope)
            : () => promptCursorScope(messages),
        logInfo: ctx.log,
    }),
    vscode: (ctx) => setupVsCode(ctx),
    codex: (ctx) => setupCodex(ctx),
    claude: (ctx) => setupClaude(ctx),
    agentforce: (ctx) => setupAgentforce(ctx),
    windsurf: (ctx) => setupWindsurf(ctx),
    jetbrains: (ctx) => setupJetBrains(ctx),
};
async function runSetup(tool, ctx) {
    await TOOL_HANDLERS[tool](ctx);
}
async function promptCursorScope(msgs) {
    if (!process.stdin.isTTY)
        return 'project';
    return select({
        message: msgs.getMessage('prompt.cursorRuleScope'),
        choices: [
            { value: 'project', name: msgs.getMessage('prompt.cursorRuleScopeProject') },
            { value: 'user', name: msgs.getMessage('prompt.cursorRuleScopeUser') },
        ],
    });
}
function writeSalesforcePlaywrightUtils(cwd, profiles, writer) {
    if (!profiles.some((p) => p.id === 'qa'))
        return;
    const utils = generateSalesforcePlaywrightUtils();
    for (const [relativePath, content] of Object.entries(utils)) {
        const fullPath = join(cwd, relativePath);
        ensureDir(join(fullPath, '..'));
        writer.write(fullPath, content);
    }
}
function writeOfflineRunbook(cwd, writer) {
    const runbook = generateOfflineRunbook(cwd);
    const fullPath = join(cwd, '.setup-agents', 'OFFLINE.md');
    ensureDir(join(fullPath, '..'));
    writer.write(fullPath, runbook);
}
function writeGeneratedPrompts(cwd, profiles, writer) {
    const files = getPromptRegistryFiles(profiles.map((p) => p.id), PLUGIN_VERSION);
    if (Object.keys(files).length === 0)
        return;
    const promptsDir = join(cwd, '.generated-prompts');
    ensureDir(promptsDir);
    for (const [filename, content] of Object.entries(files)) {
        writer.write(join(promptsDir, filename), content);
    }
}
function writeSharedSkillAssets(cwd, profiles, writer) {
    const assets = getDemandLoadedSkillFiles(profiles.map((p) => p.id));
    if (Object.keys(assets).length === 0)
        return;
    const skillsDir = join(cwd, '.setup-agents', 'skills');
    for (const [relativePath, content] of Object.entries(assets)) {
        const fullPath = join(skillsDir, relativePath);
        ensureDir(join(fullPath, '..'));
        writer.write(fullPath, content);
    }
}
function writeDocSyncSkill(cwd, profiles, tools, writer) {
    const docSyncProfiles = profiles.filter((p) => DOC_SYNC_PROFILES.has(p.id));
    if (docSyncProfiles.length === 0)
        return;
    const skillContent = generateDocSyncSkill(docSyncProfiles, PLUGIN_VERSION);
    const blockContent = managedBlock('doc-sync-skill', PLUGIN_VERSION, skillContent);
    if (tools.includes('cursor')) {
        const skillDir = join(cwd, '.cursor', 'skills', 'doc-sync');
        ensureDir(skillDir);
        writer.write(join(skillDir, 'SKILL.md'), skillContent);
    }
    if (tools.includes('claude')) {
        const rulesDir = join(cwd, '.claude', 'rules');
        ensureDir(rulesDir);
        writer.write(join(rulesDir, 'doc-sync.md'), [`<!-- setup-agents: ${PLUGIN_VERSION} -->`, '', blockContent].join('\n'));
    }
    if (tools.includes('codex')) {
        const codexDir = join(cwd, '.codex');
        ensureDir(codexDir);
        writer.write(join(codexDir, 'doc-sync.md'), [`<!-- setup-agents: ${PLUGIN_VERSION} -->`, '', blockContent].join('\n'));
    }
    if (tools.includes('vscode')) {
        const skillsDir = join(cwd, '.github', 'skills');
        ensureDir(skillsDir);
        writer.write(join(skillsDir, 'doc-sync.md'), skillContent);
    }
    if (tools.includes('windsurf')) {
        const windsurfDir = join(cwd, '.windsurf', 'skills');
        ensureDir(windsurfDir);
        writer.write(join(windsurfDir, 'doc-sync.md'), [`<!-- setup-agents: ${PLUGIN_VERSION} -->`, '', blockContent].join('\n'));
    }
    if (tools.includes('jetbrains')) {
        const promptsDir = join(cwd, '.idea', 'ai-prompts');
        ensureDir(promptsDir);
        writer.write(join(promptsDir, 'doc-sync.md'), [`<!-- setup-agents: ${PLUGIN_VERSION} -->`, '', blockContent].join('\n'));
    }
}
function writeProjectKnowledgeTemplates(cwd) {
    if (!projectKnowledgeExists(cwd)) {
        writeProjectKnowledge(cwd, generateProjectKnowledgeTemplate());
    }
    if (!docIndexExists(cwd)) {
        writeDocIndex(cwd, generateDocIndexTemplate());
    }
    if (!classCatalogExists(cwd)) {
        writeClassCatalog(cwd, generateClassCatalogTemplate());
    }
}
function writePlaybooks(cwd, writer) {
    const playbooksDir = join(cwd, PLAYBOOKS_DIR);
    ensureDir(playbooksDir);
    const playbooks = generateDefaultPlaybooks(PLUGIN_VERSION);
    for (const [filename, content] of Object.entries(playbooks)) {
        const fullPath = join(playbooksDir, filename);
        writer.write(fullPath, content);
    }
}
function writePermissions(cwd, profiles, writer) {
    const allCaps = new Set(profiles.flatMap((p) => p.capabilities));
    const content = generatePermissionsContent([...allCaps], PLUGIN_VERSION);
    const permissionsPath = join(cwd, '.setup-agents', 'permissions.md');
    ensureDir(join(cwd, '.setup-agents'));
    writer.write(permissionsPath, content);
}
function writeEnvAudit(cwd, writer) {
    const content = generateEnvAudit(cwd);
    const auditPath = join(cwd, '.setup-agents', 'env-audit.md');
    ensureDir(join(cwd, '.setup-agents'));
    writer.write(auditPath, content);
}
async function syncProfileDocsIfAvailable(cwd, profiles, log) {
    const syncable = profiles.filter((p) => listSyncableProfiles().includes(p.id));
    if (syncable.length === 0)
        return;
    const docsDir = join(cwd, '.setup-agents', 'docs');
    const toSync = syncable.filter((profile) => {
        const profileDocsDir = join(docsDir, profile.id);
        return !existsSync(profileDocsDir) || !readdirSync(profileDocsDir).some((f) => f.endsWith('.md'));
    });
    const results = await Promise.allSettled(toSync.map(async (profile) => {
        log(`Syncing docs for profile "${profile.id}"...`);
        const result = await syncProfileDocs(cwd, profile.id, { maxPagesPerSource: 5 });
        if (result.synced > 0)
            log(`  Synced ${result.synced} doc source(s) → ${result.files.length} files`);
        if (result.errors.length > 0)
            log(`  Warnings: ${result.errors.join(', ')}`);
        return result;
    }));
    for (let i = 0; i < results.length; i++) {
        if (results[i].status === 'rejected') {
            log(`  Skipped docs sync for "${toSync[i].id}" (network unavailable)`);
        }
    }
}
//# sourceMappingURL=local.js.map