import * as path from 'path';
import * as fs from 'fs/promises';
import { AbstractInventoryBuilder } from '../src/core/design/AbstractInventoryBuilder.js';
import * as yaml from 'js-yaml';

// Define the categories for organizing schemas
const COMMON_SCHEMAS = ['api', 'urischemes', 'cors', 'properties', 'scope', 'product', 'plan', 'quota'];

interface SchemaInfo {
  name: string;
  version: string;
  content: string;
  category: 'common' | 'policies';
  fileType: 'ts' | 'yaml';
}

class InventoryGenerator extends AbstractInventoryBuilder {
  async generateInventoryModule(outputPath: string): Promise<void> {
    try {
      // Ensure the output directory exists
      await fs.mkdir(path.join(outputPath, 'generated/source/assets/common'), { recursive: true });
      await fs.mkdir(path.join(outputPath, 'generated/source/assets/policies'), { recursive: true });

      // Process each source path from the config
      for (const sourcePath of this.config.source) {
        if (sourcePath) {
          await this.processSourcePath(sourcePath, outputPath);
        }
      }

      console.log(`Inventory generation completed at ${outputPath}`);
    } catch (error) {
      console.error('Error generating inventory:', error);
      throw error;
    }
  }

  private async processSourcePath(sourcePath: string, outputBasePath: string): Promise<void> {
    try {
      // Resolve the full path
      const fullPath = path.resolve(process.cwd(), sourcePath.startsWith('/') ? `.${sourcePath}` : sourcePath);
      // const fullPath = '/Users/sowbarnigaa/Documents/pocs/Smith/smith/packages/schema-specification/V12_openAPI_specification.yaml';
      console.log(`Processing source path: ${fullPath}`);

      // Check if the file exists
      try {
        await fs.access(fullPath);
      } catch (error) {
        console.warn(`Source file not found: ${fullPath}`);
        return;
      }

      // Read the file content
      const content = await fs.readFile(fullPath, 'utf8');

      // Determine file type and parse accordingly
      const isYaml = fullPath.endsWith('.yaml') || fullPath.endsWith('.yml');
      let schemas;

      if (isYaml) {
        const parsed = yaml.load(content) as any;
        schemas = parsed.components?.schemas;
      } else {
        // Assume JSON
        const parsed = JSON.parse(content);
        schemas = parsed.components?.schemas || parsed.schemas;
      }

      if (!schemas) {
        console.warn(`No schemas found in ${fullPath}`);
        return;
      }

      // Process each schema
      for (const [schemaName, schema] of Object.entries<any>(schemas)) {
        await this.processSchema(schemaName, schema, outputBasePath, isYaml ? 'yaml' : 'ts');
      }
    } catch (error) {
      console.error(`Error processing source path ${sourcePath}:`, error);
      throw error;
    }
  }

  private async processSchema(
    schemaName: string,
    schema: any,
    outputBasePath: string,
    fileType: 'ts' | 'yaml'
  ): Promise<void> {
    try {
      // Extract schema information
      const schemaInfo = this.extractSchemaInfo(schemaName, schema, fileType);
      if (!schemaInfo) {
        console.warn(`Could not extract information for schema: ${schemaName}`);
        return;
      }

      // Determine the output directory
      const baseDir = path.join(
        outputBasePath,
        'generated/source/assets',
        schemaInfo.category,
        schemaInfo.name.toLowerCase()
      );

      // Create the directory if it doesn't exist
      await fs.mkdir(baseDir, { recursive: true });

      // Create the output file
      const fileName = `${schemaInfo.version.replace('\\', '-').replace('/', '-')}_${schemaInfo.name.toLowerCase()}.json`;
      const filePath = path.join(baseDir, fileName);
      
      await fs.writeFile(filePath, schemaInfo.content, 'utf8');
      console.log(`Generated: ${filePath}`);
    } catch (error) {
      console.error(`Error processing schema ${schemaName}:`, error);
      throw error;
    }
  }

  private extractSchemaInfo(
    schemaName: string,
    schema: any,
    fileType: 'ts' | 'yaml'
  ): SchemaInfo | null {
    try {
      // Determine the category (common or policies)
      const category = this.getCategory(schemaName);

      // Extract the version from the schema
      const version = this.extractVersion(schema) || 'api.ibm.com-v1';

      // Convert schema to string content
      const content = typeof schema === 'string' ? schema : JSON.stringify(schema, null, 2);

      return {
        name: schemaName,
        version,
        content,
        category,
        fileType: fileType
      };
    } catch (error) {
      console.error(`Error extracting schema info for ${schemaName}:`, error);
      return null;
    }
  }

  private getCategory(schemaName: string): 'common' | 'policies' {
    const lowerName = schemaName.toLowerCase();
    return COMMON_SCHEMAS.some(common => lowerName === common.toLowerCase()) ? 'common' : 'policies';
  }

  private extractVersion(schema: any): string | null {
    // Try to extract version from different possible locations
    if (schema.apiVersion?.default) {
      return schema.apiVersion.default;
    }

    if (schema.properties?.apiVersion?.default) {
      return schema.properties.apiVersion.default;
    }

    // If no version found, return null
    return null;
  }
}

async function run() {
  const configPath = path.join(process.cwd(), 'generator/inventory_config.json');
  const outputPath = process.cwd();

  const generator = new InventoryGenerator();
  await generator.runGeneration(configPath, outputPath);
  console.log(`Inventory generation completed at ${outputPath}`);
}

// Run the generator
run().catch(err => {
  console.error('Inventory generation failed:', err);
  process.exit(1);
});

// Made with Bob
