"""
Stage 4: Component Generation Agent
=====================================
Generates React + Tailwind component variants within the approved token set.
Each component type gets 2-3 variants with different layout approaches.
Components start page-specific and promote to shared when reused.

The agent:
- Reads IA to identify needed component types per screen
- Generates variants constrained to the approved token set
- Runs evaluation before presenting to user
- Manages the component registry and promotion lifecycle
"""

import json
import os
from datetime import datetime


COMPONENT_SYSTEM_PROMPT = """You are a senior UI engineer and designer working as a design partner. You are in Stage 4: Component Generation — your job is to build React + Tailwind components within the approved design system.

You have the approved brief, IA, and token set injected below. Every component you generate MUST use only values from the approved token set.

<role>
You are not a code generator. You are a designer who implements. Every component decision — padding, font size, border radius, color — should trace back to either the token set or a deliberate design rationale. You think about:
- How the component serves the user's task (from the brief)
- How it fits within the screen structure (from the IA)
- How it maintains consistency with the visual system (from the tokens)
- How it behaves at different breakpoints (from the responsive strategy)
- How it handles edge cases (long text, missing images, loading states, empty states)
</role>

<process>
1. IDENTIFY component types needed from the approved IA:
   For each screen, list every distinct component pattern:
   - Cards (product, recipe, summary, etc.)
   - Navigation (top bar, tab bar, breadcrumbs, back button)
   - Inputs (search, text field, select, filter chips)
   - Buttons (primary, secondary, text, icon)
   - Lists (horizontal scroll, vertical list, grid)
   - Media (image with overlay, thumbnail, hero banner)
   - Feedback (toast, modal, bottom sheet, empty state)
   - Layout (section header, divider, spacing container)

2. For each component type, GENERATE 2-3 VARIANTS:
   Each variant uses the SAME tokens but differs in:
   - Layout approach (vertical vs horizontal, compact vs spacious)
   - Information density (full detail vs summary vs minimal)
   - Interaction pattern (tap entire card vs specific CTA, expand vs navigate)
   
   CRITICAL RULES:
   - All colors must come from the token set semantic colors
   - All font sizes must come from the token type scale
   - All spacing must come from the token spacing scale
   - All border radii must come from the token radius scale
   - No hardcoded values — everything traces to a token
   - Components must be responsive (work at mobile and desktop breakpoints)
   - All interactive elements must meet 44px minimum touch target on mobile
   - All text must meet WCAG AA contrast against its background

3. Each component should be a SELF-CONTAINED React functional component:
   - Default export
   - Props for all dynamic content (title, image, price, etc.)
   - Sensible default props for preview rendering
   - Tailwind classes only (no inline styles except for token custom properties)
   - Responsive: use Tailwind breakpoint prefixes (sm:, md:, lg:)

4. For each variant, describe:
   - When to use it (which screens, which contexts)
   - What it optimizes for (scan speed, detail, engagement)
   - Edge case handling (what happens with 3 words vs 30 words as title?)
   - Accessibility considerations

5. PRESENT variants with your recommendation for which to use where.
</process>

<component_format>
```jsx
// ProductCard.jsx
// Variant: Standard Vertical Card
// Use: Product grid on Home and Search Results screens
// Optimizes for: Visual browsing, quick scanning

import React from 'react';

export default function ProductCard({ 
  title = 'Product Name',
  price = '$0.00',
  image = '/placeholder.jpg',
  category = 'Category',
  onPress = () => {},
}) {
  return (
    <button 
      onClick={onPress}
      className="group w-full text-left bg-surface rounded-lg border border-border 
                 hover:shadow-md transition-shadow duration-200
                 focus:outline-none focus:ring-2 focus:ring-primary/50"
    >
      <div className="aspect-[4/3] overflow-hidden rounded-t-lg bg-background">
        <img src={image} alt={title} 
             className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
      </div>
      <div className="p-16 space-y-8">
        <p className="text-xs text-text-secondary uppercase tracking-wide font-medium">
          {category}
        </p>
        <h3 className="text-base font-semibold text-text-primary line-clamp-2">
          {title}
        </h3>
        <p className="text-lg font-bold text-primary">
          {price}
        </p>
      </div>
    </button>
  );
}
```
</component_format>

<registry_management>
When the user approves a component:
1. Record it in the component registry with metadata
2. Place it under the appropriate page folder initially: pages/{page}/components/{ComponentName}/
3. When a second page needs it, propose promotion to components/{feature}/

Registry entry format:
{
  "id": "product_card_standard",
  "type": "card",
  "variant": "standard_vertical",
  "path": "pages/home/components/ProductCard/ProductCard.jsx",
  "location": "page_specific",
  "status": "approved",
  "used_in": ["home"],
  "tags": ["product", "browsing", "grid-item"],
  "props": ["title", "price", "image", "category", "onPress"],
  "breakpoints": ["mobile", "desktop"],
  "eval_status": "passed",
  "notes": "Recommended for product grids. Use CompactCard for list views."
}
</registry_management>

<opinion_guidelines>
Push back when:
- A component tries to show too much information at once
- Interactive areas are too small for touch (< 44px)
- Text contrast doesn't meet WCAG AA
- A component doesn't handle edge cases (empty, loading, error)
- The user wants a pattern that fights the established IA structure
- A component works on desktop but breaks on mobile

Advocate for:
- Graceful degradation (component still works with missing data)
- Loading skeletons over spinners (perceived performance)
- Clear interactive affordances (buttons should look tappable)
- Consistent patterns across screens (if cards swipe on Home, they should swipe everywhere)
- White space as a design element, not wasted space
</opinion_guidelines>

Remember: you are generating production React code, not wireframes. The components should be copy-pasteable into a real project with the approved Tailwind config."""


# ═══════════════════════════════════════════════════════════
# CONTEXT BUILDER
# ═══════════════════════════════════════════════════════════

def load_component_context(project_path: str) -> str:
    """Load all upstream context for component generation."""
    parts = []
    
    # Brief (for persona and product context)
    brief_path = os.path.join(project_path, "brief", "brief.json")
    if os.path.exists(brief_path):
        with open(brief_path) as f:
            brief = json.load(f)
        parts.append("<approved_brief>")
        parts.append(json.dumps(brief, indent=2))
        parts.append("</approved_brief>")
    
    # Token set (the constraint system)
    tokens_path = os.path.join(project_path, "tokens", "selected.json")
    if os.path.exists(tokens_path):
        with open(tokens_path) as f:
            tokens = json.load(f)
        parts.append("<approved_tokens>")
        parts.append(json.dumps(tokens, indent=2))
        parts.append("</approved_tokens>")
    
    # Tailwind config
    config_path = os.path.join(project_path, "tokens", "tailwind.config.js")
    if os.path.exists(config_path):
        with open(config_path) as f:
            config = f.read()
        parts.append("<tailwind_config>")
        parts.append(config)
        parts.append("</tailwind_config>")
    
    # IA screens (what components are needed)
    screens_dir = os.path.join(project_path, "architecture", "screens")
    if os.path.exists(screens_dir):
        parts.append("<approved_ia>")
        for screen_name in sorted(os.listdir(screens_dir)):
            screen_dir = os.path.join(screens_dir, screen_name)
            if not os.path.isdir(screen_dir) or screen_name.startswith('_'):
                continue
            
            # Load mobile IA (more constrained, drives component design)
            mobile_path = os.path.join(screen_dir, f"{screen_name}_mobile.json")
            if os.path.exists(mobile_path):
                with open(mobile_path) as f:
                    mobile_ia = json.load(f)
                parts.append(f"\n### Screen: {screen_name} (mobile)")
                parts.append(json.dumps(mobile_ia, indent=2))
        parts.append("</approved_ia>")
    
    # Existing component registry
    registry_path = os.path.join(project_path, "components", "registry.json")
    if os.path.exists(registry_path):
        with open(registry_path) as f:
            registry = json.load(f)
        if registry.get("components"):
            parts.append("<existing_components>")
            parts.append(json.dumps(registry, indent=2))
            parts.append("</existing_components>")
    
    # Responsive strategy
    strategy_path = os.path.join(project_path, "architecture", "responsive_strategy.json")
    if os.path.exists(strategy_path):
        with open(strategy_path) as f:
            strategy = json.load(f)
        parts.append("<responsive_strategy>")
        parts.append(json.dumps(strategy, indent=2))
        parts.append("</responsive_strategy>")
    
    return "\n".join(parts)


# ═══════════════════════════════════════════════════════════
# COMPONENT MANAGEMENT
# ═══════════════════════════════════════════════════════════

def save_component(project_path: str, component_name: str, page_name: str,
                   code: str, registry_entry: dict):
    """Save a component to its page-specific location and register it."""
    # Create page component directory
    comp_dir = os.path.join(project_path, "pages", page_name, "components", component_name)
    os.makedirs(comp_dir, exist_ok=True)
    os.makedirs(os.path.join(comp_dir, "_rejected"), exist_ok=True)
    
    # Save component code
    code_path = os.path.join(comp_dir, f"{component_name}.jsx")
    with open(code_path, 'w') as f:
        f.write(code)
    
    # Update registry entry with path
    registry_entry["path"] = f"pages/{page_name}/components/{component_name}/{component_name}.jsx"
    registry_entry["location"] = "page_specific"
    registry_entry["created"] = datetime.now().isoformat()
    if "status" not in registry_entry:
        registry_entry["status"] = "approved"
    
    # Add to registry
    registry_path = os.path.join(project_path, "components", "registry.json")
    with open(registry_path) as f:
        registry = json.load(f)
    
    # Check for existing entry with same id
    existing_ids = {c["id"] for c in registry["components"]}
    if registry_entry["id"] in existing_ids:
        registry["components"] = [c for c in registry["components"] if c["id"] != registry_entry["id"]]
    
    registry["components"].append(registry_entry)
    registry["last_updated"] = datetime.now().isoformat()
    
    with open(registry_path, 'w') as f:
        json.dump(registry, f, indent=2)
    
    print(f"  ✓ Saved: {component_name} → pages/{page_name}/components/{component_name}/")
    return code_path


def reject_component_variant(project_path: str, component_name: str, page_name: str,
                              variant_name: str, code: str, reason: str = ""):
    """Archive a rejected component variant."""
    rejected_dir = os.path.join(project_path, "pages", page_name, "components", 
                                 component_name, "_rejected")
    os.makedirs(rejected_dir, exist_ok=True)
    
    safe_variant = variant_name.lower().replace(" ", "_")
    path = os.path.join(rejected_dir, f"{safe_variant}.jsx")
    
    header = f"// REJECTED: {reason}\n// Date: {datetime.now().isoformat()}\n\n"
    with open(path, 'w') as f:
        f.write(header + code)
    
    print(f"  Archived rejected variant: {variant_name}")


def promote_component(project_path: str, component_id: str, feature_name: str):
    """Promote a page-specific component to shared (components/{feature}/)."""
    registry_path = os.path.join(project_path, "components", "registry.json")
    with open(registry_path) as f:
        registry = json.load(f)
    
    # Find the component
    comp = None
    for c in registry["components"]:
        if c["id"] == component_id:
            comp = c
            break
    
    if not comp:
        print(f"Component '{component_id}' not found in registry.")
        return
    
    old_path = os.path.join(project_path, comp["path"])
    if not os.path.exists(old_path):
        print(f"Component file not found: {old_path}")
        return
    
    # Create new location
    component_name = os.path.basename(os.path.dirname(old_path))
    new_dir = os.path.join(project_path, "components", feature_name, component_name)
    os.makedirs(new_dir, exist_ok=True)
    
    # Copy (don't move — keep original for reference)
    new_path = os.path.join(new_dir, f"{component_name}.jsx")
    with open(old_path) as f:
        code = f.read()
    with open(new_path, 'w') as f:
        f.write(code)
    
    # Update registry
    comp["path"] = f"components/{feature_name}/{component_name}/{component_name}.jsx"
    comp["location"] = "shared"
    comp["promoted_at"] = datetime.now().isoformat()
    comp["promoted_from"] = old_path
    
    registry["last_updated"] = datetime.now().isoformat()
    with open(registry_path, 'w') as f:
        json.dump(registry, f, indent=2)
    
    print(f"  ✓ Promoted {component_name} → components/{feature_name}/{component_name}/")


def get_registry(project_path: str) -> dict:
    """Load the component registry."""
    path = os.path.join(project_path, "components", "registry.json")
    with open(path) as f:
        return json.load(f)


# ═══════════════════════════════════════════════════════════
# STAGE COMPLETION
# ═══════════════════════════════════════════════════════════

def complete_components(project_path: str):
    """Verify components cover the IA and advance to Stage 5."""
    registry = get_registry(project_path)
    approved = [c for c in registry["components"] if c.get("status") == "approved"]
    
    if not approved:
        print("Cannot complete Stage 4 — no approved components.")
        return False
    
    import sys
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
    from scaffold import advance_stage, log_decision
    
    by_type = {}
    for c in approved:
        by_type.setdefault(c.get("type", "unknown"), []).append(c)
    
    summary = []
    for t, comps in sorted(by_type.items()):
        names = [c["id"] for c in comps]
        summary.append(f"- **{t}**: {', '.join(names)}")
    
    log_decision(project_path, "Components Approved",
        f"## Component Library\n\n"
        f"**{len(approved)} approved components** across {len(by_type)} types:\n\n" +
        "\n".join(summary)
    )
    
    advance_stage(project_path, "page_assembly",
                  f"Component generation complete. {len(approved)} components approved.")
    
    print(f"\n✓ Stage 4 complete. Project advanced to: page_assembly")
    return True


# ═══════════════════════════════════════════════════════════
# FULL PROMPT ASSEMBLY
# ═══════════════════════════════════════════════════════════

def get_full_prompt(project_path: str) -> str:
    """Assemble complete system prompt with all upstream context."""
    prompt = COMPONENT_SYSTEM_PROMPT
    context = load_component_context(project_path)
    if context:
        prompt += f"\n\n<project_context>\n{context}\n</project_context>"
    return prompt


if __name__ == "__main__":
    import sys as _sys
    if len(_sys.argv) > 1:
        project_path = _sys.argv[1]
        prompt = get_full_prompt(project_path)
        print(f"Component agent prompt: {len(prompt)} chars")
        
        registry = get_registry(project_path)
        count = len(registry.get("components", []))
        print(f"Components in registry: {count}")
    else:
        print("Usage: python components.py <project_path>")
