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'

export class DomainValidation {
  private root: DataDomain

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

  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
  }
}
