"""Workspace module - workspace generation and context reconstruction."""

from __future__ import annotations

from datetime import datetime
from pathlib import Path

from cleave.core.assessment import PATTERNS, assess_directive, match_pattern
from cleave.core.permissions import infer_permissions
from cleave.core.yaml_utils import parse_yaml_simple, to_yaml

# =============================================================================
# INTENT SCAFFOLDS - Pattern-specific templates
# =============================================================================

INTENT_SCAFFOLDS = {
    "full_stack_crud": {
        "success_criteria": [
            "Users can create new {entity} via UI form",
            "Users can view {entity} list and details",
            "Users can update existing {entity}",
            "Users can delete {entity} with confirmation",
            "All operations persist to database correctly",
        ],
        "constraints": [
            "Use existing database schema conventions",
            "Follow established API patterns",
            "Maintain consistent UI/UX with existing pages",
        ],
        "out_of_scope": [
            "Bulk operations",
            "Advanced filtering/search",
            "Export functionality",
        ],
    },
    "authentication": {
        "success_criteria": [
            "Users can register with valid credentials",
            "Users can log in and receive auth token",
            "Protected routes reject unauthenticated requests",
            "Tokens expire and refresh correctly",
            "Passwords are securely hashed",
        ],
        "constraints": [
            "Use industry-standard hashing (bcrypt/argon2)",
            "Tokens must be signed and validated",
            "No plaintext credentials in logs or responses",
        ],
        "out_of_scope": [
            "Social login (OAuth providers)",
            "Multi-factor authentication",
            "Password recovery flow",
        ],
    },
    "external_integration": {
        "success_criteria": [
            "API client successfully authenticates with provider",
            "Core operations complete without errors",
            "Webhooks are received and processed",
            "Failures are logged and retried appropriately",
            "Transaction records are stored for audit",
        ],
        "constraints": [
            "Handle rate limits gracefully",
            "Implement idempotency for webhooks",
            "Never expose API keys in client code",
        ],
        "out_of_scope": [
            "Multi-provider support",
            "Admin dashboard for monitoring",
            "Historical data migration",
        ],
    },
    "database_migration": {
        "success_criteria": [
            "Migration applies cleanly to dev/staging/prod",
            "Rollback script works correctly",
            "No data loss during migration",
            "Performance impact is acceptable",
        ],
        "constraints": [
            "Must be reversible (rollback plan required)",
            "Zero-downtime if production",
            "Test on copy of production data first",
        ],
        "out_of_scope": [
            "Application code changes (separate task)",
            "Performance optimization (beyond index)",
        ],
    },
    "performance_optimization": {
        "success_criteria": [
            "Target latency SLA is met (e.g., p95 < 100ms)",
            "Cache hit rate exceeds threshold",
            "No stale data served beyond TTL",
            "Monitoring dashboards show improvement",
        ],
        "constraints": [
            "Cache invalidation must be consistent",
            "Graceful degradation if cache unavailable",
            "Memory usage within limits",
        ],
        "out_of_scope": [
            "Infrastructure scaling",
            "Database query optimization (separate task)",
            "CDN configuration",
        ],
    },
    "breaking_api_change": {
        "success_criteria": [
            "New versioned endpoint works correctly",
            "Old endpoint returns deprecation warning",
            "All clients updated to new endpoint",
            "Documentation updated for both versions",
        ],
        "constraints": [
            "Maintain backward compatibility for deprecation period",
            "Clearly document migration path",
            "Version must be in URL or header",
        ],
        "out_of_scope": [
            "Automatic client migration",
            "Breaking changes to data format",
        ],
    },
    "simple_refactor": {
        "success_criteria": [
            "All tests pass after refactor",
            "No functional changes (behavior preserved)",
            "Code follows project conventions",
            "All call sites updated",
        ],
        "constraints": [
            "Mechanical changes only - no logic changes",
            "Single commit for atomic change",
            "Update imports/references consistently",
        ],
        "out_of_scope": [
            "Functional improvements",
            "Performance optimization",
            "Adding new tests",
        ],
    },
}


def generate_intent_scaffold(directive: str, pattern_id: str | None) -> dict:
    """Generate pattern-aware intent scaffold.

    If pattern is matched, uses pattern-specific templates.
    Otherwise, provides generic TODOs.
    """
    goal = directive.split(".")[0] if "." in directive else directive
    if len(goal) > 100:
        goal = goal[:100] + "..."

    if pattern_id and pattern_id in INTENT_SCAFFOLDS:
        scaffold = INTENT_SCAFFOLDS[pattern_id]
        return {
            "goal": goal,
            "success_criteria": scaffold["success_criteria"],
            "constraints": scaffold["constraints"],
            "out_of_scope": scaffold["out_of_scope"],
        }
    else:
        return {
            "goal": goal,
            "success_criteria": [
                "TODO: Define testable outcome 1",
                "TODO: Define testable outcome 2",
                "TODO: Define testable outcome 3",
            ],
            "constraints": ["TODO: Add hard constraints"],
            "out_of_scope": ["TODO: Add explicit exclusions"],
        }


def generate_manifest(
    directive: str,
    children: list[str],
    mode: str = "lean",
    threshold: float = 2.0,
    validate: bool = True,
    max_depth: int = 5,
    parallel: bool = True,
    parent_manifest: dict | None = None,
    node_id: str = "root",
    depth: int = 0,
    include_permissions: bool = False,
) -> str:
    """Generate manifest.yaml content with pattern-aware scaffolding.

    Args:
        directive: The directive to decompose
        children: List of child labels
        mode: lean or robust
        threshold: Complexity threshold for cleaving
        validate: Add validation step
        max_depth: Maximum recursion depth
        parallel: Execute children in parallel
        parent_manifest: Parent's manifest for nested cleaves (inherits intent)
        node_id: This node's ID in the tree (e.g., "0.1.2")
        depth: Current depth in the tree (0 = root)
        include_permissions: Include inferred permissions in manifest
    """
    assessment = assess_directive(directive, threshold, validate)

    # Get pattern ID for intent scaffolding
    match = match_pattern(directive)
    pattern_id = match.pattern_id if match else None

    # Inherit intent from parent (immutable) or generate fresh
    if parent_manifest and "intent" in parent_manifest:
        intent = parent_manifest["intent"]  # IMMUTABLE - copied verbatim
    else:
        intent = generate_intent_scaffold(directive, pattern_id)

    # Build ancestry chain
    parent_chain = []
    if parent_manifest and "ancestry" in parent_manifest:
        # Copy parent's chain and add parent as new entry
        parent_chain = parent_manifest["ancestry"].get("parent_chain", [])[:]
        parent_chain.append(
            {
                "node_id": parent_manifest["ancestry"].get("node_id", "unknown"),
                "label": parent_manifest.get("branch_label", "parent"),
                "depth": parent_manifest["ancestry"].get("depth", depth - 1),
                "directive_summary": parent_manifest.get("root_directive", directive)[:100] + "...",
            }
        )

    # Inherit root_directive from parent or use this directive
    root_directive = directive
    if parent_manifest and "root_directive" in parent_manifest:
        root_directive = parent_manifest["root_directive"]

    manifest = {
        "version": 1,
        "mode": mode,
        "threshold": threshold,
        "validate": validate,
        "max_splits": len(children),
        "max_depth": max_depth,
        "max_retries": 2,
        "parallel": parallel,
        "halt_on_failure": False,
        "root_directive": root_directive,
        "branch_directive": directive if depth > 0 else None,  # Only for non-root
        "branch_label": children[0] if children else "task",  # Label for this branch
        "intent": intent,
        "assessment": {
            "complexity": assessment.complexity,
            "systems": assessment.systems,
            "modifiers": assessment.modifiers,
            "method": assessment.method,
            "pattern": assessment.pattern,
            "confidence": assessment.confidence,
            "decision": assessment.decision,
            "reasoning": assessment.reasoning,
        },
        "ancestry": {
            "depth": depth,
            "node_id": node_id,
            "parent_chain": parent_chain,
            "sibling_count": len(children),
            "remaining_budget": max_depth - depth,
        },
        "children": [{"id": i, "label": label, "depth": depth + 1} for i, label in enumerate(children)],
        "created_at": datetime.now().isoformat(),
    }

    # Add inferred permissions if requested
    if include_permissions:
        perms = infer_permissions(directive, pattern_id)
        manifest["inferred_permissions"] = perms

    return to_yaml(manifest)


def generate_task_file(
    task_id: int,
    label: str,
    mission: str,
    siblings: list[str],
    depth: int = 1,
    tdd: bool = True,
) -> str:
    """Generate N-task.md content.

    Args:
        task_id: Numeric ID for this task
        label: Human-readable task label
        mission: Task mission statement
        siblings: List of sibling task labels
        depth: Depth in cleave tree
        tdd: Include TDD workflow instructions (default: True)
    """
    sibling_refs = [i for i in range(len(siblings)) if i != task_id]
    sibling_list = "\n".join([f"- Sibling {i}: {siblings[i]}" for i in sibling_refs])

    # TDD workflow section (on by default)
    tdd_section = ""
    if tdd:
        tdd_section = """
## Workflow (TDD)

**Red -> Green -> Refactor**

1. **Write failing test(s)** that define expected behavior
2. **Implement minimum code** to make tests pass
3. **Refactor** if needed while keeping tests green
4. **Document** verification evidence in Result section

Tests are documentation of intent. Write them first.
"""

    # Verification section - proof of work (always included, richer with TDD)
    verification_section = """
**Verification:**
"""
    if tdd:
        verification_section += """- Test command: `[command to run tests]`
- Test output: [PASS/FAIL - summary]
- Coverage: [% or N/A]
- Edge cases tested: [list]
"""
    else:
        verification_section += """- Verification command: `[command to verify this works]`
- Verification output: [result summary]
"""

    return f"""---
task_id: {task_id}
ancestry:
  depth: {depth}
  parent_manifest: ../manifest.yaml
siblings: {sibling_refs}
tdd: {str(tdd).lower()}
---

# Task {task_id}: {label}

## Root Intent (Reference)

See manifest.yaml for:
- Goal: [inherited from root]
- Success Criteria: [applicable outcomes]
- Constraints: [applicable rules]

## Mission

{mission}
{tdd_section}
## Scope

**In scope:**
- TODO: Define scope

**Out of scope:**
- TODO: Define exclusions

## Sibling Coordination

Working with:
{sibling_list}

**Depends on:** [TODO: interfaces needed from siblings]
**Must provide:** [TODO: interfaces to siblings]

## Execution Notes

[Sequential thinking summary, key decisions made during execution]

## Result

**Status:** PENDING

**Summary:** [What was accomplished]

**Artifacts:**
- [Files created/modified with full paths]

**Interfaces Published:**
- `function_name(params) -> return_type: description`

**Decisions Made:**
- [Key choices with rationale]

**Assumptions:**
- [What was assumed to be true]
{verification_section}
**Alignment Check:**
- Aligns with root goal? YES/NO - [explanation]
- Violates constraints? YES/NO - [which ones, if any]
- Success criteria achievable? YES/NO - [how]
"""


def generate_siblings_yaml(children: list[str]) -> str:
    """Generate siblings.yaml."""
    siblings_data = {
        "coordination": {
            "created_at": datetime.now().isoformat(),
            "parent_node": "root",
            "depth": 1,
            "mode": "parallel",
        },
        "siblings": [
            {
                "id": i,
                "label": label,
                "focus": f"TODO: Describe {label} focus",
                "status": "pending",
                "exports": {"interfaces": [], "artifacts": []},
                "dependencies": [],
                "file_claims": [],
            }
            for i, label in enumerate(children)
        ],
        "conflicts": [],
        "messages": [],
    }

    return to_yaml(siblings_data)


def init_workspace(
    directive: str,
    children: list[str],
    output_dir: str = ".cleave",
    mode: str = "lean",
    threshold: float = 2.0,
    max_depth: int = 5,
    parent_manifest_path: str | None = None,
    node_id: str = "root",
    depth: int = 0,
    include_permissions: bool = False,
    tdd: bool = True,
) -> dict:
    """Initialize cleave workspace.

    Args:
        directive: The directive to decompose
        children: List of child task labels
        output_dir: Output directory for workspace files
        mode: lean or robust
        threshold: Complexity threshold
        max_depth: Maximum recursion depth
        parent_manifest_path: Path to parent's manifest.yaml (for nested cleaves)
        node_id: This node's ID in the tree (e.g., "0.1.2")
        depth: Current depth in the tree (0 = root)
        include_permissions: Include inferred permissions in manifest
        tdd: Include TDD workflow in task files (default: True)

    Returns:
        dict with created files, or error dict if MAX_DEPTH exceeded
    """
    # MAX_DEPTH enforcement
    if depth >= max_depth:
        return {
            "error": "MAX_DEPTH_EXCEEDED",
            "message": f"Cannot cleave at depth {depth}: max_depth is {max_depth}",
            "guidance": "Either increase max_depth, or execute this task atomically without cleaving",
        }

    # Load parent manifest if provided (for nested cleaves)
    parent_manifest = None
    if parent_manifest_path:
        parent_path = Path(parent_manifest_path)
        if parent_path.exists():
            parent_manifest = parse_yaml_simple(parent_path.read_text())
            # Inherit max_depth from parent if not explicitly set
            if "max_depth" in parent_manifest:
                max_depth = parent_manifest["max_depth"]
            # Re-check depth after loading parent's max_depth
            if depth >= max_depth:
                return {
                    "error": "MAX_DEPTH_EXCEEDED",
                    "message": f"Cannot cleave at depth {depth}: inherited max_depth is {max_depth}",
                    "guidance": "Execute this task atomically without further cleaving",
                }

    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    created_files = {}

    # manifest.yaml
    manifest_content = generate_manifest(
        directive=directive,
        children=children,
        mode=mode,
        threshold=threshold,
        max_depth=max_depth,
        parent_manifest=parent_manifest,
        node_id=node_id,
        depth=depth,
        include_permissions=include_permissions,
    )
    manifest_path = output_path / "manifest.yaml"
    manifest_path.write_text(manifest_content)
    created_files["manifest.yaml"] = str(manifest_path)

    # siblings.yaml
    siblings_content = generate_siblings_yaml(children)
    siblings_path = output_path / "siblings.yaml"
    siblings_path.write_text(siblings_content)
    created_files["siblings.yaml"] = str(siblings_path)

    # Task files (children are at depth + 1)
    child_depth = depth + 1
    for i, label in enumerate(children):
        task_content = generate_task_file(
            task_id=i,
            label=label,
            mission=f"TODO: Define specific mission for {label}",
            siblings=children,
            depth=child_depth,
            tdd=tdd,
        )
        task_path = output_path / f"{i}-task.md"
        task_path.write_text(task_content)
        created_files[f"{i}-task.md"] = str(task_path)

    # metrics.yaml
    metrics = {
        "telemetry": {
            "created_at": datetime.now().isoformat(),
            "ancestry_access_count": 0,
            "sibling_access_count": 0,
            "intent_reference_count": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "fast_path_hits": 0,
            "sequential_fallback": 0,
        }
    }
    metrics_path = output_path / "metrics.yaml"
    metrics_path.write_text(to_yaml(metrics))
    created_files["metrics.yaml"] = str(metrics_path)

    return created_files


def reconstruct_context(manifest_path: str) -> dict:
    """Reconstruct full context from manifest."""
    path = Path(manifest_path)
    if not path.exists():
        return {"error": f"Manifest not found: {manifest_path}"}

    content = path.read_text()
    manifest = parse_yaml_simple(content)

    context = {
        "root_intent": manifest.get("intent", {}),
        "ancestry": manifest.get("ancestry", {}),
        "current_node": manifest.get("ancestry", {}).get("node_id", "unknown"),
        "depth": manifest.get("ancestry", {}).get("depth", 0),
        "parent_chain": manifest.get("ancestry", {}).get("parent_chain", []),
        "constraints": manifest.get("intent", {}).get("constraints", []),
        "assessment": manifest.get("assessment", {}),
        "children": manifest.get("children", []),
    }

    siblings_path = path.parent / "siblings.yaml"
    if siblings_path.exists():
        context["siblings"] = parse_yaml_simple(siblings_path.read_text())

    return context
