/**
 * Shared settings.local.json utilities for init, update, and doctor commands
 * Handles merging required hooks into existing settings
 */
import { readFileSync, writeFileSync, readdirSync, statSync, lstatSync, unlinkSync, symlinkSync, renameSync } from 'fs';
import { join } from 'path';
import fsExtra from 'fs-extra';
const { ensureDirSync } = fsExtra;
import { logger } from './logger.js';
import { pathExists } from './files.js';
/**
 * Get list of installed skill names from the skills directory
 */
export function getInstalledSkillNames(projectRoot) {
    const skillsDir = join(projectRoot, '.claude/skills');
    if (!pathExists(skillsDir)) {
        return [];
    }
    try {
        const entries = readdirSync(skillsDir);
        return entries.filter((entry) => {
            if (entry.startsWith('.'))
                return false;
            const entryPath = join(skillsDir, entry);
            try {
                const stats = statSync(entryPath);
                return stats.isDirectory();
            }
            catch {
                return false;
            }
        });
    }
    catch {
        return [];
    }
}
/**
 * Legacy statusline paths that should be migrated
 */
const LEGACY_STATUSLINE_PATHS = [
    '.claude/core/statusline.sh',
    '.claude/statusline.sh',
    '.claude/pennyfarthing/statusline.sh',
    '.claude/pennyfarthing/scripts/statusline.sh',
    '.claude/scripts/statusline.sh',
    '.pennyfarthing/scripts/statusline.sh'
];
/**
 * Legacy script paths that should be migrated to .pennyfarthing/scripts/
 */
const LEGACY_SCRIPT_PATHS = [
    '.claude/pennyfarthing/scripts/',
    '.claude/scripts/'
];
/**
 * Check if a hook entry contains a specific hook by command substring
 */
function hookEntryContains(entry, substring) {
    if (typeof entry === 'object' && entry !== null) {
        const hookEntry = entry;
        return hookEntry.hooks?.some(h => h.command?.includes(substring)) ?? false;
    }
    return false;
}
/**
 * Find a hook entry by command substring
 */
function findHookEntry(hookArray, substring) {
    return hookArray.find((entry) => hookEntryContains(entry, substring));
}
/**
 * Migrate hook paths from legacy locations to .pennyfarthing/scripts/
 */
function migrateHookPaths(hookArray) {
    let migrated = false;
    for (const entry of hookArray) {
        if (typeof entry === 'object' && entry !== null) {
            const hookEntry = entry;
            if (hookEntry.hooks) {
                for (const h of hookEntry.hooks) {
                    if (h.command) {
                        for (const legacyPath of LEGACY_SCRIPT_PATHS) {
                            if (h.command.includes(legacyPath)) {
                                h.command = h.command.replace(legacyPath, '.pennyfarthing/scripts/');
                                migrated = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    return migrated;
}
/**
 * Merge required hooks into existing settings.local.json
 * This ensures critical hooks are always configured
 *
 * @param projectRoot - Project root directory
 * @param assetsPath - Path to pennyfarthing-dist assets
 * @param options - Options including dryRun and registerSkills
 * @returns true if any changes were made
 */
export async function mergeSettingsLocalJson(projectRoot, assetsPath, options) {
    const settingsPath = join(projectRoot, '.pennyfarthing/settings.local.json');
    const templatePath = join(assetsPath, 'templates/settings.local.json.template');
    if (!pathExists(templatePath)) {
        logger.warning('settings.local.json template not found');
        return false;
    }
    const templateContent = JSON.parse(readFileSync(templatePath, 'utf8'));
    // Get installed skills to register in permissions
    const installedSkills = options.registerSkills ? getInstalledSkillNames(projectRoot) : [];
    // If no existing settings, create from template with all installed skills
    if (!pathExists(settingsPath)) {
        if (installedSkills.length > 0) {
            const permissions = templateContent.permissions?.allow || [];
            for (const skill of installedSkills) {
                const skillPermission = `Skill(${skill})`;
                if (!permissions.includes(skillPermission)) {
                    permissions.push(skillPermission);
                }
            }
            templateContent.permissions = { ...templateContent.permissions, allow: permissions };
        }
        if (!options.dryRun) {
            ensureDirSync(join(projectRoot, '.pennyfarthing'));
            writeFileSync(settingsPath, JSON.stringify(templateContent, null, 2), 'utf8');
        }
        logger.created('.pennyfarthing/settings.local.json');
        if (installedSkills.length > 0) {
            logger.info(`  Registered ${installedSkills.length} skills in permissions`);
        }
        return true;
    }
    // Read existing settings
    let existingSettings;
    try {
        existingSettings = JSON.parse(readFileSync(settingsPath, 'utf8'));
    }
    catch {
        logger.warning('Could not parse existing settings.local.json, skipping merge');
        return false;
    }
    let modified = false;
    // Ensure hooks object exists
    if (!existingSettings.hooks) {
        existingSettings.hooks = {};
        modified = true;
    }
    const hooks = existingSettings.hooks;
    // Merge SessionStart hooks - critical for PROJECT_ROOT
    if (!hooks.SessionStart) {
        hooks.SessionStart = templateContent.hooks?.SessionStart || [];
        modified = true;
        logger.info('Added missing SessionStart hooks');
    }
    else if (Array.isArray(hooks.SessionStart)) {
        const hasSessionStartHook = hooks.SessionStart.some((entry) => hookEntryContains(entry, 'session-start.sh'));
        if (!hasSessionStartHook && templateContent.hooks?.SessionStart) {
            const sessionStartEntry = findHookEntry(templateContent.hooks.SessionStart, 'session-start.sh');
            if (sessionStartEntry) {
                hooks.SessionStart = [sessionStartEntry, ...hooks.SessionStart];
                modified = true;
                logger.info('Added missing session-start.sh hook');
            }
        }
    }
    // Merge SessionEnd hooks if missing
    if (!hooks.SessionEnd && templateContent.hooks?.SessionEnd) {
        hooks.SessionEnd = templateContent.hooks.SessionEnd;
        modified = true;
        logger.info('Added missing SessionEnd hooks');
    }
    // Merge Stop hooks if missing (question reflector enforcement)
    if (!hooks.Stop && templateContent.hooks?.Stop) {
        hooks.Stop = templateContent.hooks.Stop;
        modified = true;
        logger.info('Added missing Stop hooks');
    }
    else if (Array.isArray(hooks.Stop)) {
        const hasReflectorHook = hooks.Stop.some((entry) => hookEntryContains(entry, 'question-reflector-check'));
        if (!hasReflectorHook && templateContent.hooks?.Stop) {
            const reflectorEntry = findHookEntry(templateContent.hooks.Stop, 'question-reflector-check');
            if (reflectorEntry) {
                hooks.Stop = [reflectorEntry, ...hooks.Stop];
                modified = true;
                logger.info('Added missing question-reflector-check hook');
            }
        }
    }
    // Merge PostToolUse hooks if missing (bell mode)
    if (!hooks.PostToolUse && templateContent.hooks?.PostToolUse) {
        hooks.PostToolUse = templateContent.hooks.PostToolUse;
        modified = true;
        logger.info('Added missing PostToolUse hooks');
    }
    else if (Array.isArray(hooks.PostToolUse)) {
        const hasBellModeHook = hooks.PostToolUse.some((entry) => hookEntryContains(entry, 'bell-mode-hook'));
        if (!hasBellModeHook && templateContent.hooks?.PostToolUse) {
            const bellModeEntry = findHookEntry(templateContent.hooks.PostToolUse, 'bell-mode-hook');
            if (bellModeEntry) {
                hooks.PostToolUse = [bellModeEntry, ...hooks.PostToolUse];
                modified = true;
                logger.info('Added missing bell-mode-hook');
            }
        }
    }
    // Merge PreToolUse hooks - ensure all required hooks are configured
    if (!hooks.PreToolUse && templateContent.hooks?.PreToolUse) {
        hooks.PreToolUse = templateContent.hooks.PreToolUse;
        modified = true;
        logger.info('Added missing PreToolUse hooks');
    }
    else if (Array.isArray(hooks.PreToolUse)) {
        // Check for context-circuit-breaker
        const hasCircuitBreaker = hooks.PreToolUse.some((entry) => hookEntryContains(entry, 'context-circuit-breaker'));
        if (!hasCircuitBreaker && templateContent.hooks?.PreToolUse) {
            const circuitBreakerEntry = findHookEntry(templateContent.hooks.PreToolUse, 'context-circuit-breaker');
            if (circuitBreakerEntry) {
                hooks.PreToolUse = [...hooks.PreToolUse, circuitBreakerEntry];
                modified = true;
                logger.info('Added missing context-circuit-breaker hook');
            }
        }
        // Check for schema-validation hook
        const hasSchemaValidation = hooks.PreToolUse.some((entry) => hookEntryContains(entry, 'schema-validation'));
        if (!hasSchemaValidation && templateContent.hooks?.PreToolUse) {
            const schemaValidationEntry = findHookEntry(templateContent.hooks.PreToolUse, 'schema-validation');
            if (schemaValidationEntry) {
                hooks.PreToolUse = [...hooks.PreToolUse, schemaValidationEntry];
                modified = true;
                logger.info('Added missing schema-validation hook');
            }
        }
    }
    // Ensure statusLine is configured and points to new location
    const statusLine = existingSettings.statusLine;
    if (!statusLine) {
        existingSettings.statusLine = templateContent.statusLine;
        modified = true;
        logger.info('Added missing statusLine configuration');
    }
    else if (statusLine.command && typeof statusLine.command === 'string') {
        // Migrate from any legacy path to new path
        for (const legacyPath of LEGACY_STATUSLINE_PATHS) {
            if (statusLine.command.includes(legacyPath)) {
                statusLine.command = statusLine.command.replace(legacyPath, '.pennyfarthing/scripts/misc/statusline.sh');
                modified = true;
                logger.info(`Updated statusLine path from ${legacyPath} to new location`);
                break;
            }
        }
    }
    // Migrate hook paths from legacy locations to .pennyfarthing/scripts/
    for (const hookType of ['SessionStart', 'SessionEnd', 'PreToolUse', 'PostToolUse', 'Stop']) {
        if (Array.isArray(hooks[hookType])) {
            if (migrateHookPaths(hooks[hookType])) {
                modified = true;
                logger.info(`Migrated ${hookType} hook paths to new location`);
            }
        }
    }
    // Merge skill permissions - ensure all installed skills are registered
    if (installedSkills.length > 0) {
        if (!existingSettings.permissions) {
            existingSettings.permissions = { allow: [] };
            modified = true;
        }
        const permissions = existingSettings.permissions;
        if (!permissions.allow) {
            permissions.allow = [];
            modified = true;
        }
        const allowList = permissions.allow;
        let skillsAdded = 0;
        for (const skill of installedSkills) {
            const skillPermission = `Skill(${skill})`;
            if (!allowList.includes(skillPermission)) {
                allowList.push(skillPermission);
                skillsAdded++;
                modified = true;
            }
        }
        if (skillsAdded > 0) {
            logger.info(`Registered ${skillsAdded} missing skills in permissions`);
        }
    }
    if (modified && !options.dryRun) {
        writeFileSync(settingsPath, JSON.stringify(existingSettings, null, 2), 'utf8');
        logger.updated('.pennyfarthing/settings.local.json');
    }
    else if (!modified) {
        logger.skipped('.pennyfarthing/settings.local.json', 'already configured');
    }
    return modified;
}
/**
 * Create a symlink at .claude/settings.local.json pointing to .pennyfarthing/settings.local.json
 * Idempotent — no-op if correct symlink already exists.
 */
export function ensureSettingsSymlink(projectRoot) {
    const symlinkPath = join(projectRoot, '.claude/settings.local.json');
    const relativeTarget = '../.pennyfarthing/settings.local.json';
    // Already a symlink — nothing to do
    try {
        if (lstatSync(symlinkPath).isSymbolicLink()) {
            return;
        }
    }
    catch {
        // Doesn't exist yet — continue to create
    }
    ensureDirSync(join(projectRoot, '.claude'));
    symlinkSync(relativeTarget, symlinkPath);
}
/**
 * Migrate settings.local.json from .claude/ (old) to .pennyfarthing/ (new).
 * If .claude/settings.local.json is a regular file, move it to .pennyfarthing/
 * and replace with a symlink. Idempotent — no-op if already migrated or nothing exists.
 */
export function migrateSettingsFile(projectRoot) {
    const oldPath = join(projectRoot, '.claude/settings.local.json');
    const newPath = join(projectRoot, '.pennyfarthing/settings.local.json');
    // Check if old path exists at all
    let oldStats;
    try {
        oldStats = lstatSync(oldPath);
    }
    catch {
        // Old path doesn't exist — nothing to migrate
        return;
    }
    // Already a symlink — already migrated
    if (oldStats.isSymbolicLink()) {
        return;
    }
    // Old path is a real file — migrate it
    if (!pathExists(newPath)) {
        // No file at new location — move the old one there
        ensureDirSync(join(projectRoot, '.pennyfarthing'));
        renameSync(oldPath, newPath);
    }
    else {
        // New location already has a file — just remove the old one
        unlinkSync(oldPath);
    }
    // Create symlink at old location pointing to new
    symlinkSync('../.pennyfarthing/settings.local.json', oldPath);
}
//# sourceMappingURL=settings.js.map