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

from __future__ import annotations

from dataclasses import dataclass

from cleave.core.performance import timed


# =============================================================================
# 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"],
        # Socratic probe questions - codebase-conditional
        "probe_questions": [
            {
                "trigger": {"type": "file_missing", "pattern": "**/models/**"},
                "question": "No models directory detected. Create new schema or extend existing tables?",
                "options": ["new_schema", "extend_existing", "external_db"],
            },
            {
                "trigger": {"type": "file_exists", "pattern": "**/api/routes/**"},
                "question": "Existing API routes found. Follow current patterns or establish new conventions?",
                "options": ["follow_existing", "new_conventions"],
            },
            {
                "trigger": {"type": "always"},
                "question": "What entity/resource is being managed?",
                "options": ["freeform"],
            },
        ],
    },
    "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"],
        "probe_questions": [
            {
                "trigger": {"type": "file_missing", "pattern": "**/auth/**"},
                "question": "No existing auth module detected. Greenfield implementation or integrating with external IdP?",
                "options": ["greenfield", "external_idp", "migrate_existing"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["fastapi", "flask", "express"]},
                "question": "Framework detected. Use framework's built-in auth patterns or custom implementation?",
                "options": ["framework_native", "custom"],
            },
            {
                "trigger": {"type": "file_exists", "pattern": "**/models/user*"},
                "question": "User model exists. Add token storage to user table or separate sessions table?",
                "options": ["extend_user", "separate_sessions", "stateless_jwt"],
            },
            {
                "trigger": {"type": "always"},
                "question": "What happens to existing sessions during migration (if any)?",
                "options": ["invalidate_all", "graceful_migration", "no_existing_sessions"],
            },
        ],
    },
    "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",
        ],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "Which provider(s) are being integrated?",
                "options": ["freeform"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["webhook", "callback"]},
                "question": "Webhook endpoint strategy: dedicated routes or unified handler?",
                "options": ["dedicated_routes", "unified_handler"],
            },
            {
                "trigger": {"type": "file_missing", "pattern": "**/services/*client*"},
                "question": "No API client pattern detected. Use SDK or raw HTTP client?",
                "options": ["official_sdk", "http_client", "both"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Idempotency requirement: strict (database-backed) or eventual?",
                "options": ["strict_db", "eventual_dedup", "not_required"],
            },
        ],
    },
    "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"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "Production deployment: zero-downtime required or maintenance window available?",
                "options": ["zero_downtime", "maintenance_window", "dev_only"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["backfill", "transform"]},
                "question": "Data volume: how many rows need transformation?",
                "options": ["small_<10k", "medium_10k-1m", "large_>1m"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Rollback strategy: reversible migration or snapshot restore?",
                "options": ["reversible_down", "snapshot_restore", "forward_only"],
            },
        ],
    },
    "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"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "What's the target SLA (e.g., p95 < 100ms)?",
                "options": ["freeform"],
            },
            {
                "trigger": {"type": "file_missing", "pattern": "**/cache/**"},
                "question": "No caching layer detected. Add Redis/Memcached or in-memory cache?",
                "options": ["redis", "memcached", "in_memory", "no_cache"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Cache invalidation strategy: TTL-based, event-driven, or hybrid?",
                "options": ["ttl_based", "event_driven", "hybrid"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["query", "slow", "index"]},
                "question": "Is this primarily query optimization or application-level caching?",
                "options": ["query_optimization", "app_caching", "both"],
            },
        ],
    },
    "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"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "Versioning strategy: URL path (/v2/), header, or query param?",
                "options": ["url_path", "header", "query_param"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Deprecation period: how long to support old version?",
                "options": ["30_days", "90_days", "6_months", "indefinite"],
            },
            {
                "trigger": {"type": "file_exists", "pattern": "**/clients/**"},
                "question": "Client code detected in repo. Update clients in same PR or separate?",
                "options": ["same_pr", "separate_pr", "external_clients_only"],
            },
        ],
    },
    "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"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "Scope: single file, module, or cross-cutting?",
                "options": ["single_file", "module", "cross_cutting"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["rename"]},
                "question": "Rename scope: all references or public API only?",
                "options": ["all_references", "public_api_only"],
            },
        ],
    },
    "bug_fix": {
        "name": "Bug Fix",
        "description": "Fix broken functionality, crashes, or incorrect behavior",
        "keywords": [
            "fix",
            "bug",
            "broken",
            "issue",
            "error",
            "crash",
            "regression",
            "defect",
            "incorrect",
            "failing",
            "problem",
        ],
        "required_any": ["fix", "bug", "broken", "crash", "error", "regression"],
        "expected_components": {
            "problem": ["crash", "error", "broken", "failing", "incorrect", "regression"],
            "area": ["auth", "api", "database", "ui", "workflow", "validation"],
        },
        "systems_base": 0.5,
        "modifiers_default": ["error_handling"],
        "split_strategy": ["Reproduce bug", "Fix implementation", "Add regression test"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "Can you reproduce the bug consistently?",
                "options": ["yes_reproduce", "intermittent", "not_yet"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Is this a regression (previously working)?",
                "options": ["regression", "never_worked", "unknown"],
            },
            {
                "trigger": {"type": "keyword_detected", "keywords": ["crash", "error"]},
                "question": "Do we have logs or stack traces?",
                "options": ["have_logs", "no_logs", "partial_info"],
            },
        ],
    },
    "refactor": {
        "name": "Refactor",
        "description": "Replace or rewrite implementation while preserving behavior",
        "keywords": [
            "refactor",
            "replace",
            "rewrite",
            "improve",
            "clean",
            "restructure",
            "reorganize",
            "swap",
            "substitute",
            "modernize",
        ],
        "required_any": ["replace", "rewrite", "swap", "substitute", "refactor"],
        "expected_components": {
            "operation": ["replace", "rewrite", "swap", "substitute"],
            "target": ["implementation", "approach", "library", "framework", "pattern"],
        },
        "systems_base": 1.0,
        "modifiers_default": [],
        "split_strategy": ["Implement replacement", "Update call sites", "Remove old implementation"],
        "probe_questions": [
            {
                "trigger": {"type": "always"},
                "question": "What is being replaced?",
                "options": ["freeform"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Replacement strategy: parallel (side-by-side) or sequential (in-place)?",
                "options": ["parallel_then_switch", "in_place"],
            },
            {
                "trigger": {"type": "always"},
                "question": "Are there existing tests to verify behavior preservation?",
                "options": ["tests_exist", "need_tests", "no_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
# =============================================================================


# Architectural boundary signals used by estimate_systems() to infer how many
# distinct systems a directive touches when no pattern matches.  Each category
# that fires counts as one system boundary.
SYSTEM_SIGNALS: dict[str, list[str]] = {
    "abstraction": ["interface", "abstraction", "abstract", "protocol", "contract"],
    "discovery": ["registry", "discover", "plugin", "loader", "dynamic"],
    "observability": [
        "decorator",
        "instrument",
        "monitor",
        "metrics",
        "analytics",
        "telemetry",
        "profil",
    ],
    "streaming": ["stream", "real-time", "realtime", "websocket", "sse", "event-driven"],
    # "service" removed — matches "customer service"; "api" kept as it reliably
    # indicates backend work even in documentation-adjacent directives.
    "backend_layer": ["backend", "api", "endpoint", "route", "microservice"],
    "frontend_layer": ["frontend", "ui", "component", "page", "form", "tui", "terminal"],
    # "model" removed — matches "machine learning model"; "orm" added as precise signal.
    "data_layer": ["database", "db", "schema", "migration", "orm", "table"],
    "auth_layer": ["auth", "token", "session", "credential", "jwt", "oauth"],
    "infrastructure": ["cache", "queue", "worker", "job", "celery"],
    # "serial" removed — matches "serial port"; "serializ" kept to catch serialize/serialization.
    "io_layer": ["export", "import", "format", "parse", "serializ"],
    # "command" removed — matches "command pattern"; "subcommand"/"argparse"/"click" added.
    "cli_layer": ["cli", "subcommand", "argparse", "click"],
    "security_layer": ["security", "validation", "scan", "audit", "policy"],
    "config_layer": ["config", "setting", "preference"],
}


def estimate_systems(directive: str) -> int:
    """Estimate the number of architectural system boundaries in a directive.

    Scans the directive text for signals that indicate distinct system layers.
    Each signal category that fires counts as one system boundary.  Used as the
    fallback when no pattern in the library matches.

    Special case: If directive mentions specific file(s) with extension:
    - Single file: cap systems at 1 (scoped to one file)
    - Multiple files: use max(base_count, num_files) to account for file organization

    Returns:
        System count clamped to [1, 6].
    """
    directive_lower = directive.lower()

    # Check for file extension pattern (word.ext where ext is 2-4 chars)
    import re
    file_extensions = r'\w+\.(ts|tsx|js|jsx|py|java|go|rs|rb|php|cpp|c|h|kt|swift|cs|m|scala)'
    file_matches = re.findall(file_extensions, directive_lower)

    # Calculate base system count from signals
    base_count = sum(
        1
        for keywords in SYSTEM_SIGNALS.values()
        if any(kw in directive_lower for kw in keywords)
    )

    if len(file_matches) == 1:
        # Single file mention - cap at 1 system
        return 1
    elif len(file_matches) > 1:
        # Multiple files - don't cap, but recognize file organization
        # Each file is likely its own system or subsystem
        return max(1, min(max(base_count, len(file_matches)), 6))

    # No files mentioned, use normal estimation
    return max(1, min(base_count, 6))


def match_pattern(directive: str, return_all_matches: bool = False) -> PatternMatch | list[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 specific file mention (Bug Fix pattern boost)
    - -0.15 for vague terms

    For 0.90+ confidence, all expected_components categories must be covered.

    Args:
        directive: The directive to match
        return_all_matches: If True, return all matches >= 0.80 sorted by confidence

    Returns:
        Best matching pattern, or list if return_all_matches=True, or None
    """
    directive_lower = directive.lower()
    matches = []  # Collect all matches >= threshold

    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

        # Bug Fix pattern: file-specific confidence boost
        # When a specific file is mentioned, the fix is more clearly scoped
        if pattern_id == "bug_fix":
            import re
            file_pattern = r'\w+\.(ts|tsx|js|jsx|py|java|go|rs|rb|php|cpp|c|h|kt|swift|cs|m|scala)'
            if re.search(file_pattern, directive_lower):
                confidence += 0.15

        # 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))

        # Determine threshold per pattern (Bug Fix has lower threshold)
        threshold = 0.65 if pattern_id == "bug_fix" else 0.80

        if confidence >= threshold:
            matches.append(PatternMatch(
                pattern_id=pattern_id,
                name=pattern["name"],
                confidence=confidence,
                keywords_matched=keywords_matched,
                systems=pattern["systems_base"],
                modifiers=pattern["modifiers_default"],
            ))

    if not matches:
        return None if not return_all_matches else []

    # Sort by confidence descending
    matches.sort(key=lambda m: m.confidence, reverse=True)

    if return_all_matches:
        return matches
    else:
        return matches[0]  # Return highest confidence


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.

    Args:
        systems: Number of distinct architectural boundaries
        modifiers: List of complexity modifier names

    Returns:
        Complexity score, capped at 100.0 to prevent downstream issues
    """
    modifier_count = len(modifiers)
    complexity = (1 + systems) * (1 + 0.5 * modifier_count)
    # Cap complexity at 100.0 to prevent integer overflow in downstream calculations
    complexity = min(complexity, 100.0)
    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,
    }


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

    Args:
        directive: The task directive to assess (must be non-empty)
        threshold: Complexity threshold for cleave decision (default: 2.0)
        validate: Whether to add validation step offset (default: True)

    Returns:
        AssessmentResult with complexity, pattern match, and decision

    Raises:
        ValueError: If directive is empty or whitespace-only
    """
    # Validate input - fail fast on empty directives
    if not directive or not directive.strip():
        raise ValueError("Directive cannot be empty or whitespace-only")

    match = match_pattern(directive)

    # match_pattern already applies pattern-specific thresholds
    # If it returned a match, it passed the threshold check
    if match:
        detected_modifiers = detect_modifiers(directive)
        all_modifiers = list(set(match.modifiers + detected_modifiers))

        # Check file detection to override pattern systems if needed
        import re
        file_extensions = r'\w+\.(ts|tsx|js|jsx|py|java|go|rs|rb|php|cpp|c|h|kt|swift|cs|m|scala)'
        file_matches = re.findall(file_extensions, directive.lower())

        # systems_for_calc: used for complexity calculation (can be fractional)
        # systems_for_display: used for reporting (always >= 1)
        systems_for_calc = match.systems
        systems_for_display = match.systems

        if len(file_matches) == 1:
            # Single file mention - cap at 1 system
            systems_for_calc = max(1, match.systems)
            systems_for_display = 1
        elif len(file_matches) > 1:
            # Multiple files - use max of pattern systems and file count
            systems_for_calc = max(match.systems, len(file_matches))
            systems_for_display = max(match.systems, len(file_matches))
        else:
            # No file mention - keep pattern systems for calc, round up for display
            systems_for_display = max(1, match.systems)

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

        result = AssessmentResult(
            complexity=complexity,
            systems=systems_for_display,
            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: {systems_for_display}, Modifiers: {len(all_modifiers)}. "
            f"Formula: (1 + {systems_for_calc}) * (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 = estimate_systems(directive)
        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
        )
