"""Centralized collection registry - single source of truth for all collection definitions.

When adding a new collection:
1. Create model dataclasses (MyModel + MyModelQueryResult) in a new file
2. Add a CollectionRegistry.register() call below with field definitions
3. Done. Setup scripts, VDB providers, crawler all derive from this automatically.
"""

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Type

from .fields import FieldDefinition, FieldType


@dataclass
class CollectionDefinition:
    """Complete definition of a vector database collection."""

    # Identity
    name: str                                   # e.g., "incidents_general"
    pattern: str                                # e.g., "incident" (for collection name matching)

    # Schema
    fields: List[FieldDefinition]               # Ordered list of fields

    # Model classes
    model_class: Type                           # e.g., Incident (with vector)
    query_result_class: Type                    # e.g., IncidentQueryResult (without vector)

    # Search/query configuration
    output_fields: List[str]                    # Fields returned by search/query (excludes vector)
    tags_field: Optional[str] = None            # Field name for hybrid keyword search

    # Crawler configuration
    embedding_template: Optional[str] = None    # Jinja2 template; None = use default key-value format
    similarity_threshold: float = 0.85

    def get_json_validation_schema(self) -> Dict[str, Any]:
        """Generate JSON Schema for crawler data validation.

        Derives the schema from field definitions, eliminating the need
        for separate collection.yaml json_output_format sections.
        """
        properties: Dict[str, Any] = {}
        required: List[str] = []

        for f in self.fields:
            if f.field_type == FieldType.FLOAT_VECTOR:
                continue

            prop: Dict[str, Any] = {}

            if f.field_type == FieldType.VARCHAR:
                prop["type"] = "string"
                if f.json_min_length is not None:
                    prop["minLength"] = f.json_min_length
                if f.json_pattern:
                    prop["pattern"] = f.json_pattern
            elif f.field_type in (FieldType.INT16, FieldType.INT32):
                prop["type"] = "integer"
                if f.json_minimum is not None:
                    prop["minimum"] = f.json_minimum
                if f.json_maximum is not None:
                    prop["maximum"] = f.json_maximum
            elif f.field_type == FieldType.FLOAT:
                prop["type"] = "number"
            elif f.field_type == FieldType.ARRAY_VARCHAR:
                prop["type"] = "array"
                prop["items"] = {"type": "string"}
                if f.json_min_items is not None:
                    prop["minItems"] = f.json_min_items

            if f.json_description:
                prop["description"] = f.json_description

            properties[f.name] = prop
            if f.json_required:
                required.append(f.name)

        return {
            "type": "array",
            "items": {
                "type": "object",
                "required": required,
                "properties": properties,
            }
        }

    def get_vdb_fields(self) -> List[FieldDefinition]:
        """Return only fields that are stored in the VDB (excludes validation-only fields)."""
        return [f for f in self.fields if f.store_in_vdb]


class CollectionRegistry:
    """Central registry of all collection definitions.

    Single source of truth for collection schemas, models, and configuration.
    """

    _collections: Dict[str, "CollectionDefinition"] = {}

    @classmethod
    def register(cls, definition: "CollectionDefinition") -> None:
        """Register a collection definition."""
        cls._collections[definition.name] = definition

    @classmethod
    def get(cls, collection_name: str) -> Optional["CollectionDefinition"]:
        """Get collection definition by exact name."""
        return cls._collections.get(collection_name)

    @classmethod
    def get_by_pattern(cls, collection_name: str) -> Optional["CollectionDefinition"]:
        """Get collection definition by pattern match.

        E.g., 'my_incidents_v2' matches pattern 'incident'.
        """
        name_lower = collection_name.lower()
        for defn in cls._collections.values():
            if defn.pattern in name_lower:
                return defn
        return None

    @classmethod
    def all(cls) -> List["CollectionDefinition"]:
        """Get all registered collections."""
        return list(cls._collections.values())

    @classmethod
    def all_names(cls) -> List[str]:
        """Get all default collection names."""
        return [d.name for d in cls._collections.values()]

    @classmethod
    def all_patterns(cls) -> List[str]:
        """Get all supported collection patterns."""
        return [d.pattern for d in cls._collections.values()]


# ---------------------------------------------------------------------------
# Register all collections
# ---------------------------------------------------------------------------

from .incidents import Incident, IncidentQueryResult
from .chaos_patterns import ChaosPattern, ChaosPatternQueryResult
from .best_practices import BestPractice, BestPracticeQueryResult
from .security import Security, SecurityQueryResult
from .regulations import Regulation, RegulationQueryResult
from .domain_indicators import DomainIndicator, DomainIndicatorQueryResult


# ---- Incidents ----

_INCIDENTS_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=64, is_primary=True,
                    json_pattern="^[a-z0-9-]+$"),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
    FieldDefinition("company", FieldType.VARCHAR, max_length=128, json_min_length=1),
    FieldDefinition("year", FieldType.INT16, json_minimum=2000, json_maximum=2030),
    FieldDefinition("narrative", FieldType.VARCHAR, max_length=4096, json_min_length=10),
    FieldDefinition("impact", FieldType.VARCHAR, max_length=4096, json_min_length=10),
    FieldDefinition("industries", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1),
    FieldDefinition("tags", FieldType.ARRAY_VARCHAR, max_length=256, max_capacity=100,
                    json_min_items=1),
    # trigger_keywords appears in Perplexity responses but gets merged into tags by Incident.from_dict()
    FieldDefinition("trigger_keywords", FieldType.ARRAY_VARCHAR, store_in_vdb=False,
                    json_min_items=1),
]

CollectionRegistry.register(CollectionDefinition(
    name="incidents_general",
    pattern="incident",
    fields=_INCIDENTS_FIELDS,
    model_class=Incident,
    query_result_class=IncidentQueryResult,
    output_fields=["id", "company", "year", "narrative", "impact", "industries", "tags"],
    tags_field="tags",
    embedding_template="{{narrative}} Impact: {{impact}}",
    similarity_threshold=0.85,
))


# ---- Chaos Patterns ----

_CHAOS_PATTERNS_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=128, is_primary=True),
    FieldDefinition("name", FieldType.VARCHAR, max_length=256, json_min_length=1),
    FieldDefinition("category", FieldType.VARCHAR, max_length=512, json_min_length=1),
    FieldDefinition("description", FieldType.VARCHAR, max_length=4096, json_min_length=10),
    FieldDefinition("cause", FieldType.VARCHAR, max_length=4096, json_min_length=10),
    FieldDefinition("symptoms", FieldType.VARCHAR, max_length=4096, json_min_length=10),
    FieldDefinition("technology", FieldType.VARCHAR, max_length=256, nullable=True,
                    json_required=False),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
]

CollectionRegistry.register(CollectionDefinition(
    name="chaos_patterns",
    pattern="chaos",
    fields=_CHAOS_PATTERNS_FIELDS,
    model_class=ChaosPattern,
    query_result_class=ChaosPatternQueryResult,
    output_fields=["id", "name", "category", "description", "cause", "symptoms"],
    tags_field=None,
    embedding_template=None,  # Uses default key-value format
    similarity_threshold=0.85,
))


# ---- Best Practices ----

_BEST_PRACTICES_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=128, is_primary=True,
                    json_pattern="^[a-z0-9-]+$"),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
    FieldDefinition("framework", FieldType.VARCHAR, max_length=128, json_min_length=1,
                    json_description="The framework or technology (e.g., OpenAI, LangChain, AWS)"),
    FieldDefinition("category", FieldType.VARCHAR, max_length=256, json_min_length=1,
                    json_description="Category of the best practice"),
    FieldDefinition("name", FieldType.VARCHAR, max_length=512, json_min_length=1,
                    json_description="Short descriptive name of the issue/pattern"),
    FieldDefinition("description", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="Detailed description from source"),
    FieldDefinition("problem", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="What problem this addresses"),
    FieldDefinition("why_it_fails", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="Explanation of why the naive approach fails"),
    FieldDefinition("solution", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="The recommended solution or best practice"),
    FieldDefinition("tags", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=50,
                    json_min_items=1, json_description="Relevant tags for categorization"),
]

CollectionRegistry.register(CollectionDefinition(
    name="best_practices_general",
    pattern="best_practice",
    fields=_BEST_PRACTICES_FIELDS,
    model_class=BestPractice,
    query_result_class=BestPracticeQueryResult,
    output_fields=["id", "framework", "category", "name", "description", "problem",
                   "why_it_fails", "solution", "tags"],
    tags_field="tags",
    embedding_template=None,  # Uses default key-value format
    similarity_threshold=0.85,
))


# ---- Security (CWEs, vulnerabilities) ----

_SECURITY_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=256, is_primary=True,
                    json_description="The name of the weakness or CWE ID"),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
    FieldDefinition("category", FieldType.VARCHAR, max_length=128, json_min_length=1,
                    json_description="Category: Distributed/Concurrency/Data/Integration/Resource/Error"),
    FieldDefinition("description", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="From source - not interpreted, include tier if CWE"),
    FieldDefinition("cause", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="What triggers this - from source"),
    FieldDefinition("detection", FieldType.VARCHAR, max_length=4096, nullable=True,
                    json_required=False, json_description="How to identify - from source, if documented"),
    FieldDefinition("mitigation", FieldType.VARCHAR, max_length=4096, nullable=True,
                    json_required=False, json_description="How to fix - from source, if documented"),
    FieldDefinition("tags", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=50,
                    json_min_items=1),
]

CollectionRegistry.register(CollectionDefinition(
    name="security",
    pattern="security",
    fields=_SECURITY_FIELDS,
    model_class=Security,
    query_result_class=SecurityQueryResult,
    output_fields=["id", "category", "description", "cause", "detection", "mitigation", "tags"],
    tags_field="tags",
    embedding_template=None,  # Uses default key-value format
    similarity_threshold=0.85,
))


# ---- Regulations (GDPR, HIPAA, SOC2, etc.) ----

_REGULATIONS_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=256, is_primary=True,
                    json_description="ID and name, e.g., 'GDPR-Art17 - Right to Erasure'"),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
    FieldDefinition("regulation_id", FieldType.VARCHAR, max_length=64, json_min_length=1,
                    json_description="Name of the regulation (e.g., GDPR, HIPAA)"),
    FieldDefinition("jurisdiction", FieldType.VARCHAR, max_length=64, json_min_length=1,
                    json_description="Jurisdiction (e.g., EU, US, California)"),
    FieldDefinition("description", FieldType.VARCHAR, max_length=4096, json_min_length=10,
                    json_description="What the regulation requires"),
    FieldDefinition("pattern", FieldType.VARCHAR, max_length=1024, json_min_length=1,
                    json_description="Anti-pattern that violates this regulation"),
    FieldDefinition("signals", FieldType.ARRAY_VARCHAR, max_length=256, max_capacity=50,
                    json_min_items=1, json_description="Code signals indicating violation"),
    FieldDefinition("example_fix", FieldType.VARCHAR, max_length=4096, nullable=True,
                    json_required=False, json_description="Example code fix"),
    FieldDefinition("penalty", FieldType.VARCHAR, max_length=512, nullable=True,
                    json_required=False, json_description="Potential penalty for violation"),
    FieldDefinition("tags", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=50,
                    json_min_items=1, json_description="Data types affected"),
]

CollectionRegistry.register(CollectionDefinition(
    name="regulations",
    pattern="regulation",
    fields=_REGULATIONS_FIELDS,
    model_class=Regulation,
    query_result_class=RegulationQueryResult,
    output_fields=["id", "regulation_id", "jurisdiction", "description", "pattern",
                   "signals", "example_fix", "penalty", "tags"],
    tags_field="tags",
    embedding_template=None,  # Uses default key-value format
    similarity_threshold=0.85,
))


# ---- Domain Indicators (business domain detection) ----

_DOMAIN_INDICATORS_FIELDS = [
    FieldDefinition("id", FieldType.VARCHAR, max_length=128, is_primary=True,
                    json_description="Domain identifier (e.g., 'payments', 'authentication')"),
    FieldDefinition("vector", FieldType.FLOAT_VECTOR),
    FieldDefinition("name", FieldType.VARCHAR, max_length=256, json_min_length=1,
                    json_description="Human-readable domain name"),
    FieldDefinition("variables", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1, json_description="Variable names indicating this domain"),
    FieldDefinition("functions", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1, json_description="Function names indicating this domain"),
    FieldDefinition("database_fields", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1, json_description="Database field names indicating this domain"),
    FieldDefinition("libraries", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1, json_description="Libraries/packages for this domain"),
    FieldDefinition("file_paths", FieldType.ARRAY_VARCHAR, max_length=256, max_capacity=100,
                    json_min_items=1, json_description="File paths indicating this domain"),
    FieldDefinition("api_patterns", FieldType.ARRAY_VARCHAR, max_length=256, max_capacity=100,
                    json_min_items=1, json_description="API endpoint patterns"),
    FieldDefinition("integrations", FieldType.ARRAY_VARCHAR, max_length=128, max_capacity=100,
                    json_min_items=1, json_description="Third-party integrations"),
    FieldDefinition("common_patterns", FieldType.ARRAY_VARCHAR, max_length=256, max_capacity=100,
                    json_min_items=1, json_description="Common code patterns"),
]

CollectionRegistry.register(CollectionDefinition(
    name="domain_indicators",
    pattern="domain_indicator",
    fields=_DOMAIN_INDICATORS_FIELDS,
    model_class=DomainIndicator,
    query_result_class=DomainIndicatorQueryResult,
    output_fields=["id", "name", "variables", "functions", "database_fields",
                   "libraries", "file_paths", "api_patterns", "integrations", "common_patterns"],
    tags_field=None,  # No single tags field for hybrid search
    embedding_template=None,  # Uses default key-value format
    similarity_threshold=0.85,
))
