import path from 'path';
import { FileSystemUtils } from './file-system.js';
import { writeChangeMetadata, validateSchemaName } from './change-metadata.js';
import { readProjectConfig } from '../core/project-config.js';
const DEFAULT_SCHEMA = 'spec-driven';
/**
 * Validates that a change name follows kebab-case conventions.
 *
 * Valid names:
 * - Start with a lowercase letter
 * - Contain only lowercase letters, numbers, and hyphens
 * - Do not start or end with a hyphen
 * - Do not contain consecutive hyphens
 *
 * @param name - The change name to validate
 * @returns Validation result with `valid: true` or `valid: false` with an error message
 *
 * @example
 * validateChangeName('add-auth') // { valid: true }
 * validateChangeName('Add-Auth') // { valid: false, error: '...' }
 */
export function validateChangeName(name) {
    // Pattern: starts with lowercase letter, followed by lowercase letters/numbers,
    // optionally followed by hyphen + lowercase letters/numbers (repeatable)
    const kebabCasePattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
    if (!name) {
        return { valid: false, error: 'Change name cannot be empty' };
    }
    if (!kebabCasePattern.test(name)) {
        // Provide specific error messages for common mistakes
        if (/[A-Z]/.test(name)) {
            return { valid: false, error: 'Change name must be lowercase (use kebab-case)' };
        }
        if (/\s/.test(name)) {
            return { valid: false, error: 'Change name cannot contain spaces (use hyphens instead)' };
        }
        if (/_/.test(name)) {
            return { valid: false, error: 'Change name cannot contain underscores (use hyphens instead)' };
        }
        if (name.startsWith('-')) {
            return { valid: false, error: 'Change name cannot start with a hyphen' };
        }
        if (name.endsWith('-')) {
            return { valid: false, error: 'Change name cannot end with a hyphen' };
        }
        if (/--/.test(name)) {
            return { valid: false, error: 'Change name cannot contain consecutive hyphens' };
        }
        if (/[^a-z0-9-]/.test(name)) {
            return { valid: false, error: 'Change name can only contain lowercase letters, numbers, and hyphens' };
        }
        if (/^[0-9]/.test(name)) {
            return { valid: false, error: 'Change name must start with a letter' };
        }
        return { valid: false, error: 'Change name must follow kebab-case convention (e.g., add-auth, refactor-db)' };
    }
    return { valid: true };
}
/**
 * Creates a new change directory with metadata file.
 *
 * @param projectRoot - The root directory of the project (where `openspec/` lives)
 * @param name - The change name (must be valid kebab-case)
 * @param options - Optional settings for the change
 * @throws Error if the change name is invalid
 * @throws Error if the schema name is invalid
 * @throws Error if the change directory already exists
 *
 * @returns Result containing the resolved schema name
 *
 * @example
 * // Creates openspec/changes/add-auth/ with default schema
 * const result = await createChange('/path/to/project', 'add-auth')
 * console.log(result.schema) // 'spec-driven' or value from config
 *
 * @example
 * // Creates openspec/changes/add-auth/ with custom schema
 * const result = await createChange('/path/to/project', 'add-auth', { schema: 'my-workflow' })
 * console.log(result.schema) // 'my-workflow'
 */
export async function createChange(projectRoot, name, options = {}) {
    // Validate the name first
    const validation = validateChangeName(name);
    if (!validation.valid) {
        throw new Error(validation.error);
    }
    const defaultSchema = options.defaultSchema ?? DEFAULT_SCHEMA;
    // Determine schema: explicit option → project config → supplied default
    let schemaName;
    if (options.schema) {
        schemaName = options.schema;
    }
    else {
        // Try to read from project config
        try {
            const config = readProjectConfig(projectRoot);
            schemaName = config?.schema ?? defaultSchema;
        }
        catch {
            // If config read fails, use default
            schemaName = defaultSchema;
        }
    }
    // Validate the resolved schema
    validateSchemaName(schemaName, projectRoot);
    // Build the change directory path
    const changeDir = path.join(options.changesDir ?? path.join(projectRoot, 'openspec', 'changes'), name);
    // Check if change already exists
    if (await FileSystemUtils.directoryExists(changeDir)) {
        throw new Error(`Change '${name}' already exists at ${changeDir}`);
    }
    // Creating a change may scaffold or complete the root itself (an
    // implicit root, or a config-only/incomplete clone). Never leave a
    // half-root behind that doctor immediately calls unhealthy: ensure
    // specs/ and changes/archive/ exist, and write a config only when
    // none exists. The config records the PROJECT default schema, never
    // a one-change --schema override.
    const openspecDir = path.join(projectRoot, 'openspec');
    // Create the directory (including parent directories if needed)
    await FileSystemUtils.createDirectory(changeDir);
    await FileSystemUtils.createDirectory(path.join(openspecDir, 'specs'));
    await FileSystemUtils.createDirectory(path.join(openspecDir, 'changes', 'archive'));
    const configPath = path.join(openspecDir, 'config.yaml');
    const configYmlPath = path.join(openspecDir, 'config.yml');
    if (!(await FileSystemUtils.fileExists(configPath)) &&
        !(await FileSystemUtils.fileExists(configYmlPath))) {
        await FileSystemUtils.writeFile(configPath, `schema: ${defaultSchema}\n`);
    }
    // Write metadata file with schema and creation date
    const today = new Date().toISOString().split('T')[0];
    writeChangeMetadata(changeDir, {
        schema: schemaName,
        created: today,
        ...options.metadata,
    }, projectRoot);
    return { schema: schemaName, changeDir };
}
//# sourceMappingURL=change-utils.js.map