/*
 * 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
 */

/**
 * Tests for AI Template CLI Discovery
 *
 * **Validates: Requirement 1.4**
 * WHEN the CLI init command is invoked with `--template ai`,
 * THE Template_Service SHALL locate and scaffold from the AI template directory
 */

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

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

/**
 * Expected files in the AI template application directory.
 */
const EXPECTED_APPLICATION_FILES = [
    'package.json',
    'README.md',
    'runtime.config.ts',
    'tsconfig.json',
    '.env.example',
    'gitignore.template',
];

/**
 * Expected directories in the AI template application directory.
 */
const EXPECTED_APPLICATION_DIRS = [
    'src',
    'public',
];

/**
 * Expected files in the AI template CDK directory.
 */
const EXPECTED_CDK_FILES = [
    'package.json.template',
    'README.md.template',
    'tsconfig.json.template',
    'cdk.json.template',
    '.gitignore.template',
];

/**
 * Expected directories in the AI template CDK directory.
 */
const EXPECTED_CDK_DIRS = [
    'bin',
    'lib',
];

/**
 * Expected files in the AI template src directory.
 */
const EXPECTED_SRC_FILES = [
    'App.tsx',
    'index.tsx',
    'routes.tsx',
];

/**
 * Expected directories in the AI template src directory.
 */
const EXPECTED_SRC_DIRS = [
    'handlers',
    'services',
    'ties',
    'types',
];

/**
 * Gets the path to a template directory.
 *
 * @param templateType - The template type
 * @returns The absolute path to the template directory
 */
function getTemplatePath(templateType: TemplateType): string {
    return path.join(__dirname, '..', templateType);
}

/**
 * Checks if a template directory exists.
 *
 * @param templateType - The template type
 * @returns True if the template directory exists
 */
function templateExists(templateType: TemplateType): boolean {
    const templatePath = getTemplatePath(templateType);
    return fs.existsSync(templatePath);
}

/**
 * Gets the application directory path for a template.
 *
 * @param templateType - The template type
 * @returns The absolute path to the application directory
 */
function getApplicationPath(templateType: TemplateType): string {
    return path.join(getTemplatePath(templateType), 'application');
}

/**
 * Gets the CDK directory path for a template.
 *
 * @param templateType - The template type
 * @returns The absolute path to the CDK directory
 */
function getCdkPath(templateType: TemplateType): string {
    return path.join(getTemplatePath(templateType), 'cdk');
}

describe('AI Template CLI Discovery', () => {
    /**
     * **Validates: Requirement 1.4**
     */
    describe('Requirement 1.4: Template Directory Discovery', () => {
        it('should have AI template directory at src/bin/templates/ai/', () => {
            const aiTemplatePath = getTemplatePath('ai');
            expect(fs.existsSync(aiTemplatePath)).toBe(true);
        });

        it('should have application subdirectory in AI template', () => {
            const applicationPath = getApplicationPath('ai');
            expect(fs.existsSync(applicationPath)).toBe(true);
        });

        it('should have cdk subdirectory in AI template', () => {
            const cdkPath = getCdkPath('ai');
            expect(fs.existsSync(cdkPath)).toBe(true);
        });
    });

    /**
     * **Validates: Requirement 1.5**
     * THE AI_Template application directory SHALL follow the same structure as the basic template
     */
    describe('Requirement 1.5: Template Structure Consistency', () => {
        it('should have all required application files', () => {
            const applicationPath = getApplicationPath('ai');

            for (const file of EXPECTED_APPLICATION_FILES) {
                const filePath = path.join(applicationPath, file);
                expect(fs.existsSync(filePath)).toBe(true);
            }
        });

        it('should have all required application directories', () => {
            const applicationPath = getApplicationPath('ai');

            for (const dir of EXPECTED_APPLICATION_DIRS) {
                const dirPath = path.join(applicationPath, dir);
                expect(fs.existsSync(dirPath)).toBe(true);
                expect(fs.statSync(dirPath).isDirectory()).toBe(true);
            }
        });

        it('should have all required CDK files', () => {
            const cdkPath = getCdkPath('ai');

            for (const file of EXPECTED_CDK_FILES) {
                const filePath = path.join(cdkPath, file);
                expect(fs.existsSync(filePath)).toBe(true);
            }
        });

        it('should have all required CDK directories', () => {
            const cdkPath = getCdkPath('ai');

            for (const dir of EXPECTED_CDK_DIRS) {
                const dirPath = path.join(cdkPath, dir);
                expect(fs.existsSync(dirPath)).toBe(true);
                expect(fs.statSync(dirPath).isDirectory()).toBe(true);
            }
        });

        it('should have all required src files', () => {
            const srcPath = path.join(getApplicationPath('ai'), 'src');

            for (const file of EXPECTED_SRC_FILES) {
                const filePath = path.join(srcPath, file);
                expect(fs.existsSync(filePath)).toBe(true);
            }
        });

        it('should have all required src directories', () => {
            const srcPath = path.join(getApplicationPath('ai'), 'src');

            for (const dir of EXPECTED_SRC_DIRS) {
                const dirPath = path.join(srcPath, dir);
                expect(fs.existsSync(dirPath)).toBe(true);
                expect(fs.statSync(dirPath).isDirectory()).toBe(true);
            }
        });
    });

    /**
     * **Validates: Requirement 1.1, 1.2, 1.3**
     * Template directory structure requirements
     */
    describe('Template Directory Structure', () => {
        it('should follow the same structure as basic template', () => {
            const basicAppPath = getApplicationPath('basic');
            const aiAppPath = getApplicationPath('ai');

            // Both should have the same top-level structure
            const basicDirs = fs.readdirSync(basicAppPath, { withFileTypes: true })
                .filter(d => d.isDirectory())
                .map(d => d.name)
                .sort();

            const aiDirs = fs.readdirSync(aiAppPath, { withFileTypes: true })
                .filter(d => d.isDirectory())
                .map(d => d.name)
                .sort();

            // AI template should have at least the same directories as basic
            for (const dir of basicDirs) {
                expect(aiDirs).toContain(dir);
            }
        });

        it('should have CDK lib directory with required stack templates', () => {
            const cdkLibPath = path.join(getCdkPath('ai'), 'lib');
            const requiredStacks = [
                'ai-stack.ts.template',
                'config.ts.template',
                'infra-stack.ts.template',
                'web-stack.ts.template',
                'runtime-stack.ts.template',
                'awake-stack.ts.template',
            ];

            for (const stack of requiredStacks) {
                const stackPath = path.join(cdkLibPath, stack);
                expect(fs.existsSync(stackPath)).toBe(true);
            }
        });

        it('should have CDK bin directory with app.ts.template', () => {
            const cdkBinPath = path.join(getCdkPath('ai'), 'bin');
            const appPath = path.join(cdkBinPath, 'app.ts.template');
            expect(fs.existsSync(appPath)).toBe(true);
        });
    });

    /**
     * **Validates: Requirement 2.1**
     * THE AI_Template SHALL include a `src/handlers/chat.handlers.ts` file
     */
    describe('AI-Specific Files', () => {
        it('should have chat handlers file', () => {
            const handlersPath = path.join(getApplicationPath('ai'), 'src', 'handlers', 'chat.handlers.ts');
            expect(fs.existsSync(handlersPath)).toBe(true);
        });

        it('should have bedrock service file', () => {
            const servicePath = path.join(getApplicationPath('ai'), 'src', 'services', 'bedrock.service.ts');
            expect(fs.existsSync(servicePath)).toBe(true);
        });

        it('should have memory service file', () => {
            const servicePath = path.join(getApplicationPath('ai'), 'src', 'services', 'memory.service.ts');
            expect(fs.existsSync(servicePath)).toBe(true);
        });

        it('should have search service file', () => {
            const servicePath = path.join(getApplicationPath('ai'), 'src', 'services', 'search.service.ts');
            expect(fs.existsSync(servicePath)).toBe(true);
        });

        it('should have AI ties file', () => {
            const tiesPath = path.join(getApplicationPath('ai'), 'src', 'ties', 'ai.ties.ts');
            expect(fs.existsSync(tiesPath)).toBe(true);
        });

        it('should have chat types file', () => {
            const typesPath = path.join(getApplicationPath('ai'), 'src', 'types', 'chat.types.ts');
            expect(fs.existsSync(typesPath)).toBe(true);
        });

        it('should have AI-specific CDK stack', () => {
            const aiStackPath = path.join(getCdkPath('ai'), 'lib', 'ai-stack.ts.template');
            expect(fs.existsSync(aiStackPath)).toBe(true);
        });
    });

    /**
     * **Validates: Requirement 8.1, 8.2, 8.3, 8.4**
     * Package configuration requirements
     */
    describe('Package Configuration', () => {
        it('should have package.json with required dependencies', () => {
            const packagePath = path.join(getApplicationPath('ai'), 'package.json');
            const packageContent = fs.readFileSync(packagePath, 'utf-8');
            const packageJson = JSON.parse(packageContent);

            // Check for AI-specific dependencies
            const deps = packageJson.dependencies || {};
            expect(deps).toHaveProperty('@aws-sdk/client-bedrock-runtime');
            expect(deps).toHaveProperty('@aws-sdk/client-dynamodb');
            expect(deps).toHaveProperty('@aws-sdk/lib-dynamodb');
            expect(deps).toHaveProperty('@opensearch-project/opensearch');
            expect(deps).toHaveProperty('@aws-sdk/credential-provider-node');
        });

        it('should have template variables in package.json', () => {
            const packagePath = path.join(getApplicationPath('ai'), 'package.json');
            const packageContent = fs.readFileSync(packagePath, 'utf-8');

            // Check for template variable substitution markers
            expect(packageContent).toContain('{{projectName}}');
        });
    });

    /**
     * **Validates: Requirement 9.1, 9.2, 9.3, 9.4, 9.6**
     * Environment configuration requirements
     */
    describe('Environment Configuration', () => {
        it('should have .env.example with required variables', () => {
            const envPath = path.join(getApplicationPath('ai'), '.env.example');
            const envContent = fs.readFileSync(envPath, 'utf-8');

            expect(envContent).toContain('BEDROCK_MODEL_ID');
            expect(envContent).toContain('MEMORY_TABLE_NAME');
            expect(envContent).toContain('OPENSEARCH_ENDPOINT');
            expect(envContent).toContain('AWS_REGION');
        });
    });

    /**
     * **Validates: Requirement 10.1, 10.2, 10.3, 10.4, 10.5, 10.6**
     * Documentation requirements
     */
    describe('Documentation', () => {
        it('should have README.md with setup instructions', () => {
            const readmePath = path.join(getApplicationPath('ai'), 'README.md');
            expect(fs.existsSync(readmePath)).toBe(true);

            const readmeContent = fs.readFileSync(readmePath, 'utf-8');
            expect(readmeContent.length).toBeGreaterThan(100);
        });
    });
});
