/*
 * Elastic License 2.0
 * Copyright (C) 2025–present Raman Marozau, Work Target Insight Function. All rights reserved.
 * Contact: raman@worktif.com
 *
 * This file is part of the Licensed Work: worktif.runtime.cdk <worktif_runtime_cdk>.
 * Use of this software is governed by the Elastic License 2.0; see the LICENSE file
 * or https://www.elastic.co/licensing/elastic-license for details.
 *
 * SPDX-License-Identifier: Elastic-2.0
 */

/**
 * Property-based tests for CDK Templates Framework Interface Usage
 *
 * Feature: cdk-templates-best-practices, Property 4: Framework Interface Usage
 * **Validates: Requirements 2.1, 2.2, 3.1, 4.1, 5.1**
 *
 * This test verifies that generated `lib/infra-stack.ts` template files contain
 * the required framework interface calls:
 * - createInfrastructureContext() with infraStack parameter
 * - createResourceFacade() function call
 * - createStackIntegration() or createExtendedStackIntegration() function call
 * - Access to context.factories for resource creation
 */

import { describe, expect, it, beforeAll } from '@jest/globals';
import * as fc from 'fast-check';
import * as fs from 'fs';
import * as path from 'path';

/**
 * Template types available in the codebase.
 */
type TemplateType = 'basic' | 'advanced';

/**
 * Represents the result of reading a template file.
 */
interface TemplateReadResult {
    exists: boolean;
    content: string;
    templateType: TemplateType;
    filePath: string;
}

/**
 * Reads a template file for a given template type and file name.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @param directory - The directory ('lib' or 'bin')
 * @param fileName - The file name (e.g., 'infra-stack.ts.template')
 * @returns The template read result with content and metadata
 */
function readTemplateFile(
    templateType: TemplateType,
    directory: 'lib' | 'bin',
    fileName: string
): TemplateReadResult {
    const templatePath = path.join(
        __dirname,
        '..',
        templateType,
        'cdk',
        directory,
        fileName
    );

    const exists = fs.existsSync(templatePath);
    const content = exists ? fs.readFileSync(templatePath, 'utf-8') : '';

    return {
        exists,
        content,
        templateType,
        filePath: templatePath,
    };
}

/**
 * Reads the infra-stack.ts.template file for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns The template read result with content and metadata
 */
function readInfraStackTemplate(templateType: TemplateType): TemplateReadResult {
    return readTemplateFile(templateType, 'lib', 'infra-stack.ts.template');
}

/**
 * Reads the app.ts.template file for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns The template read result with content and metadata
 */
function readAppTemplate(templateType: TemplateType): TemplateReadResult {
    return readTemplateFile(templateType, 'bin', 'app.ts.template');
}

/**
 * Gets all available template types that have infra-stack.ts.template files.
 *
 * @returns Array of template types with existing infra-stack templates
 */
function getAvailableTemplateTypes(): TemplateType[] {
    const templateTypes: TemplateType[] = ['basic', 'advanced'];
    return templateTypes.filter(type => readInfraStackTemplate(type).exists);
}

/**
 * Gets all available template types that have app.ts.template files.
 *
 * @returns Array of template types with existing app templates
 */
function getAvailableAppTemplateTypes(): TemplateType[] {
    const templateTypes: TemplateType[] = ['basic', 'advanced'];
    return templateTypes.filter(type => readAppTemplate(type).exists);
}

/**
 * Reads the tsconfig.json.template file for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns The template read result with content and metadata
 */
function readTsConfigTemplate(templateType: TemplateType): TemplateReadResult {
    const templatePath = path.join(
        __dirname,
        '..',
        templateType,
        'cdk',
        'tsconfig.json.template'
    );

    const exists = fs.existsSync(templatePath);
    const content = exists ? fs.readFileSync(templatePath, 'utf-8') : '';

    return {
        exists,
        content,
        templateType,
        filePath: templatePath,
    };
}

/**
 * Gets all available template types that have tsconfig.json.template files.
 *
 * @returns Array of template types with existing tsconfig templates
 */
function getAvailableTsConfigTemplateTypes(): TemplateType[] {
    const templateTypes: TemplateType[] = ['basic', 'advanced'];
    return templateTypes.filter(type => readTsConfigTemplate(type).exists);
}

/**
 * Reads the config.ts.template file for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns The template read result with content and metadata
 */
function readConfigTemplate(templateType: TemplateType): TemplateReadResult {
    return readTemplateFile(templateType, 'lib', 'config.ts.template');
}

/**
 * Gets all available template types that have config.ts.template files.
 *
 * @returns Array of template types with existing config templates
 */
function getAvailableConfigTemplateTypes(): TemplateType[] {
    const templateTypes: TemplateType[] = ['basic', 'advanced'];
    return templateTypes.filter(type => readConfigTemplate(type).exists);
}

/**
 * Gets all lib/ files for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns Array of file names in the lib/ directory
 */
function getLibFiles(templateType: TemplateType): string[] {
    const libPath = path.join(__dirname, '..', templateType, 'cdk', 'lib');
    if (!fs.existsSync(libPath)) {
        return [];
    }
    return fs.readdirSync(libPath).filter(file => file.endsWith('.template'));
}

/**
 * Gets all bin/ files for a given template type.
 *
 * @param templateType - The template type ('basic' or 'advanced')
 * @returns Array of file names in the bin/ directory
 */
function getBinFiles(templateType: TemplateType): string[] {
    const binPath = path.join(__dirname, '..', templateType, 'cdk', 'bin');
    if (!fs.existsSync(binPath)) {
        return [];
    }
    return fs.readdirSync(binPath).filter(file => file.endsWith('.template'));
}

/**
 * Counts non-empty, non-comment, non-import lines in TypeScript code.
 *
 * @param content - The TypeScript file content
 * @returns The number of code lines (excluding comments, imports, and empty lines)
 */
function countCodeLines(content: string): number {
    const lines = content.split('\n');
    let codeLineCount = 0;
    let inMultiLineComment = false;

    for (const line of lines) {
        const trimmedLine = line.trim();

        // Handle multi-line comments
        if (inMultiLineComment) {
            if (trimmedLine.includes('*/')) {
                inMultiLineComment = false;
            }
            continue;
        }

        // Skip empty lines
        if (trimmedLine === '') {
            continue;
        }

        // Skip single-line comments
        if (trimmedLine.startsWith('//')) {
            continue;
        }

        // Skip multi-line comment start
        if (trimmedLine.startsWith('/*')) {
            if (!trimmedLine.includes('*/')) {
                inMultiLineComment = true;
            }
            continue;
        }

        // Skip import statements
        if (trimmedLine.startsWith('import ')) {
            continue;
        }

        // Skip shebang
        if (trimmedLine.startsWith('#!')) {
            continue;
        }

        // Count this as a code line
        codeLineCount++;
    }

    return codeLineCount;
}

describe('CDK Templates Best Practices', () => {
    describe('Property 4: Framework Interface Usage', () => {
        let availableTemplates: TemplateType[];

        beforeAll(() => {
            availableTemplates = getAvailableTemplateTypes();
        });

        /**
         * Feature: cdk-templates-best-practices, Property 4: Framework Interface Usage
         * **Validates: Requirements 2.1, 2.2, 3.1, 4.1, 5.1**
         *
         * For any generated lib/infra-stack.ts file, the code SHALL contain:
         * - createInfrastructureContext() function call with infraStack parameter
         * - createResourceFacade() function call
         * - createStackIntegration() or createExtendedStackIntegration() function call
         * - Access to context.factories for resource creation
         */
        it('should contain required framework interface calls for any template type', () => {
            // Skip if no templates are available
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);

                        // Template must exist
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Requirement 2.1: createInfrastructureContext() with infraStack parameter
                        // The function must be called with an object containing infraStack
                        expect(content).toContain('createInfrastructureContext');
                        expect(content).toMatch(/createInfrastructureContext\s*\(\s*\{[^}]*infraStack/s);

                        // Requirement 3.1: createResourceFacade() function call
                        expect(content).toContain('createResourceFacade');
                        expect(content).toMatch(/createResourceFacade\s*\(/);

                        // Requirement 5.1: createStackIntegration() or createExtendedStackIntegration()
                        expect(content).toMatch(/create(Extended)?StackIntegration\s*\(/);

                        // Requirement 4.1: Access to context.factories for resource creation
                        // This can be in comments or actual code
                        expect(content).toContain('context.factories');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that createInfrastructureContext is imported from the correct module.
         * **Validates: Requirements 2.1, 14.3**
         */
        it('should import createInfrastructureContext from @worktif/runtime/infra', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify import from correct module
                        expect(result.content).toMatch(
                            /import\s*\{[^}]*createInfrastructureContext[^}]*\}\s*from\s*['"]@worktif\/runtime\/infra['"]/s
                        );

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that createResourceFacade is imported from the correct module.
         * **Validates: Requirements 3.1, 14.3**
         */
        it('should import createResourceFacade from @worktif/runtime/infra', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify import from correct module
                        expect(result.content).toMatch(
                            /import\s*\{[^}]*createResourceFacade[^}]*\}\s*from\s*['"]@worktif\/runtime\/infra['"]/s
                        );

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that stack integration is imported from the correct module.
         * **Validates: Requirements 5.1, 14.3**
         */
        it('should import stack integration from @worktif/runtime/infra', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify import from correct module (either createStackIntegration or createExtendedStackIntegration)
                        expect(result.content).toMatch(
                            /import\s*\{[^}]*create(Extended)?StackIntegration[^}]*\}\s*from\s*['"]@worktif\/runtime\/infra['"]/s
                        );

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the InfrastructureContext is used as the aggregate root.
         * **Validates: Requirements 2.2, 2.3, 2.4, 2.5, 2.6**
         */
        it('should use InfrastructureContext as aggregate root with proper access patterns', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Requirement 2.2: context.factories access
                        expect(content).toContain('context.factories');

                        // Requirement 2.5: context.observers access (for auto-grant/auto-inject)
                        expect(content).toContain('context.observers');

                        // Requirement 2.6: context.decorators access (for cross-cutting concerns)
                        expect(content).toContain('context.decorators');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that decorators are registered for cross-cutting concerns.
         * **Validates: Requirements 7.1**
         */
        it('should demonstrate decorator registration for cross-cutting concerns', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Requirement 7.1: TaggingDecorator for consistent resource tagging
                        expect(content).toContain('TaggingDecorator');
                        expect(content).toMatch(/context\.decorators\.register\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that observers are registered for auto-grant functionality.
         * **Validates: Requirements 8.1**
         */
        it('should demonstrate observer registration for auto-grant', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Requirement 8.1: AutoGrantObserver for automatic IAM permission granting
                        expect(content).toContain('AutoGrantObserver');
                        expect(content).toMatch(/context\.observers\.register\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the template exports a createInfraStack function.
         * **Validates: Requirements 9.5**
         */
        it('should export createInfraStack function', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify export of createInfraStack function
                        expect(result.content).toMatch(/export\s+function\s+createInfraStack\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the template imports configuration from ./config.
         * **Validates: Requirements 14.2**
         */
        it('should import configuration from ./config', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify import from ./config
                        expect(result.content).toMatch(/import\s*.*\s*from\s*['"]\.\/config['"]/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that RuntimeInfraStack is imported and used.
         * **Validates: Requirements 2.1**
         */
        it('should import and instantiate RuntimeInfraStack', () => {
            if (availableTemplates.length === 0) {
                console.warn('No infra-stack.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTemplates),
                    (templateType: TemplateType) => {
                        const result = readInfraStackTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify RuntimeInfraStack import
                        expect(content).toMatch(
                            /import\s*\{[^}]*RuntimeInfraStack[^}]*\}\s*from\s*['"]@worktif\/runtime\/infra['"]/s
                        );

                        // Verify RuntimeInfraStack instantiation
                        expect(content).toMatch(/new\s+RuntimeInfraStack\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 5: Entry Point Minimization', () => {
        let availableAppTemplates: TemplateType[];

        beforeAll(() => {
            availableAppTemplates = getAvailableAppTemplateTypes();
        });

        /**
         * Feature: cdk-templates-best-practices, Property 5: Entry Point Minimization
         * **Validates: Requirements 1.3, 1.5**
         *
         * For any generated bin/app.ts file, the code (excluding comments and import statements)
         * SHALL NOT exceed 50 lines, and SHALL only contain:
         * - CDK App instantiation (new cdk.App())
         * - Stage resolution from context or environment
         * - Stack instantiation by calling imported functions
         * - app.synth() call
         */
        it('should not exceed 50 lines of code excluding comments and imports', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const codeLines = countCodeLines(result.content);

                        // Requirement 1.5: Entry point SHALL NOT exceed 50 lines
                        expect(codeLines).toBeLessThanOrEqual(50);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point contains CDK App instantiation.
         * **Validates: Requirements 1.3**
         */
        it('should contain CDK App instantiation', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify CDK App instantiation
                        expect(result.content).toMatch(/new\s+cdk\.App\s*\(\s*\)/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point contains stage resolution.
         * **Validates: Requirements 1.3**
         */
        it('should contain stage resolution from context or environment', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify stage resolution (either from context or environment)
                        expect(result.content).toMatch(/app\.node\.tryGetContext\s*\(\s*['"]stage['"]\s*\)/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point calls app.synth().
         * **Validates: Requirements 1.3**
         */
        it('should call app.synth() at the end', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify app.synth() call
                        expect(result.content).toMatch(/app\.synth\s*\(\s*\)/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point imports stack configurations from ../lib/.
         * **Validates: Requirements 1.4, 14.1**
         */
        it('should import stack configurations from ../lib/', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify imports from ../lib/
                        expect(content).toMatch(/import\s*.*\s*from\s*['"]\.\.\/lib\/config['"]/);
                        expect(content).toMatch(/import\s*.*\s*from\s*['"]\.\.\/lib\/infra-stack['"]/);
                        expect(content).toMatch(/import\s*.*\s*from\s*['"]\.\.\/lib\/runtime-stack['"]/);
                        expect(content).toMatch(/import\s*.*\s*from\s*['"]\.\.\/lib\/web-stack['"]/);
                        expect(content).toMatch(/import\s*.*\s*from\s*['"]\.\.\/lib\/awake-stack['"]/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point calls stack creation functions.
         * **Validates: Requirements 1.3**
         */
        it('should call stack creation functions', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify stack creation function calls
                        expect(content).toMatch(/createInfraStack\s*\(/);
                        expect(content).toMatch(/createRuntimeStack\s*\(/);
                        expect(content).toMatch(/createWebStack\s*\(/);
                        expect(content).toMatch(/createAwakeStack\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that the entry point does NOT directly instantiate stacks.
         * **Validates: Requirements 1.3**
         */
        it('should NOT directly instantiate RuntimeInfraStack, RuntimeStack, or RuntimeWebStack', () => {
            if (availableAppTemplates.length === 0) {
                console.warn('No app.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableAppTemplates),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify NO direct stack instantiation (should use create* functions instead)
                        expect(content).not.toMatch(/new\s+RuntimeInfraStack\s*\(/);
                        expect(content).not.toMatch(/new\s+RuntimeStack\s*\(/);
                        expect(content).not.toMatch(/new\s+RuntimeWebStack\s*\(/);
                        expect(content).not.toMatch(/new\s+RuntimeAwakeStack\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 6: TypeScript Configuration', () => {
        let availableTsConfigTemplates: TemplateType[];

        beforeAll(() => {
            availableTsConfigTemplates = getAvailableTsConfigTemplateTypes();
        });

        /**
         * Feature: cdk-templates-best-practices, Property 6: TypeScript Configuration
         * **Validates: Requirements 11.1, 11.2, 11.3**
         *
         * For any generated tsconfig.json file, the include array SHALL contain
         * both bin and lib directories, and all existing compiler options
         * SHALL be preserved.
         */
        it('should include both bin/**/*.ts and lib/**/*.ts in include array', () => {
            if (availableTsConfigTemplates.length === 0) {
                console.warn('No tsconfig.json.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTsConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readTsConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Parse JSON content
                        const tsconfig = JSON.parse(result.content);

                        // Requirement 11.1: lib/**/*.ts in include array
                        expect(tsconfig.include).toContain('lib/**/*.ts');

                        // Requirement 11.2: bin/**/*.ts in include array
                        expect(tsconfig.include).toContain('bin/**/*.ts');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that existing compiler options are preserved.
         * **Validates: Requirements 11.3**
         */
        it('should preserve existing compiler options', () => {
            if (availableTsConfigTemplates.length === 0) {
                console.warn('No tsconfig.json.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTsConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readTsConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Parse JSON content
                        const tsconfig = JSON.parse(result.content);

                        // Verify essential compiler options are preserved
                        expect(tsconfig.compilerOptions).toBeDefined();
                        expect(tsconfig.compilerOptions.target).toBe('ES2020');
                        expect(tsconfig.compilerOptions.module).toBe('commonjs');
                        expect(tsconfig.compilerOptions.strict).toBe(true);
                        expect(tsconfig.compilerOptions.experimentalDecorators).toBe(true);
                        expect(tsconfig.compilerOptions.esModuleInterop).toBe(true);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that exclude array contains node_modules and cdk.out.
         * **Validates: Requirements 11.3**
         */
        it('should exclude node_modules and cdk.out', () => {
            if (availableTsConfigTemplates.length === 0) {
                console.warn('No tsconfig.json.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableTsConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readTsConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Parse JSON content
                        const tsconfig = JSON.parse(result.content);

                        // Verify exclude array
                        expect(tsconfig.exclude).toContain('node_modules');
                        expect(tsconfig.exclude).toContain('cdk.out');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 1: Directory and File Structure', () => {
        /**
         * Feature: cdk-templates-best-practices, Property 1: Directory and File Structure
         * **Validates: Requirements 1.1, 9.1, 9.2, 9.3, 9.4, 10.1**
         *
         * For any generated CDK template (basic or advanced), the following directory
         * and file structure SHALL exist:
         * - lib/ directory alongside bin/ directory
         * - lib/config.ts file
         * - lib/infra-stack.ts file
         * - lib/runtime-stack.ts file
         * - lib/web-stack.ts file
         * - lib/awake-stack.ts file
         */
        it('should create correct directory and file structure for any template type', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const libFiles = getLibFiles(templateType);
                        const binFiles = getBinFiles(templateType);

                        // Verify lib/ directory exists with files
                        expect(libFiles.length).toBeGreaterThan(0);

                        // Verify bin/ directory exists with files
                        expect(binFiles.length).toBeGreaterThan(0);

                        // Verify required lib/ files exist
                        const requiredLibFiles = [
                            'config.ts.template',
                            'infra-stack.ts.template',
                            'runtime-stack.ts.template',
                            'web-stack.ts.template',
                            'awake-stack.ts.template',
                        ];

                        for (const file of requiredLibFiles) {
                            expect(libFiles).toContain(file);
                        }

                        // Verify bin/app.ts.template exists
                        expect(binFiles).toContain('app.ts.template');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 2: File Naming Convention', () => {
        /**
         * Feature: cdk-templates-best-practices, Property 2: File Naming Convention
         * **Validates: Requirements 1.2, 9.5, 12.2**
         *
         * For any stack configuration file in the lib/ directory, the file name
         * SHALL match the pattern *-stack.ts or be config.ts, and each stack file
         * SHALL export at least one function or class.
         */
        it('should follow naming convention for lib/ files', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const libFiles = getLibFiles(templateType);

                        for (const file of libFiles) {
                            // Remove .template extension for checking
                            const baseName = file.replace('.template', '');

                            // File should be config.ts or *-stack.ts
                            const isConfig = baseName === 'config.ts';
                            const isStack = baseName.endsWith('-stack.ts');

                            expect(isConfig || isStack).toBe(true);
                        }

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that stack files export functions.
         * **Validates: Requirements 9.5**
         */
        it('should export functions from stack files', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const stackFiles = [
                            'infra-stack.ts.template',
                            'runtime-stack.ts.template',
                            'web-stack.ts.template',
                            'awake-stack.ts.template',
                        ];

                        for (const fileName of stackFiles) {
                            const result = readTemplateFile(templateType, 'lib', fileName);
                            if (result.exists) {
                                // Verify export function exists
                                expect(result.content).toMatch(/export\s+function\s+create\w+Stack\s*\(/);
                            }
                        }

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 3: Import Path Correctness', () => {
        /**
         * Feature: cdk-templates-best-practices, Property 3: Import Path Correctness
         * **Validates: Requirements 1.4, 14.1, 14.2, 14.3, 14.4, 14.5, 14.6**
         *
         * For any generated template file:
         * - bin/app.ts SHALL contain imports from ../lib/ for stack configurations
         * - Stack files in lib/ SHALL contain imports from ./config for shared configuration
         * - Stack files in lib/ SHALL contain imports from @worktif/runtime/infra for framework interfaces
         */
        it('should have correct import paths in bin/app.ts', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const result = readAppTemplate(templateType);
                        if (!result.exists) return true;

                        const content = result.content;

                        // Verify imports from ../lib/
                        expect(content).toMatch(/from\s*['"]\.\.\/lib\/config['"]/);
                        expect(content).toMatch(/from\s*['"]\.\.\/lib\/infra-stack['"]/);
                        expect(content).toMatch(/from\s*['"]\.\.\/lib\/runtime-stack['"]/);
                        expect(content).toMatch(/from\s*['"]\.\.\/lib\/web-stack['"]/);
                        expect(content).toMatch(/from\s*['"]\.\.\/lib\/awake-stack['"]/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that lib/ stack files import from ./config.
         * **Validates: Requirements 14.2**
         */
        it('should import from ./config in lib/ stack files', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const stackFiles = [
                            'infra-stack.ts.template',
                            'runtime-stack.ts.template',
                            'web-stack.ts.template',
                            'awake-stack.ts.template',
                        ];

                        for (const fileName of stackFiles) {
                            const result = readTemplateFile(templateType, 'lib', fileName);
                            if (result.exists) {
                                // Verify import from ./config
                                expect(result.content).toMatch(/from\s*['"]\.\/config['"]/);
                            }
                        }

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that lib/ stack files import from @worktif/runtime/infra.
         * **Validates: Requirements 14.3**
         */
        it('should import from @worktif/runtime/infra in lib/ stack files', () => {
            const templateTypes: TemplateType[] = ['basic', 'advanced'];

            fc.assert(
                fc.property(
                    fc.constantFrom(...templateTypes),
                    (templateType: TemplateType) => {
                        const stackFiles = [
                            'infra-stack.ts.template',
                            'runtime-stack.ts.template',
                            'web-stack.ts.template',
                            'awake-stack.ts.template',
                        ];

                        for (const fileName of stackFiles) {
                            const result = readTemplateFile(templateType, 'lib', fileName);
                            if (result.exists) {
                                // Verify import from @worktif/runtime/infra
                                expect(result.content).toMatch(/from\s*['"]@worktif\/runtime\/infra['"]/);
                            }
                        }

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 7: Template Consistency', () => {
        /**
         * Feature: cdk-templates-best-practices, Property 7: Template Consistency
         * **Validates: Requirements 12.1, 12.3**
         *
         * For any basic and advanced template pair, the directory structure SHALL be
         * identical, and the only differences SHALL be in:
         * - Stack configuration content (handler imports, register configuration)
         */
        it('should have identical directory structure between basic and advanced templates', () => {
            fc.assert(
                fc.property(
                    fc.constant(true),
                    () => {
                        const basicLibFiles = getLibFiles('basic').sort();
                        const advancedLibFiles = getLibFiles('advanced').sort();

                        const basicBinFiles = getBinFiles('basic').sort();
                        const advancedBinFiles = getBinFiles('advanced').sort();

                        // Verify lib/ files are identical
                        expect(basicLibFiles).toEqual(advancedLibFiles);

                        // Verify bin/ files are identical
                        expect(basicBinFiles).toEqual(advancedBinFiles);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that both templates have the same file naming conventions.
         * **Validates: Requirements 12.2**
         */
        it('should use same file naming conventions in both templates', () => {
            fc.assert(
                fc.property(
                    fc.constant(true),
                    () => {
                        const basicLibFiles = getLibFiles('basic');
                        const advancedLibFiles = getLibFiles('advanced');

                        // Both should have the same set of files
                        expect(new Set(basicLibFiles)).toEqual(new Set(advancedLibFiles));

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });

    describe('Property 8: Configuration Module Completeness', () => {
        let availableConfigTemplates: TemplateType[];

        beforeAll(() => {
            availableConfigTemplates = getAvailableConfigTemplateTypes();
        });

        /**
         * Feature: cdk-templates-best-practices, Property 8: Configuration Module Completeness
         * **Validates: Requirements 10.2, 10.3, 10.4, 10.5, 10.6**
         *
         * For any generated lib/config.ts file, the code SHALL:
         * - Export a getConfig(stage: Stage) function
         * - Define serviceName, enableSeo, enableAwake, and env configuration
         * - Import and use getStageDefaults() from the framework
         * - Export TypeScript interfaces for configuration types
         */
        it('should export getConfig function', () => {
            if (availableConfigTemplates.length === 0) {
                console.warn('No config.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify export of getConfig function
                        expect(result.content).toMatch(/export\s+function\s+getConfig\s*\(/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that config defines required configuration properties.
         * **Validates: Requirements 10.3**
         */
        it('should define serviceName, enableSeo, enableAwake, and env configuration', () => {
            if (availableConfigTemplates.length === 0) {
                console.warn('No config.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify required configuration properties
                        expect(content).toContain('serviceName');
                        expect(content).toContain('enableSeo');
                        expect(content).toContain('enableAwake');
                        expect(content).toContain('env');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that config imports and uses getStageDefaults.
         * **Validates: Requirements 10.6**
         */
        it('should import and use getStageDefaults from framework', () => {
            if (availableConfigTemplates.length === 0) {
                console.warn('No config.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        const content = result.content;

                        // Verify import of getStageDefaults
                        expect(content).toMatch(/import\s*\{[^}]*getStageDefaults[^}]*\}\s*from\s*['"]@worktif\/runtime\/infra['"]/s);

                        // Verify usage of getStageDefaults
                        expect(content).toContain('getStageDefaults');

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });

        /**
         * Validates that config exports TypeScript interfaces.
         * **Validates: Requirements 10.4**
         */
        it('should export TypeScript interfaces for configuration types', () => {
            if (availableConfigTemplates.length === 0) {
                console.warn('No config.ts.template files found, skipping test');
                return;
            }

            fc.assert(
                fc.property(
                    fc.constantFrom(...availableConfigTemplates),
                    (templateType: TemplateType) => {
                        const result = readConfigTemplate(templateType);
                        expect(result.exists).toBe(true);

                        // Verify export of StackConfig interface
                        expect(result.content).toMatch(/export\s+interface\s+StackConfig/);

                        return true;
                    }
                ),
                { numRuns: 100 }
            );
        });
    });
});
