"""Assessment module - complexity calculation and pattern matching."""

from __future__ import annotations

from dataclasses import dataclass


# =============================================================================
# PATTERN LIBRARY - 7 Core Patterns for Fast-Path Assessment
# =============================================================================

PATTERNS = {
    "full_stack_crud": {
        "name": "Full-Stack CRUD",
        "description": "Create/read/update/delete operations spanning UI, API, and database layers",
        "keywords": [
            "form",
            "page",
            "crud",
            "create",
            "update",
            "delete",
            "add",
            "edit",
            "remove",
            "table",
            "migration",
            "endpoint",
            "api",
            "frontend",
            "backend",
            "react",
            "vue",
            "angular",
            "express",
            "django",
            "rails",
            "postgres",
            "mysql",
            "mongodb",
        ],
        "required_any": ["crud", "create", "add", "update", "delete", "form", "page"],
        "expected_components": {
            "ui_layer": ["react", "vue", "angular", "frontend", "form", "page", "component"],
            "api_layer": ["api", "endpoint", "express", "django", "rails", "backend", "route"],
            "db_layer": ["postgres", "mysql", "mongodb", "table", "database", "schema"],
        },
        "systems_base": 3,
        "modifiers_default": ["data_migration"],
        "split_strategy": ["Database layer", "API layer", "UI layer"],
    },
    "authentication": {
        "name": "Authentication System",
        "description": "User authentication with credential storage, token management, protected routes",
        "keywords": [
            "auth",
            "authentication",
            "login",
            "register",
            "logout",
            "jwt",
            "token",
            "session",
            "oauth",
            "saml",
            "bcrypt",
            "password",
            "credential",
            "sign",
            "verify",
            "middleware",
            "guard",
            "protected",
            "secure",
        ],
        "required_any": ["auth", "login", "jwt", "oauth", "session", "password"],
        "expected_components": {
            "mechanism": ["jwt", "session", "oauth", "saml", "token"],
            "storage": ["postgres", "mysql", "mongodb", "database", "store", "bcrypt", "hash"],
            "protection": ["middleware", "guard", "protected", "route", "endpoint"],
        },
        "systems_base": 2,
        "modifiers_default": ["state_coordination", "error_handling", "security_critical"],
        "split_strategy": ["Credential storage", "Token generation/validation", "Route protection"],
    },
    "external_integration": {
        "name": "External Service Integration",
        "description": "Third-party API integration with error handling and state management",
        "keywords": [
            "stripe",
            "twilio",
            "sendgrid",
            "aws",
            "s3",
            "lambda",
            "google",
            "api",
            "integrate",
            "webhook",
            "callback",
            "payment",
            "email",
            "sms",
            "storage",
            "analytics",
            "external",
            "third-party",
            "provider",
        ],
        "required_any": ["stripe", "twilio", "sendgrid", "aws", "s3", "webhook", "integrate"],
        "expected_components": {
            "provider": ["stripe", "twilio", "sendgrid", "aws", "s3", "google", "azure"],
            "operation": ["webhook", "callback", "payment", "email", "sms", "upload"],
            "error_handling": ["retry", "error", "idempotent", "handle", "fallback"],
        },
        "systems_base": 4,
        "modifiers_default": ["state_coordination", "error_handling", "third_party_api"],
        "split_strategy": [
            "Database schema",
            "API client + error handling",
            "Webhook handlers",
            "UI integration",
        ],
    },
    "database_migration": {
        "name": "Database Migration",
        "description": "Schema changes, data transformations, or backfill operations",
        "keywords": [
            "migrate",
            "migration",
            "schema",
            "column",
            "table",
            "index",
            "alter",
            "backfill",
            "transform",
            "constraint",
            "rollback",
            "transaction",
        ],
        "required_any": ["migrate", "migration", "schema", "column", "alter", "backfill"],
        "expected_components": {
            "change_type": ["column", "table", "index", "constraint", "alter", "add", "remove"],
            "strategy": ["migration", "backfill", "rollback", "transaction"],
        },
        "systems_base": 1,
        "modifiers_default": ["data_migration"],
        "split_strategy": ["Migration script", "Backfill script", "Validation + rollback"],
    },
    "performance_optimization": {
        "name": "Performance Optimization",
        "description": "Caching, query optimization, or performance improvements with SLAs",
        "keywords": [
            "optimize",
            "cache",
            "redis",
            "memcached",
            "cdn",
            "performance",
            "latency",
            "throughput",
            "p95",
            "p99",
            "response time",
            "query",
            "index",
            "slow",
        ],
        "required_any": ["optimize", "cache", "redis", "performance", "latency"],
        "expected_components": {
            "technology": ["redis", "memcached", "cdn", "cache", "index"],
            "sla": ["p95", "p99", "latency", "ms", "response time", "throughput"],
            "invalidation": ["invalidat", "expire", "ttl", "refresh"],
        },
        "systems_base": 3,
        "modifiers_default": ["state_coordination", "concurrency", "performance_critical"],
        "split_strategy": ["Caching layer", "Cache invalidation", "Monitoring + metrics"],
    },
    "breaking_api_change": {
        "name": "Breaking API Change",
        "description": "API contract modifications with versioning or backwards compatibility",
        "keywords": [
            "version",
            "v1",
            "v2",
            "deprecate",
            "deprecated",
            "breaking",
            "rename",
            "endpoint",
            "contract",
            "backward",
            "migration",
            "client",
        ],
        "required_any": ["version", "v2", "deprecate", "breaking", "backward"],
        "expected_components": {
            "change": ["rename", "remove", "modify", "change", "breaking"],
            "versioning": ["version", "v1", "v2", "v3", "deprecat"],
            "client_plan": ["client", "consumer", "update", "migrat"],
        },
        "systems_base": 2,
        "modifiers_default": ["breaking_changes"],
        "split_strategy": ["New versioned endpoint", "Deprecation + dual-support", "Client migration"],
    },
    "simple_refactor": {
        "name": "Simple Refactor",
        "description": "Code cleanup, renaming, or structural changes without functional modifications",
        "keywords": [
            "refactor",
            "rename",
            "reorganize",
            "cleanup",
            "extract",
            "inline",
            "move",
            "restructure",
            "mechanical",
            "no functional",
        ],
        "required_any": ["refactor", "rename", "cleanup", "extract", "reorganize"],
        "expected_components": {
            "operation": ["rename", "extract", "inline", "move", "reorganize"],
            "scope": ["function", "class", "file", "module", "component", "method"],
        },
        "systems_base": 1,
        "modifiers_default": [],
        "split_strategy": ["By module/scope", "Update tests"],
    },
}

MODIFIERS = {
    "state_coordination": [
        "transaction",
        "cache invalidation",
        "eventual consistency",
        "sync",
        "distributed",
    ],
    "error_handling": ["rollback", "compensation", "recovery", "retry", "error handling"],
    "concurrency": ["concurrent", "lock", "atomic", "race condition", "thread"],
    "security_critical": ["auth", "encrypt", "secret", "pii", "credential", "password", "token"],
    "breaking_changes": ["breaking", "backward compat", "deprecate", "migration path"],
    "data_migration": ["migration", "schema", "backfill", "transform", "alter"],
    "third_party_api": ["stripe", "twilio", "sendgrid", "aws", "external api", "webhook"],
    "performance_critical": ["sla", "latency", "p95", "p99", "throughput", "<100ms"],
}


# =============================================================================
# DATA CLASSES
# =============================================================================


@dataclass
class AssessmentResult:
    """Result of complexity assessment."""

    complexity: float
    systems: int
    modifiers: list[str]
    method: str
    pattern: str | None = None
    confidence: float = 0.0
    decision: str = ""
    reasoning: str = ""
    skip_interrogation: bool = False  # Tier 0: trivial task, skip questions


@dataclass
class PatternMatch:
    """Result of pattern matching."""

    pattern_id: str
    name: str
    confidence: float
    keywords_matched: list[str]
    systems: int
    modifiers: list[str]


# =============================================================================
# CORE FUNCTIONS
# =============================================================================


def match_pattern(directive: str) -> PatternMatch | None:
    """Match directive against 7 core patterns.

    Confidence scoring:
    - Base 0.55 for having a required keyword
    - +0.05 per keyword matched (up to 0.20)
    - +0.08 per expected component category covered (key for high confidence)
    - +0.05 for specific technology names
    - -0.15 for vague terms

    For 0.90+ confidence, all expected_components categories must be covered.
    """
    directive_lower = directive.lower()
    best_match: PatternMatch | None = None
    best_confidence = 0.0

    for pattern_id, pattern in PATTERNS.items():
        keywords_matched = [kw for kw in pattern["keywords"] if kw in directive_lower]
        has_required = any(req in directive_lower for req in pattern["required_any"])

        if not has_required:
            continue

        # Base confidence for having required keyword
        confidence = 0.55

        # +0.05 per keyword matched (capped at 0.20)
        confidence += min(0.20, len(keywords_matched) * 0.05)

        # Component coverage scoring (key differentiator for high confidence)
        expected = pattern.get("expected_components", {})
        components_covered = 0
        for component_keywords in expected.values():
            if any(kw in directive_lower for kw in component_keywords):
                components_covered += 1

        if expected:
            # +0.08 per component covered
            confidence += components_covered * 0.08
            # Bonus if ALL components covered (0.90+ territory)
            if components_covered == len(expected):
                confidence += 0.05

        # Small bonus for specific technology names
        specific_tech = ["postgres", "mysql", "mongodb", "redis", "stripe", "aws", "jwt", "oauth"]
        if any(tech in directive_lower for tech in specific_tech):
            confidence += 0.05

        # Penalty for vague terms
        vague_terms = ["better", "improve", "fix issues", "make it", "update"]
        if any(vague in directive_lower for vague in vague_terms):
            confidence -= 0.15

        confidence = min(0.98, max(0.0, confidence))

        if confidence > best_confidence:
            best_confidence = confidence
            best_match = PatternMatch(
                pattern_id=pattern_id,
                name=pattern["name"],
                confidence=confidence,
                keywords_matched=keywords_matched,
                systems=pattern["systems_base"],
                modifiers=pattern["modifiers_default"],
            )

    return best_match if best_match and best_match.confidence >= 0.80 else None


def detect_modifiers(directive: str) -> list[str]:
    """Detect which complexity modifiers apply."""
    directive_lower = directive.lower()
    detected = []

    for modifier, keywords in MODIFIERS.items():
        if any(kw in directive_lower for kw in keywords):
            detected.append(modifier)

    return detected


def calculate_complexity(systems: int, modifiers: list[str]) -> float:
    """Calculate complexity: (1 + systems) * (1 + 0.5 * modifiers).

    The multiplicative formula captures interaction effects between modifiers.
    A task with state coordination + concurrency is more than 2x harder than either alone.
    """
    modifier_count = len(modifiers)
    complexity = (1 + systems) * (1 + 0.5 * modifier_count)
    return round(complexity, 1)


def effective_complexity(complexity: float, validate: bool = True) -> float:
    """Apply validation offset for threshold comparison.

    Per SKILL.md: effective_complexity = complexity + (1 if validate else 0)
    This is used when comparing against threshold, not baked into base complexity.
    """
    return complexity + (1 if validate else 0)


def should_skip_interrogation(assessment: AssessmentResult, threshold: float = 2.0) -> bool:
    """Tier 0: Check if task is trivial enough to skip interrogation.

    Criteria (ALL must be met):
    1. Fast-path confidence >= 0.90 (very high pattern match)
    2. Effective complexity <= threshold (task won't cleave)
    3. Pattern = "Simple Refactor" (low risk category)

    Returns True if interrogation should be skipped (Tier 0).
    """
    eff_complexity = effective_complexity(assessment.complexity, validate=True)
    return (
        assessment.confidence >= 0.90
        and eff_complexity <= threshold
        and assessment.pattern == "Simple Refactor"
    )


def detect_flags(directive: str) -> dict[str, bool]:
    """Detect special flags in directive text.

    Flags:
    - iamverysmart: Skip interrogation, literal interpretation
    - cleave-robust: Force robust mode with sequential thinking
    """
    directive_lower = directive.lower()
    return {
        "iamverysmart": "iamverysmart" in directive_lower,
        "robust": "cleave-robust" in directive_lower or "cleave_robust" in directive_lower,
    }


def assess_directive(
    directive: str, threshold: float = 2.0, validate: bool = True
) -> AssessmentResult:
    """Assess directive complexity using fast-path pattern matching."""
    match = match_pattern(directive)

    if match and match.confidence >= 0.80:
        detected_modifiers = detect_modifiers(directive)
        all_modifiers = list(set(match.modifiers + detected_modifiers))

        complexity = calculate_complexity(match.systems, all_modifiers)
        eff_complexity = effective_complexity(complexity, validate)
        decision = "execute" if eff_complexity <= threshold else "cleave"

        result = AssessmentResult(
            complexity=complexity,
            systems=match.systems,
            modifiers=all_modifiers,
            method="fast-path",
            pattern=match.name,
            confidence=match.confidence,
            decision=decision,
            reasoning=f"Pattern '{match.name}' matched with {match.confidence:.0%} confidence. "
            f"Systems: {match.systems}, Modifiers: {len(all_modifiers)}. "
            f"Formula: (1 + {match.systems}) * (1 + 0.5 * {len(all_modifiers)}) = {complexity}. "
            f"Effective (with validate={validate}): {eff_complexity}",
        )
        # Check for Tier 0 (trivial task, skip interrogation)
        result.skip_interrogation = should_skip_interrogation(result, threshold)
        return result
    else:
        detected_modifiers = detect_modifiers(directive)
        estimated_systems = 2
        complexity = calculate_complexity(estimated_systems, detected_modifiers)
        eff_complexity = effective_complexity(complexity, validate)

        return AssessmentResult(
            complexity=complexity,
            systems=estimated_systems,
            modifiers=detected_modifiers,
            method="sequential",
            pattern=None,
            confidence=match.confidence if match else 0.0,
            decision="needs_assessment",
            reasoning=f"No pattern matched with sufficient confidence "
            f"(best: {match.confidence if match else 0:.0%}). "
            f"Recommend sequential thinking. Rough estimate: {complexity}",
            skip_interrogation=False,  # Sequential assessment always needs questions
        )
