"""FieldDefinition — The living problem space for AFS simulations."""

import numpy as np
from dataclasses import dataclass, field
from typing import Optional
import json
import time

_field_counter = 0
_dim_counter = 0


@dataclass
class Dimension:
    id: str
    name: str
    description: str = ""
    category: str = "abstract"
    range: tuple = (0.0, 1.0)
    display_range: Optional[tuple] = None
    display_unit: str = ""
    lifecycle_state: str = "born"  # born, active, dormant, archived


class FieldDefinition:
    """
    N-dimensional problem space definition.

    Holds dimensions (concepts), the similarity/influence matrix,
    and metadata about the problem being explored.
    """

    def __init__(self, metadata: Optional[dict] = None, field_id: Optional[str] = None):
        global _field_counter
        _field_counter += 1
        self.field_id = field_id or f"field_{_field_counter}_{int(time.time())}"
        self.dimensions: list[Dimension] = []
        self.similarity_matrix: Optional[np.ndarray] = None
        self.metadata = metadata or {}

    def add_dimension(self, name: str, description: str = "",
                      category: str = "abstract", **kwargs) -> str:
        global _dim_counter
        _dim_counter += 1
        dim_id = f"dim_{_dim_counter}_{int(time.time())}"
        dim = Dimension(
            id=dim_id,
            name=name,
            description=description,
            category=category,
            range=kwargs.get("range", (0.0, 1.0)),
            display_range=kwargs.get("display_range"),
            display_unit=kwargs.get("display_unit", ""),
            lifecycle_state=kwargs.get("lifecycle_state", "born"),
        )
        self.dimensions.append(dim)
        return dim_id

    def promote_dimension(self, dim_id: str) -> bool:
        dim = self.get_dimension(dim_id)
        if dim and dim.lifecycle_state in ("born", "dormant"):
            dim.lifecycle_state = "active"
            return True
        return False

    def demote_dimension(self, dim_id: str) -> bool:
        dim = self.get_dimension(dim_id)
        if dim and dim.lifecycle_state == "active":
            dim.lifecycle_state = "dormant"
            return True
        return False

    def get_dimension(self, dim_id: str) -> Optional[Dimension]:
        for d in self.dimensions:
            if d.id == dim_id:
                return d
        return None

    def get_active_dimensions(self) -> list[Dimension]:
        return [d for d in self.dimensions if d.lifecycle_state in ("born", "active")]

    def get_dimension_count(self) -> int:
        return len(self.get_active_dimensions())

    def update_similarity_matrix(self, matrix: np.ndarray):
        """Set the N×N similarity/influence matrix (row-major)."""
        n = self.get_dimension_count()
        if matrix.shape != (n, n) and matrix.size == n * n:
            matrix = matrix.reshape(n, n)
        self.similarity_matrix = np.asarray(matrix, dtype=np.float64)

    def to_dict(self) -> dict:
        return {
            "field_id": self.field_id,
            "metadata": self.metadata,
            "dimensions": [
                {
                    "id": d.id, "name": d.name, "description": d.description,
                    "category": d.category, "range": list(d.range),
                    "lifecycle_state": d.lifecycle_state,
                }
                for d in self.dimensions
            ],
            "similarity_matrix": self.similarity_matrix.tolist() if self.similarity_matrix is not None else None,
        }

    def to_json(self) -> str:
        return json.dumps(self.to_dict(), indent=2)

    @classmethod
    def from_dict(cls, data: dict) -> "FieldDefinition":
        fd = cls(metadata=data.get("metadata"), field_id=data.get("field_id"))
        for d in data.get("dimensions", []):
            dim_id = fd.add_dimension(
                name=d["name"],
                description=d.get("description", ""),
                category=d.get("category", "abstract"),
                lifecycle_state=d.get("lifecycle_state", "active"),
            )
            # Override auto-generated id if provided
            if "id" in d:
                fd.dimensions[-1].id = d["id"]
            if d.get("lifecycle_state") == "active":
                fd.promote_dimension(dim_id)
        if data.get("similarity_matrix"):
            n = fd.get_dimension_count()
            fd.update_similarity_matrix(np.array(data["similarity_matrix"], dtype=np.float64).reshape(n, n))
        return fd

    @classmethod
    def from_json(cls, json_str: str) -> "FieldDefinition":
        return cls.from_dict(json.loads(json_str))
