# CoreDNS Corefile Generator
# Generates and manages Corefile configuration for CoreDNS

use ../utils/log.nu *

# Generate Corefile from configuration
export def generate-corefile [
    config: record  # CoreDNS configuration
] -> string {
    log debug $"Generating Corefile from config: ($config)"

    let local_config = $config.local? | default {}
    let zones = $local_config.zones? | default ["provisioning.local"]
    let port = $local_config.port? | default 5353
    let zones_path = $local_config.zones_path? | default "~/.provisioning/coredns/zones"
    let upstream = $config.upstream? | default ["8.8.8.8", "1.1.1.1"]
    let enable_logging = $config.enable_logging? | default true
    let enable_metrics = $config.enable_metrics? | default true
    let metrics_port = $config.metrics_port? | default 9153

    let dynamic_enabled = $config.dynamic_updates?.enabled? | default true
    let dynamic_endpoint = $config.dynamic_updates?.api_endpoint? | default "http://localhost:8080/dns"

    mut corefile_content = []

    # Generate zone blocks
    for zone in $zones {
        let zone_block = generate-zone-block $zone $port $zones_path $enable_logging $dynamic_enabled
        $corefile_content = ($corefile_content | append $zone_block)
    }

    # Generate catch-all forward block
    let forward_block = generate-forward-block $port $upstream $enable_logging
    $corefile_content = ($corefile_content | append $forward_block)

    # Generate metrics block if enabled
    if $enable_metrics {
        let metrics_block = generate-metrics-block $metrics_port
        $corefile_content = ($corefile_content | append $metrics_block)
    }

    $corefile_content | str join "\n\n"
}

# Generate zone block for Corefile
def generate-zone-block [
    zone: string
    port: int
    zones_path: string
    enable_logging: bool
    dynamic_enabled: bool
] -> string {
    let expanded_path = $zones_path | path expand

    mut plugins = []

    # File plugin
    $plugins = ($plugins | append $"    file ($expanded_path)/($zone).zone")

    # Dynamic updates via HTTP (if enabled)
    if $dynamic_enabled {
        $plugins = ($plugins | append "    # Dynamic DNS updates via orchestrator API")
        $plugins = ($plugins | append "    # Updates handled through external API")
    }

    # Logging
    if $enable_logging {
        $plugins = ($plugins | append "    log")
    }

    # Error handling
    $plugins = ($plugins | append "    errors")

    # Cache
    $plugins = ($plugins | append "    cache 30")

    let plugins_str = $plugins | str join "\n"

    $"($zone):($port) {
($plugins_str)
}"
}

# Generate forward block for Corefile
def generate-forward-block [
    port: int
    upstream: list<string>
    enable_logging: bool
] -> string {
    let upstream_str = $upstream | str join " "

    mut plugins = []
    $plugins = ($plugins | append $"    forward . ($upstream_str)")

    if $enable_logging {
        $plugins = ($plugins | append "    log")
    }

    $plugins = ($plugins | append "    errors")
    $plugins = ($plugins | append "    cache 30")

    let plugins_str = $plugins | str join "\n"

    $".:($port) {
($plugins_str)
}"
}

# Generate metrics block for Corefile
def generate-metrics-block [
    metrics_port: int
] -> string {
    $".:($metrics_port) {
    prometheus
    errors
}"
}

# Generate zone file for domain
export def generate-zone-file [
    zone_name: string      # Zone name (e.g., "provisioning.local")
    records: list          # List of DNS records
    config: record         # CoreDNS configuration
] -> string {
    log debug $"Generating zone file for ($zone_name)"

    let default_ttl = $config.default_ttl? | default 3600
    let admin_email = $"admin.($zone_name)"
    let serial = date now | format date "%Y%m%d01"  # YYYYMMDDnn format

    mut zone_content = []

    # Origin and TTL
    $zone_content = ($zone_content | append $"$ORIGIN ($zone_name).")
    $zone_content = ($zone_content | append $"$TTL ($default_ttl)")
    $zone_content = ($zone_content | append "")

    # SOA record
    let soa_block = generate-soa-record $zone_name $admin_email $serial
    $zone_content = ($zone_content | append $soa_block)
    $zone_content = ($zone_content | append "")

    # NS record
    $zone_content = ($zone_content | append $"@       IN      NS      ns1.($zone_name).")
    $zone_content = ($zone_content | append "")

    # A record for nameserver
    $zone_content = ($zone_content | append "ns1     IN      A       127.0.0.1")
    $zone_content = ($zone_content | append "")

    # User-defined records
    if ($records | is-not-empty) {
        $zone_content = ($zone_content | append "; Infrastructure records")

        for record in $records {
            let record_line = generate-record-line $record
            $zone_content = ($zone_content | append $record_line)
        }
    }

    $zone_content | str join "\n"
}

# Generate SOA record
def generate-soa-record [
    zone_name: string
    admin_email: string
    serial: string
] -> string {
    $"@       IN      SOA     ns1.($zone_name). ($admin_email). (
                        ($serial)  ; Serial
                        3600       ; Refresh
                        1800       ; Retry
                        604800     ; Expire
                        86400 )    ; Minimum TTL"
}

# Generate DNS record line
def generate-record-line [
    record: record
] -> string {
    let name = $record.name
    let type = $record.type
    let value = $record.value
    let ttl = $record.ttl? | default ""
    let priority = $record.priority? | default ""
    let comment = $record.comment? | default ""

    mut parts = []

    # Name (padded to 16 chars)
    $parts = ($parts | append ($name | fill -a left -w 16))

    # TTL (optional)
    if ($ttl != "") {
        $parts = ($parts | append ($ttl | into string | fill -a left -w 8))
    } else {
        $parts = ($parts | append ("" | fill -a left -w 8))
    }

    # Class (always IN)
    $parts = ($parts | append "IN")

    # Type
    $parts = ($parts | append ($type | fill -a left -w 8))

    # Priority (for MX and SRV)
    if ($priority != "") {
        $parts = ($parts | append ($priority | into string))
    }

    # Value
    $parts = ($parts | append $value)

    let record_line = $parts | str join " "

    # Add comment if present
    if ($comment != "") {
        $"($record_line)  ; ($comment)"
    } else {
        $record_line
    }
}

# Validate Corefile syntax
export def validate-corefile [
    corefile_path: string  # Path to Corefile
] -> record {
    log debug $"Validating Corefile at ($corefile_path)"

    let expanded_path = $corefile_path | path expand

    if not ($expanded_path | path exists) {
        return {
            valid: false
            errors: ["Corefile not found"]
            warnings: []
        }
    }

    mut errors = []
    mut warnings = []

    let content = open $expanded_path

    # Basic validation checks
    if ($content | str contains "file ") {
        # Check if zone files are referenced
        log debug "Zone files referenced in Corefile"
    } else {
        $warnings = ($warnings | append "No zone files referenced")
    }

    if ($content | str contains "forward ") {
        log debug "Forward plugin configured"
    } else {
        $warnings = ($warnings | append "No forward plugin configured")
    }

    # Check for balanced braces
    let open_braces = $content | str replace -a -r '[^{]' '' | str length
    let close_braces = $content | str replace -a -r '[^}]' '' | str length

    if $open_braces != $close_braces {
        $errors = ($errors | append "Unbalanced braces in Corefile")
    }

    {
        valid: ($errors | is-empty)
        errors: $errors
        warnings: $warnings
    }
}

# Add plugin to Corefile
export def add-corefile-plugin [
    corefile_path: string  # Path to Corefile
    zone: string           # Zone name
    plugin_config: string  # Plugin configuration line
] -> bool {
    log debug $"Adding plugin to Corefile zone ($zone)"

    let expanded_path = $corefile_path | path expand

    if not ($expanded_path | path exists) {
        log error $"Corefile not found at ($expanded_path)"
        return false
    }

    let content = open $expanded_path

    # Find zone block and add plugin
    let zone_pattern = $"($zone):\\d+ \\{"

    if ($content | str contains -r $zone_pattern) {
        # Insert plugin before closing brace
        let updated = $content | str replace -r $"($zone_pattern)(.*?)\\}" $"$1$2    ($plugin_config)\n}"
        $updated | save -f $expanded_path
        log info $"Added plugin to ($zone) in Corefile"
        true
    } else {
        log error $"Zone ($zone) not found in Corefile"
        false
    }
}

# Write Corefile to disk
export def write-corefile [
    corefile_path: string  # Path to write Corefile
    content: string        # Corefile content
] -> bool {
    log debug $"Writing Corefile to ($corefile_path)"

    let expanded_path = $corefile_path | path expand
    let parent_dir = $expanded_path | path dirname

    # Create parent directory if needed
    if not ($parent_dir | path exists) {
        mkdir $parent_dir
    }

    let result = (do {
        $content | save -f $expanded_path
    } | complete)

    if $result.exit_code == 0 {
        log info $"Corefile written to [$expanded_path]"
        true
    } else {
        log error $"Failed to write Corefile to [$expanded_path]"
        false
    }
}

# Read Corefile from disk
export def read-corefile [
    corefile_path: string  # Path to Corefile
] -> string {
    let expanded_path = $corefile_path | path expand

    if not ($expanded_path | path exists) {
        log error $"Corefile not found at ($expanded_path)"
        return ""
    }

    open $expanded_path
}

# Update Corefile with new configuration
export def update-corefile [
    config: record         # CoreDNS configuration
    corefile_path: string  # Path to Corefile
] -> bool {
    log info "Updating Corefile with new configuration"

    let new_content = generate-corefile $config
    write-corefile $corefile_path $new_content
}
