"""Configuration models for K8s allocation."""

from dataclasses import dataclass
from pathlib import Path
from typing import Any

import yaml

# K8s Allocation Constants - Dual-Constraint Algorithm (v3.0)
CLUSTERS_COUNT = 5  # Number of K8s clusters (fixed topology)
NAMESPACES_PER_CLUSTER = 3  # Additional namespaces beyond 4 system defaults (default, kube-system, kube-node-lease, kube-public) = 7 total namespaces per cluster
WORKLOADS_PER_NAMESPACE = 2  # Base workloads per namespace (can scale to 3 for high density)

# Dual-constraint constants
WORKLOADS_PER_RESOURCE = 4  # Each resource serves exactly 4 workloads (3 regular + 1 Idle)
MIN_RESOURCES_PER_WORKLOAD = 2  # Minimum resources a workload must have
MAX_RESOURCES_PER_WORKLOAD = 5  # Maximum resources a workload can be assigned to

# Topology scaling (5 clusters × 7 namespaces = 35 namespaces)
LOW_DENSITY_WORKLOADS = 35 * 2  # 70 workloads (2 per namespace)
HIGH_DENSITY_WORKLOADS = 35 * 3  # 105 workloads (3 per namespace)

# Resource requirements for dual-constraint balance
OPTIMAL_RESOURCES_LOW_DENSITY = 53   # 70 workloads × 3 ÷ 4 = 52.5 → 53 resources
OPTIMAL_RESOURCES_HIGH_DENSITY = 79  # 105 workloads × 3 ÷ 4 = 78.75 → 79 resources
MINIMUM_RESOURCES_REQUIRED = 53     # Fail-fast threshold (supports 70 workloads minimum)

# Density threshold for topology scaling
DENSITY_THRESHOLD = 350  # Resources <= 350 → low density (70 workloads), > 350 → high density (105 workloads)


@dataclass
class K8sConfig:
    """K8s allocation configuration - dual-constraint algorithm."""

    dataset_name: str

    @property
    def clusters_count(self) -> int:
        """Get number of clusters (constant)."""
        return CLUSTERS_COUNT

    @property
    def namespaces_per_cluster(self) -> int:
        """Get additional namespaces per cluster (constant)."""
        return NAMESPACES_PER_CLUSTER

    @property
    def workloads_per_namespace(self) -> int:
        """Get base workloads per namespace (constant)."""
        return WORKLOADS_PER_NAMESPACE

    @property
    def workloads_per_resource(self) -> int:
        """Get workloads per resource (constant) - dual-constraint."""
        return WORKLOADS_PER_RESOURCE

    @property
    def min_resources_per_workload(self) -> int:
        """Get minimum resources per workload (constant)."""
        return MIN_RESOURCES_PER_WORKLOAD

    @property
    def max_resources_per_workload(self) -> int:
        """Get maximum resources per workload (constant)."""
        return MAX_RESOURCES_PER_WORKLOAD

    @property
    def low_density_workloads(self) -> int:
        """Get low density workload count (constant)."""
        return LOW_DENSITY_WORKLOADS

    @property
    def high_density_workloads(self) -> int:
        """Get high density workload count (constant)."""
        return HIGH_DENSITY_WORKLOADS

    @property
    def optimal_resources_low_density(self) -> int:
        """Get optimal resources for low density topology (constant)."""
        return OPTIMAL_RESOURCES_LOW_DENSITY

    @property
    def optimal_resources_high_density(self) -> int:
        """Get optimal resources for high density topology (constant)."""
        return OPTIMAL_RESOURCES_HIGH_DENSITY

    @property
    def minimum_resources_required(self) -> int:
        """Get minimum resources required for fail-fast (constant)."""
        return MINIMUM_RESOURCES_REQUIRED

    @property
    def density_threshold(self) -> int:
        """Get density threshold for topology scaling (constant)."""
        return DENSITY_THRESHOLD

    @classmethod
    def from_yaml(cls, config_path: str) -> "K8sConfig":
        """Load configuration from YAML file."""
        config_path = Path(config_path)

        if not config_path.exists():
            raise FileNotFoundError(f"Configuration file not found: {config_path}")

        with open(config_path) as f:
            config_data = yaml.safe_load(f)

        # Validate required fields
        if "dataset_name" not in config_data:
            raise ValueError("Configuration must include 'dataset_name'")

        # Ignore any topology/assignment/k8s_allocation_percentage settings in YAML
        # These are now constants to prevent row expansion issues
        return cls(dataset_name=config_data["dataset_name"])

    def validate(self) -> None:
        """Validate configuration values."""
        if not self.dataset_name:
            raise ValueError("dataset_name cannot be empty")

        # Constants are validated at module level - no need to validate here

    def to_dict(self) -> dict[str, Any]:
        """Convert configuration to dictionary."""
        return {
            "dataset_name": self.dataset_name,
            "topology": {
                "clusters_count": self.clusters_count,
                "namespaces_per_cluster": self.namespaces_per_cluster,
                "workloads_per_namespace": self.workloads_per_namespace,
            },
            "assignment": {
                "workloads_per_resource": self.workloads_per_resource,
                "min_resources_per_workload": self.min_resources_per_workload,
                "max_resources_per_workload": self.max_resources_per_workload,
            },
        }
