import { DataDomainKind } from '../models/kinds.js'
import { DataDomain } from './DataDomain.js'
import { DomainImpactReport } from './types.js'
import { AssociationValidation } from './validation/association_validation.js'
import { EntityValidation } from './validation/entity_validation.js'
import { PropertyValidation } from './validation/property_validation.js'
import { SemanticValidation } from './validation/semantic_validation.js'

/**
 * DomainValidation performs comprehensive validation on a data domain and its components.
 *
 * This class orchestrates validation across all domain elements including entities, properties,
 * associations, and semantics. It ensures that the data domain is well-formed and follows
 * established conventions before it can be published or used in API generation.
 *
 * ## Validation Scope
 *
 * The validation process covers:
 * - **Entities**: Structure, naming, primary keys, and inheritance
 * - **Properties**: Naming conventions, data types, and constraints
 * - **Associations**: Relationships, targets, and naming
 * - **Semantics**: Recommended patterns and best practices
 *
 * ## Validation Severity Levels
 *
 * - **Error**: Blocking issues that prevent domain publication
 * - **Warning**: Issues that may cause problems but don't block publication
 * - **Info**: Recommendations for best practices
 *
 * ## Usage
 *
 * ```typescript
 * const domain = new DataDomain()
 * // ... add entities, properties, associations
 *
 * const validator = new DomainValidation(domain)
 * const report = validator.validate()
 *
 * if (report.canProceed) {
 *   // Domain is valid and can be published
 * } else {
 *   // Handle validation errors
 *   console.log(report.impact)
 * }
 * ```
 *
 * ## Validation Rules
 *
 * ### Entity Validation Rules
 * - **Primary Key**: Each entity must have a primary key (can be inherited)
 * - **Minimum Properties**: Entities should have properties or associations (can be inherited)
 * - **Naming**: Entity names must follow PostgreSQL naming conventions
 * - **Uniqueness**: Entity names must be unique within the domain
 *
 * ### Property Validation Rules
 * - **Naming**: Properties must follow PostgreSQL column naming conventions
 * - **Length**: Names must be 2-59 characters long
 * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
 * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
 * - **Case**: Snake case is recommended (lowercase with underscores)
 *
 * ### Association Validation Rules
 * - **Targets**: Associations must have at least one target entity
 * - **Target Existence**: Target entities must exist in the domain
 * - **Naming**: Same rules as properties
 *
 * ### Semantic Validation Rules
 * - **User Entity**: Recommended to have at least one entity with User semantic
 * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
 * - **Soft Delete**: Recommended to have soft delete capability for entities
 * - **Data Types**: Semantic-specific data type validation
 *
 * @see {@link EntityValidation} for detailed entity validation rules
 * @see {@link PropertyValidation} for detailed property validation rules
 * @see {@link AssociationValidation} for detailed association validation rules
 * @see {@link SemanticValidation} for detailed semantic validation rules
 */
export class DomainValidation {
  private root: DataDomain

  constructor(root: DataDomain) {
    this.root = root
  }

  /**
   * Performs comprehensive validation on the entire data domain.
   *
   * This method validates all entities, properties, associations, and semantics
   * in the domain. It returns a detailed report of all validation issues found,
   * categorized by severity level.
   *
   * @returns A comprehensive validation report with all issues found
   */
  validate(): DomainImpactReport {
    const result: DomainImpactReport = {
      key: '',
      kind: DataDomainKind,
      impact: [],
      canProceed: true,
    }
    const entityValidator = new EntityValidation(this.root)
    const propertyValidator = new PropertyValidation(this.root)
    const associationValidator = new AssociationValidation(this.root)
    const semanticValidator = new SemanticValidation(this.root)

    let hasEntities = false
    // Validate entities, properties, and associations
    for (const entity of this.root.listEntities()) {
      if (entity.domain.key !== this.root.key) {
        // we don't need to validate foreign entities
        continue
      }
      hasEntities = true
      const report = entityValidator.validate(entity)
      for (const item of report) {
        const blocking = item.severity === 'error'
        result.canProceed = result.canProceed && !blocking
        result.impact.push({
          key: item.key,
          kind: item.kind,
          type: 'publish',
          impact: item.message,
          resolution: item.help,
          severity: item.severity,
          parent: item.parent,
        })
      }
      for (const property of entity.properties) {
        const report = propertyValidator.validate(property)
        for (const item of report) {
          const blocking = item.severity === 'error'
          result.canProceed = result.canProceed && !blocking
          result.impact.push({
            key: item.key,
            kind: item.kind,
            type: 'publish',
            impact: item.message,
            resolution: item.help,
            severity: item.severity,
            parent: item.parent,
          })
        }
      }
      for (const association of entity.associations) {
        const report = associationValidator.validate(association)
        for (const item of report) {
          const blocking = item.severity === 'error'
          result.canProceed = result.canProceed && !blocking
          result.impact.push({
            key: item.key,
            kind: item.kind,
            type: 'publish',
            impact: item.message,
            resolution: item.help,
            severity: item.severity,
            parent: item.parent,
          })
        }
      }
    }
    if (!hasEntities) {
      // no entities, no need to validate anything else
      return result
    }
    // Validate semantics
    const semanticReport = semanticValidator.validate()
    for (const item of semanticReport) {
      const blocking = item.severity === 'error'
      result.canProceed = result.canProceed && !blocking
      result.impact.push({
        key: item.key,
        kind: item.kind,
        type: 'publish',
        impact: item.message,
        resolution: item.help,
        severity: item.severity,
        parent: item.parent,
      })
    }

    return result
  }
}
