import type { NetworkTopology, ComplianceRequirement, DeploymentMilestone, CostEstimate } from '../types/infrastructure'

export type VersionedEntity = NetworkTopology | ComplianceRequirement | DeploymentMilestone | CostEstimate

export interface Version {
  id: string
  entityId: string
  entityType: string
  version: number
  timestamp: string
  userId?: string
  data: VersionedEntity
  changes: {
    field: string
    before: any
    after: any
  }[]
  comment?: string
}

class VersionControlService {
  private versions: Map<string, Version[]> = new Map()
  private maxVersionsPerEntity = 100

  saveVersion(
    entity: VersionedEntity,
    previousVersion: VersionedEntity | null,
    userId?: string,
    comment?: string
  ): Version {
    const entityId = (entity as any).id || (entity as any).country || 'unknown'
    const entityType = this.getEntityType(entity)
    const versions = this.versions.get(entityId) || []
    const versionNumber = versions.length + 1

    const changes = previousVersion
      ? this.calculateChanges(previousVersion.data, entity)
      : []

    const version: Version = {
      id: `version-${entityId}-${versionNumber}`,
      entityId,
      entityType,
      version: versionNumber,
      timestamp: new Date().toISOString(),
      userId,
      data: JSON.parse(JSON.stringify(entity)), // Deep clone
      changes,
      comment,
    }

    versions.push(version)

    // Keep only the last maxVersionsPerEntity versions
    if (versions.length > this.maxVersionsPerEntity) {
      versions.shift()
    }

    this.versions.set(entityId, versions)
    this.persistVersion(version)

    return version
  }

  private getEntityType(entity: VersionedEntity): string {
    if ('nodes' in entity) return 'topology'
    if ('country' in entity) return 'compliance'
    if ('title' in entity && 'startDate' in entity) return 'milestone'
    if ('category' in entity && 'monthly' in entity) return 'cost'
    return 'unknown'
  }

  private calculateChanges(before: VersionedEntity, after: VersionedEntity): Version['changes'] {
    const changes: Version['changes'] = []
    const beforeObj = before as any
    const afterObj = after as any

    const allKeys = new Set([...Object.keys(beforeObj), ...Object.keys(afterObj)])

    for (const key of allKeys) {
      if (key === 'id' || key === 'lastUpdated') continue

      const beforeVal = beforeObj[key]
      const afterVal = afterObj[key]

      if (JSON.stringify(beforeVal) !== JSON.stringify(afterVal)) {
        changes.push({
          field: key,
          before: beforeVal,
          after: afterVal,
        })
      }
    }

    return changes
  }

  getVersions(entityId: string): Version[] {
    return this.versions.get(entityId) || []
  }

  getVersion(entityId: string, versionNumber: number): Version | null {
    const versions = this.getVersions(entityId)
    return versions.find((v) => v.version === versionNumber) || null
  }

  getLatestVersion(entityId: string): Version | null {
    const versions = this.getVersions(entityId)
    return versions[versions.length - 1] || null
  }

  compareVersions(entityId: string, version1: number, version2: number): {
    added: Version['changes']
    removed: Version['changes']
    modified: Version['changes']
  } {
    const v1 = this.getVersion(entityId, version1)
    const v2 = this.getVersion(entityId, version2)

    if (!v1 || !v2) {
      return { added: [], removed: [], modified: [] }
    }

    const added: Version['changes'] = []
    const removed: Version['changes'] = []
    const modified: Version['changes'] = []

    const v1Data = v1.data as any
    const v2Data = v2.data as any
    const allKeys = new Set([...Object.keys(v1Data), ...Object.keys(v2Data)])

    for (const key of allKeys) {
      if (key === 'id' || key === 'lastUpdated') continue

      const v1Val = v1Data[key]
      const v2Val = v2Data[key]

      if (v1Val === undefined && v2Val !== undefined) {
        added.push({ field: key, before: undefined, after: v2Val })
      } else if (v1Val !== undefined && v2Val === undefined) {
        removed.push({ field: key, before: v1Val, after: undefined })
      } else if (JSON.stringify(v1Val) !== JSON.stringify(v2Val)) {
        modified.push({ field: key, before: v1Val, after: v2Val })
      }
    }

    return { added, removed, modified }
  }

  restoreVersion(entityId: string, versionNumber: number): VersionedEntity | null {
    const version = this.getVersion(entityId, versionNumber)
    if (!version) return null

    return JSON.parse(JSON.stringify(version.data)) // Deep clone
  }

  private async persistVersion(version: Version): Promise<void> {
    try {
      if (typeof window === 'undefined') {
        const fs = await import('fs/promises')
        const path = await import('path')
        const versionDir = path.join(process.cwd(), 'docs/infrastructure/versions')
        const versionFile = path.join(versionDir, `${version.entityId}-versions.json`)
        
        try {
          await fs.mkdir(versionDir, { recursive: true })
          let existingVersions: Version[] = []
          try {
            const content = await fs.readFile(versionFile, 'utf-8')
            existingVersions = JSON.parse(content)
          } catch {
            // File doesn't exist yet
          }
          existingVersions.push(version)
          // Keep only last maxVersionsPerEntity
          if (existingVersions.length > this.maxVersionsPerEntity) {
            existingVersions = existingVersions.slice(-this.maxVersionsPerEntity)
          }
          await fs.writeFile(versionFile, JSON.stringify(existingVersions, null, 2))
        } catch (error) {
          console.error('Failed to persist version:', error)
        }
      }
    } catch (error) {
      console.error('Failed to persist version:', error)
    }
  }
}

export const versionControlService = new VersionControlService()

