const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const logger = require('./logger');

class SchemaValidator {
  constructor() {
    this.ajv = new Ajv({ 
      allErrors: true,
      verbose: true,
      strict: false
    });
    addFormats(this.ajv);
    
    // Define schemas
    this.schemas = {
      unifiedRecommendation: this.getUnifiedRecommendationSchema(),
      techStackRequest: this.getTechStackRequestSchema(),
      templateRequest: this.getTemplateRequestSchema()
    };

    // Compile schemas
    this.compiledSchemas = {};
    for (const [name, schema] of Object.entries(this.schemas)) {
      try {
        this.compiledSchemas[name] = this.ajv.compile(schema);
        logger.info(`Schema '${name}' compiled successfully`);
      } catch (error) {
        logger.error(`Failed to compile schema '${name}': ${error.message}`);
      }
    }
  }

  /**
   * Get the unified recommendation schema
   * @returns {Object} JSON schema for unified recommendations
   */
  getUnifiedRecommendationSchema() {
    return {
      type: 'object',
      required: [
        'stack_name', 'monthly_cost', 'setup_cost', 'team_size', 'development_time',
        'satisfaction', 'success_rate', 'frontend', 'backend', 'database', 'cloud',
        'testing', 'devops', 'recommended_tool', 'recommendation_score', 'message'
      ],
      properties: {
        stack_name: {
          type: 'string',
          minLength: 1,
          maxLength: 100,
          description: 'Descriptive name for the tech stack'
        },
        monthly_cost: {
          type: 'number',
          minimum: 0,
          maximum: 10000,
          description: 'Monthly operational cost in USD'
        },
        setup_cost: {
          type: 'number',
          minimum: 0,
          maximum: 50000,
          description: 'One-time setup cost in USD'
        },
        team_size: {
          type: 'string',
          pattern: '^[0-9]+-[0-9]+$',
          description: 'Team size range (e.g., "1-2", "3-5")'
        },
        development_time: {
          type: 'number',
          minimum: 1,
          maximum: 52,
          description: 'Development time in weeks'
        },
        satisfaction: {
          type: 'number',
          minimum: 0,
          maximum: 100,
          description: 'User satisfaction score (0-100)'
        },
        success_rate: {
          type: 'number',
          minimum: 0,
          maximum: 100,
          description: 'Project success rate (0-100)'
        },
        frontend: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Frontend technology'
        },
        backend: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Backend technology'
        },
        database: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Database technology'
        },
        cloud: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Cloud platform'
        },
        testing: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Testing framework'
        },
        mobile: {
          type: 'string',
          minLength: 0,
          maxLength: 50,
          description: 'Mobile technology'
        },
        devops: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'DevOps tool'
        },
        ai_ml: {
          type: 'string',
          minLength: 0,
          maxLength: 50,
          description: 'AI/ML technology'
        },
        recommended_tool: {
          type: 'string',
          minLength: 1,
          maxLength: 50,
          description: 'Primary recommended tool'
        },
        recommendation_score: {
          type: 'number',
          minimum: 0,
          maximum: 100,
          description: 'Overall recommendation score (0-100)'
        },
        message: {
          type: 'string',
          minLength: 1,
          maxLength: 500,
          description: 'Brief explanation of the recommendation'
        }
      },
      additionalProperties: false
    };
  }

  /**
   * Get the tech stack request schema
   * @returns {Object} JSON schema for tech stack requests
   */
  getTechStackRequestSchema() {
    return {
      type: 'object',
      properties: {
        domain: {
          type: 'string',
          minLength: 1,
          maxLength: 100,
          description: 'Domain for recommendations'
        },
        budget: {
          type: 'number',
          minimum: 0,
          maximum: 100000,
          description: 'Budget constraint in USD'
        },
        preferredTechnologies: {
          type: 'array',
          items: {
            type: 'string',
            minLength: 1,
            maxLength: 50
          },
          maxItems: 10,
          description: 'Preferred technologies'
        }
      },
      additionalProperties: false
    };
  }

  /**
   * Get the template request schema
   * @returns {Object} JSON schema for template requests
   */
  getTemplateRequestSchema() {
    return {
      type: 'object',
      properties: {
        templateId: {
          type: 'string',
          format: 'uuid',
          description: 'Template ID'
        },
        includeSimilar: {
          type: 'boolean',
          description: 'Include similar templates'
        },
        includeKeywords: {
          type: 'boolean',
          description: 'Include keywords'
        },
        forceRefresh: {
          type: 'boolean',
          description: 'Force refresh recommendations'
        }
      },
      additionalProperties: false
    };
  }

  /**
   * Validate data against a schema
   * @param {string} schemaName - Name of the schema
   * @param {Object} data - Data to validate
   * @returns {Object} Validation result
   */
  validate(schemaName, data) {
    if (!this.compiledSchemas[schemaName]) {
      return {
        valid: false,
        errors: [`Schema '${schemaName}' not found`]
      };
    }

    const valid = this.compiledSchemas[schemaName](data);
    
    if (valid) {
      return {
        valid: true,
        errors: []
      };
    } else {
      const errors = this.compiledSchemas[schemaName].errors.map(error => {
        const path = error.instancePath || 'root';
        return `${path}: ${error.message}`;
      });
      
      logger.warn({
        message: `Schema validation failed for '${schemaName}'`,
        errors,
        data: JSON.stringify(data, null, 2)
      });

      return {
        valid: false,
        errors
      };
    }
  }

  /**
   * Validate unified recommendation
   * @param {Object} recommendation - Recommendation to validate
   * @returns {Object} Validation result
   */
  validateUnifiedRecommendation(recommendation) {
    return this.validate('unifiedRecommendation', recommendation);
  }

  /**
   * Validate tech stack request
   * @param {Object} request - Request to validate
   * @returns {Object} Validation result
   */
  validateTechStackRequest(request) {
    return this.validate('techStackRequest', request);
  }

  /**
   * Validate template request
   * @param {Object} request - Request to validate
   * @returns {Object} Validation result
   */
  validateTemplateRequest(request) {
    return this.validate('templateRequest', request);
  }

  /**
   * Get all available schemas
   * @returns {Array<string>} List of schema names
   */
  getAvailableSchemas() {
    return Object.keys(this.schemas);
  }

  /**
   * Get schema definition
   * @param {string} schemaName - Name of the schema
   * @returns {Object|null} Schema definition
   */
  getSchema(schemaName) {
    return this.schemas[schemaName] || null;
  }
}

module.exports = new SchemaValidator();
