"""
Reference Loader Module

Loads and formats project references for Claude prompts.
Handles template loading, variant selection, and shared resources.
"""

from pathlib import Path
from typing import Optional, Tuple, Union
import json
import logging

from lib.reference_analysis import get_best_photos, get_photo_analysis, INDEX_FILENAME

# Priority order for selecting character reference images
_SHEET_PRIORITY = ["sheet.jpg", "portrait.jpg"]

# NanoBanana limits
NANOBANANA_MAX_IMAGES = 14
NANOBANANA_MAX_PEOPLE = 5

logger = logging.getLogger(__name__)

# Repository root (where templates/ lives)
REPO_ROOT = Path(__file__).parent.parent


def load_project_references(project_path: Path) -> dict:
    """
    Load all references from a project directory.

    Args:
        project_path: Path to project root (contains references/, brief.md, template.txt)

    Returns:
        Dictionary with structure:
        {
            "available": bool,
            "people": {person_name: {"name": str, "photos": [paths], "description": str}},
            "locations": [paths],
            "style": [paths],
            "brief": str or None,
            "template": str or None (e.g., "instagram-influencer/lifestyle")
        }
    """
    refs_path = project_path / "references"

    if not refs_path.exists():
        return {"available": False}

    result = {
        "available": True,
        "people": {},
        "locations": [],
        "objects": [],
        "style": [],
        "brief": None,
        "template": None,
        "index": None,
    }

    # Load template specification
    template_file = project_path / "template.txt"
    if template_file.exists():
        result["template"] = template_file.read_text().strip()
        logger.info(f"Loaded template: {result['template']}")

    # Load brief
    brief_file = project_path / "brief.md"
    if brief_file.exists():
        result["brief"] = brief_file.read_text()
        logger.info("Loaded brief.md")

    # Load people
    people_path = refs_path / "people"
    if people_path.exists():
        for person_dir in people_path.iterdir():
            if person_dir.is_dir():
                person_data = {
                    "name": person_dir.name,
                    "photos": [],
                    "description": None
                }

                # Load description
                desc_file = person_dir / "description.md"
                if desc_file.exists():
                    person_data["description"] = desc_file.read_text()

                # List photos (jpg, jpeg, png, webp)
                for ext in ["*.jpg", "*.jpeg", "*.png", "*.webp"]:
                    for photo in person_dir.glob(ext):
                        person_data["photos"].append(str(photo))

                if person_data["photos"] or person_data["description"]:
                    result["people"][person_dir.name] = person_data
                    logger.info(f"Loaded person '{person_dir.name}': {len(person_data['photos'])} photos")

    # Load locations
    locations_path = refs_path / "locations"
    if locations_path.exists():
        for ext in ["*.jpg", "*.jpeg", "*.png", "*.webp"]:
            for loc in locations_path.glob(ext):
                result["locations"].append(str(loc))
        if result["locations"]:
            logger.info(f"Loaded {len(result['locations'])} location references")

    # Load objects
    objects_path = refs_path / "objects"
    if objects_path.exists():
        for ext in ["*.jpg", "*.jpeg", "*.png", "*.webp"]:
            for obj in objects_path.glob(ext):
                result["objects"].append(str(obj))
        if result["objects"]:
            logger.info(f"Loaded {len(result['objects'])} object references")

    # Load style references
    style_path = refs_path / "style"
    if style_path.exists():
        for ext in ["*.jpg", "*.jpeg", "*.png", "*.webp"]:
            for style in style_path.glob(ext):
                result["style"].append(str(style))
        if result["style"]:
            logger.info(f"Loaded {len(result['style'])} style references")

    # Load analysis index if available and reorder photos by usefulness
    index_path = refs_path / INDEX_FILENAME
    if index_path.exists():
        try:
            index = json.loads(index_path.read_text())
            result["index"] = index
            logger.info(f"Loaded reference index ({len(index.get('photos', []))} entries)")

            # Reorder person photos by usefulness (best face references first)
            for person_name, person_data in result["people"].items():
                person_data["photos"] = _reorder_photos_by_index(
                    person_data["photos"], index, refs_path
                )
                # Attach per-photo analysis
                person_data["photo_analysis"] = _get_photo_analyses(
                    person_data["photos"], index, refs_path
                )

            # Reorder locations, objects, and style photos
            result["locations"] = _reorder_photos_by_index(
                result["locations"], index, refs_path
            )
            result["objects"] = _reorder_photos_by_index(
                result["objects"], index, refs_path
            )
            result["style"] = _reorder_photos_by_index(
                result["style"], index, refs_path
            )
        except (json.JSONDecodeError, OSError) as e:
            logger.warning(f"Failed to load reference index: {e}")

    return result


def _reorder_photos_by_index(photos: list[str], index: dict, refs_path: Path) -> list[str]:
    """Reorder photo list by usefulness from index (highest first).

    Photos not found in the index keep their original order and
    are placed after indexed photos.
    """
    scored = []
    unscored = []

    for photo_path in photos:
        # Convert absolute path to relative for index lookup
        try:
            rel = str(Path(photo_path).relative_to(refs_path))
        except ValueError:
            rel = photo_path

        analysis = get_photo_analysis(index, rel)
        if analysis:
            scored.append((photo_path, analysis.get("usefulness", 0)))
        else:
            unscored.append(photo_path)

    # Sort scored by usefulness descending
    scored.sort(key=lambda x: x[1], reverse=True)

    return [p for p, _ in scored] + unscored


def _get_photo_analyses(photos: list[str], index: dict, refs_path: Path) -> list[Optional[dict]]:
    """Get analysis entries for a list of photo paths (in same order)."""
    analyses = []
    for photo_path in photos:
        try:
            rel = str(Path(photo_path).relative_to(refs_path))
        except ValueError:
            rel = photo_path
        analyses.append(get_photo_analysis(index, rel))
    return analyses


def parse_template_spec(template_spec: str) -> Tuple[str, Optional[str]]:
    """
    Parse 'template/variant' or 'template' into components.

    Args:
        template_spec: Template specification string (e.g., "instagram-influencer/lifestyle")

    Returns:
        Tuple of (template_name, variant) - variant may be None
    """
    if "/" in template_spec:
        parts = template_spec.split("/", 1)
        return parts[0], parts[1]
    return template_spec, None


def load_template_archetype(template_name: str, repo_root: Path = REPO_ROOT) -> Optional[str]:
    """
    Load archetype.md from a template.

    Args:
        template_name: Name of the template (e.g., "instagram-influencer")
        repo_root: Path to repository root

    Returns:
        Content of archetype.md or None if not found
    """
    archetype_path = repo_root / "templates" / template_name / "archetype.md"
    if archetype_path.exists():
        return archetype_path.read_text()
    return None


def load_template_variant(template_name: str, variant: str, repo_root: Path = REPO_ROOT) -> Optional[str]:
    """
    Load variant-specific content from a template.

    Args:
        template_name: Name of the template
        variant: Name of the variant (e.g., "lifestyle")
        repo_root: Path to repository root

    Returns:
        Content of variant markdown file or None if not found
    """
    variant_path = repo_root / "templates" / template_name / "variants" / f"{variant}.md"
    if variant_path.exists():
        return variant_path.read_text()
    return None


def render_claude_md(story_name: str, template_spec: str, repo_root: Path = REPO_ROOT) -> str:
    """
    Render CLAUDE.md from base template + template-specific section.

    Args:
        story_name: Story/project name
        template_spec: Template spec (e.g., "instagram-influencer/lifestyle")
        repo_root: Path to repository root

    Returns:
        Rendered CLAUDE.md content
    """
    template_name, variant = parse_template_spec(template_spec)

    # Load base template
    base_path = repo_root / "templates" / "CLAUDE.md.template"
    if base_path.exists():
        base = base_path.read_text()
    else:
        base = "# {story_name}\n\nTemplate: {template_spec}\n\n{template_section}\n"

    # Load template-specific section
    template_section = ""
    tmpl_claude_path = repo_root / "templates" / template_name / "CLAUDE.md.template"
    if tmpl_claude_path.exists():
        template_section = tmpl_claude_path.read_text()

    # Render
    return base.replace("{story_name}", story_name).replace(
        "{template_spec}", template_spec
    ).replace("{template_section}", template_section)


def load_shared_resources(repo_root: Path = REPO_ROOT) -> dict:
    """
    Load shared resources (_shared/) for all templates.

    Args:
        repo_root: Path to repository root

    Returns:
        Dictionary mapping resource names to content:
        {
            "hook_archetypes": "...",
            "timing_rules": "...",
            "ai_constraints": "..."
        }
    """
    shared_path = repo_root / "templates" / "_shared"
    result = {}

    if shared_path.exists():
        for md_file in shared_path.glob("*.md"):
            result[md_file.stem] = md_file.read_text()
            logger.debug(f"Loaded shared resource: {md_file.stem}")

    return result


def load_template_examples(template_name: str, repo_root: Path = REPO_ROOT, variant: str = None) -> list:
    """
    Load example scene breakdowns from a template.

    Args:
        template_name: Name of the template
        repo_root: Path to repository root
        variant: Optional variant to filter examples by

    Returns:
        List of example dictionaries (parsed JSON)
    """
    examples_path = repo_root / "templates" / template_name / "examples"
    examples = []

    if examples_path.exists():
        for example_file in examples_path.glob("*.json"):
            try:
                example = json.loads(example_file.read_text())
                # Filter by variant if specified
                if variant and example.get("variant") != variant:
                    continue
                examples.append(example)
                logger.debug(f"Loaded example: {example_file.name}")
            except json.JSONDecodeError as e:
                logger.warning(f"Failed to parse example {example_file}: {e}")

    return examples


def list_available_templates(repo_root: Path = REPO_ROOT) -> list:
    """
    List all available templates.

    Returns:
        List of template names
    """
    templates_path = repo_root / "templates"
    templates = []

    if templates_path.exists():
        for item in templates_path.iterdir():
            if item.is_dir() and not item.name.startswith("_"):
                templates.append(item.name)

    return sorted(templates)


def list_template_variants(template_name: str, repo_root: Path = REPO_ROOT) -> list:
    """
    List all available variants for a template.

    Returns:
        List of variant names (empty if no variants)
    """
    variants_path = repo_root / "templates" / template_name / "variants"
    variants = []

    if variants_path.exists():
        for item in variants_path.glob("*.md"):
            variants.append(item.stem)

    return sorted(variants)


def is_story_mode(refs: dict) -> bool:
    """True when people/ has named characters (not just 'main').

    In product-promo mode, references/people/ contains only 'main'.
    In story adaptation mode, it contains named characters like 'tom_morrison', 'janie', etc.
    """
    people = refs.get("people", {})
    return bool(people) and "main" not in people


def _load_episode_assets(take_path: Path) -> Optional[dict]:
    """Load assets.json for the current episode via pipeline.json.

    Returns the parsed assets dict or None.
    """
    pipeline_file = take_path / "pipeline.json"
    if not pipeline_file.exists():
        return None

    try:
        pipeline = json.loads(pipeline_file.read_text())
    except (json.JSONDecodeError, OSError):
        return None

    assets_iter = pipeline.get("04-assets")
    if not assets_iter:
        return None

    assets_file = take_path / "04-assets" / assets_iter / "assets.json"
    if not assets_file.exists():
        return None

    try:
        return json.loads(assets_file.read_text())
    except (json.JSONDecodeError, OSError):
        return None


def get_episode_characters(take_path: Path, refs: dict) -> dict:
    """Read assets.json for character appears_in data.

    Returns {"all": [ids sorted by total appearances desc], "by_scene": {num: [ids]}}
    Only returns characters that have reference photos in refs["people"].

    Args:
        take_path: Path to take/episode directory
        refs: References dict from load_project_references()
    """
    result = {"all": [], "by_scene": {}}
    available_chars = set(refs.get("people", {}).keys())
    if not available_chars:
        return result

    assets = _load_episode_assets(take_path)
    if not assets:
        return result

    # Build appearance counts and by_scene mapping
    appearance_counts = {}
    by_scene = {}

    for char in assets.get("characters", []):
        char_id = char.get("id", "")
        if char_id not in available_chars:
            continue
        appears_in = char.get("appears_in", [])
        appearance_counts[char_id] = len(appears_in)
        for scene_num in appears_in:
            by_scene.setdefault(scene_num, []).append(char_id)

    # Sort by appearances descending (most important characters first)
    all_sorted = sorted(appearance_counts.keys(),
                        key=lambda cid: appearance_counts[cid], reverse=True)

    result["all"] = all_sorted
    result["by_scene"] = by_scene
    return result


def get_episode_locations(take_path: Path, refs: dict) -> dict:
    """Read assets.json for location appears_in data.

    Returns {"all": [ids sorted by appearances desc], "by_scene": {num: [ids]}}
    Only returns locations that have reference images in refs (matched by filename stem).

    Location refs are flat files like references/locations/scorpion_desert.jpg,
    matched to assets by filename stem == location id.
    """
    result = {"all": [], "by_scene": {}}
    # Build set of available location ids from file stems
    available = {Path(p).stem for p in refs.get("locations", [])}
    if not available:
        return result

    assets = _load_episode_assets(take_path)
    if not assets:
        return result

    appearance_counts = {}
    by_scene = {}

    for loc in assets.get("locations", []):
        loc_id = loc.get("id", "")
        if loc_id not in available:
            continue
        appears_in = loc.get("appears_in", [])
        appearance_counts[loc_id] = len(appears_in)
        for scene_num in appears_in:
            by_scene.setdefault(scene_num, []).append(loc_id)

    all_sorted = sorted(appearance_counts.keys(),
                        key=lambda lid: appearance_counts[lid], reverse=True)
    result["all"] = all_sorted
    result["by_scene"] = by_scene
    return result


def get_episode_objects(take_path: Path, refs: dict) -> dict:
    """Read assets.json for object appears_in data.

    Returns {"all": [ids sorted by appearances desc], "by_scene": {num: [ids]}}
    Only returns objects that have reference images in refs (matched by filename stem).
    """
    result = {"all": [], "by_scene": {}}
    available = {Path(p).stem for p in refs.get("objects", [])}
    if not available:
        return result

    assets = _load_episode_assets(take_path)
    if not assets:
        return result

    appearance_counts = {}
    by_scene = {}

    for obj in assets.get("objects", []):
        obj_id = obj.get("id", "")
        if obj_id not in available:
            continue
        appears_in = obj.get("appears_in", [])
        appearance_counts[obj_id] = len(appears_in)
        for scene_num in appears_in:
            by_scene.setdefault(scene_num, []).append(obj_id)

    all_sorted = sorted(appearance_counts.keys(),
                        key=lambda oid: appearance_counts[oid], reverse=True)
    result["all"] = all_sorted
    result["by_scene"] = by_scene
    return result


def select_character_refs(refs: dict, character_ids: list,
                          max_people: int = NANOBANANA_MAX_PEOPLE) -> list:
    """Pick best ref image per character.

    Returns [{"path": str, "character_id": str, "description": str}]
    Prefers sheet.jpg > portrait.jpg > any photo.
    Capped at max_people (NanoBanana limit: 5 people per request).

    Args:
        refs: References dict from load_project_references()
        character_ids: Character IDs in priority order (most important first)
        max_people: Maximum character sheets to include
    """
    result = []
    people = refs.get("people", {})

    for char_id in character_ids:
        if len(result) >= max_people:
            break
        person = people.get(char_id)
        if not person:
            continue

        photos = person.get("photos", [])
        if not photos:
            continue

        # Pick best image: sheet.jpg > portrait.jpg > first available
        best = None
        for preferred in _SHEET_PRIORITY:
            for photo in photos:
                if Path(photo).name == preferred:
                    best = photo
                    break
            if best:
                break
        if not best:
            best = photos[0]

        # Get first line of description for context
        desc = ""
        if person.get("description"):
            first_line = person["description"].strip().split("\n")[0]
            # Strip markdown heading prefix
            desc = first_line.lstrip("# ").strip()

        result.append({
            "path": best,
            "character_id": char_id,
            "description": desc,
        })

    return result


def select_asset_refs(refs: dict, asset_ids: list, asset_type: str) -> list:
    """Pick ref images for locations or objects by matching filename stem to asset id.

    Args:
        refs: References dict from load_project_references()
        asset_ids: Asset IDs in priority order
        asset_type: "locations" or "objects"

    Returns [{"path": str, "asset_id": str}]
    """
    # Build lookup: stem -> path
    by_stem = {}
    for path_str in refs.get(asset_type, []):
        stem = Path(path_str).stem
        by_stem[stem] = path_str

    result = []
    for asset_id in asset_ids:
        if asset_id in by_stem:
            result.append({"path": by_stem[asset_id], "asset_id": asset_id})
    return result


def select_asset_refs_with_fallback(
    refs: dict, asset_ids: list, asset_type: str,
    take_path: Path, frame_index: Optional[dict] = None,
) -> list:
    """Pick ref images with frame index fallback for unmatched assets.

    1. Standard stem matching via select_asset_refs()
    2. For unmatched: check assets.json frame_references (Claude-selected)
    3. For still unmatched: auto-fallback via get_frames_for_asset()

    Each result dict has an extra "source" key: "reference" or "frame".

    Args:
        refs: References dict from load_project_references()
        asset_ids: Asset IDs in priority order
        asset_type: "locations" or "objects"
        take_path: Path to episode/take directory
        frame_index: Pre-loaded frame index (or None to skip fallback)

    Returns [{"path": str, "asset_id": str, "source": str, "description": str|None}]
    """
    from lib.frame_index import get_frames_for_asset

    # Step 1: standard matching
    standard = select_asset_refs(refs, asset_ids, asset_type)
    matched_ids = {r["asset_id"] for r in standard}
    result = [
        {**r, "source": "reference", "description": None}
        for r in standard
    ]

    if not frame_index or not frame_index.get("frames"):
        return result

    unmatched = [aid for aid in asset_ids if aid not in matched_ids]
    if not unmatched:
        return result

    # Load assets.json for frame_references field (Claude-selected)
    story_path = take_path.parent
    assets_data = _load_episode_assets(take_path)
    claude_frame_refs: dict[str, list[str]] = {}
    if assets_data:
        for asset_list_key in ("characters", "locations", "objects"):
            for asset in assets_data.get(asset_list_key, []):
                aid = asset.get("id", "")
                fr = asset.get("frame_references", [])
                if aid and fr:
                    claude_frame_refs[aid] = fr

    for asset_id in unmatched:
        # Step 2: Claude-selected frame_references
        if asset_id in claude_frame_refs:
            for ref_path in claude_frame_refs[asset_id]:
                abs_path = story_path / ref_path
                if abs_path.exists():
                    # Find description from frame index
                    desc = None
                    for f in frame_index.get("frames", []):
                        if f.get("path") == ref_path:
                            desc = f.get("description", "")
                            break
                    result.append({
                        "path": str(abs_path),
                        "asset_id": asset_id,
                        "source": "frame",
                        "description": desc,
                    })
                    matched_ids.add(asset_id)
                    break  # one frame per asset is enough
            if asset_id in matched_ids:
                continue

        # Step 3: auto-fallback via frame index search
        frame_matches = get_frames_for_asset(frame_index, asset_id)
        if frame_matches:
            best = frame_matches[0]
            abs_path = story_path / best["path"]
            if abs_path.exists():
                result.append({
                    "path": str(abs_path),
                    "asset_id": asset_id,
                    "source": "frame",
                    "description": best.get("description", ""),
                })
                logger.info(
                    f"Frame fallback for {asset_type}/{asset_id}: "
                    f"ep{best['episode']} scene {best['scene']}"
                )

    return result


def format_references_for_prompt(refs: dict) -> str:
    """
    Format references as text for inclusion in Claude prompts.

    Args:
        refs: References dictionary from load_project_references()

    Returns:
        Formatted string suitable for prompt inclusion
    """
    if not refs.get("available"):
        return ""

    sections = []

    # Template info
    if refs.get("template"):
        sections.append(f"## Template: {refs['template']}")

    # Brief
    if refs.get("brief"):
        sections.append(f"## User Brief\n\n{refs['brief']}")

    # People
    if refs.get("people"):
        people_section = "## People in This Project\n\n"
        for name, data in refs["people"].items():
            people_section += f"### {name.replace('_', ' ').title()}\n"
            if data.get("description"):
                people_section += f"{data['description']}\n"
            if data.get("photos"):
                people_section += f"\n**Photos available**: {len(data['photos'])} reference photos\n"
            people_section += "\n"
        sections.append(people_section)

    # Locations
    if refs.get("locations"):
        sections.append(f"## Location References\n\n{len(refs['locations'])} location photos available for visual reference")

    # Objects
    if refs.get("objects"):
        sections.append(f"## Object References\n\n{len(refs['objects'])} object reference images available")

    # Style
    if refs.get("style"):
        sections.append(f"## Style References\n\n{len(refs['style'])} style/mood reference images available")

    return "\n\n".join(sections)


def format_template_for_prompt(template_spec: str, repo_root: Path = REPO_ROOT) -> str:
    """
    Format template content for inclusion in Claude prompts.

    Args:
        template_spec: Template specification (e.g., "instagram-influencer/lifestyle")
        repo_root: Path to repository root

    Returns:
        Formatted string with archetype + variant content
    """
    template_name, variant = parse_template_spec(template_spec)
    sections = []

    # Load archetype
    archetype = load_template_archetype(template_name, repo_root)
    if archetype:
        sections.append(f"## Template Archetype: {template_name}\n\n{archetype}")

    # Load variant if specified
    if variant:
        variant_content = load_template_variant(template_name, variant, repo_root)
        if variant_content:
            sections.append(f"## Variant: {variant}\n\n{variant_content}")

    return "\n\n---\n\n".join(sections)


def format_shared_resources_for_prompt(repo_root: Path = REPO_ROOT) -> str:
    """
    Format shared resources for inclusion in Claude prompts.

    Args:
        repo_root: Path to repository root

    Returns:
        Formatted string with all shared resources
    """
    shared = load_shared_resources(repo_root)
    sections = []

    # Order matters - put most relevant first
    order = ["timing_rules", "hook_archetypes", "ai_constraints"]

    for key in order:
        if key in shared:
            sections.append(f"## {key.replace('_', ' ').title()}\n\n{shared[key]}")

    # Add any remaining resources not in order
    for key, content in shared.items():
        if key not in order:
            sections.append(f"## {key.replace('_', ' ').title()}\n\n{content}")

    return "\n\n---\n\n".join(sections)


def format_examples_for_prompt(template_spec: str, repo_root: Path = REPO_ROOT, max_examples: int = 2) -> str:
    """
    Format example breakdowns for inclusion in Claude prompts.

    Args:
        template_spec: Template specification
        repo_root: Path to repository root
        max_examples: Maximum number of examples to include

    Returns:
        Formatted string with example breakdowns
    """
    template_name, variant = parse_template_spec(template_spec)
    examples = load_template_examples(template_name, repo_root, variant)

    if not examples:
        return ""

    sections = ["## Example Scene Breakdowns\n\nStudy these examples for structure and pacing:"]

    for i, example in enumerate(examples[:max_examples]):
        example_text = f"\n### Example {i + 1}: {example.get('title', 'Untitled')}\n\n"
        example_text += f"**Duration**: {example.get('duration', 'N/A')} seconds\n"
        example_text += f"**Mood**: {example.get('mood', 'N/A')}\n\n"

        if "scenes" in example:
            example_text += "**Scenes**:\n"
            for scene in example["scenes"]:
                example_text += f"\n{scene.get('number', '?')}. **{scene.get('type', 'scene')}** ({scene.get('duration', '?')}s, {scene.get('timing_phase', 'N/A')} phase)\n"
                example_text += f"   {scene.get('what_is_shown', 'No description')[:200]}...\n"

        if "production_notes" in example:
            example_text += f"\n**Notes**: {example['production_notes']}\n"

        sections.append(example_text)

    return "\n".join(sections)


def get_full_prompt_context(project_path: Path, repo_root: Path = REPO_ROOT) -> str:
    """
    Get complete prompt context including references, template, and shared resources.

    This is the main function to call when building prompts that need template context.

    Args:
        project_path: Path to project root
        repo_root: Path to repository root

    Returns:
        Complete formatted context string for Claude prompts
    """
    refs = load_project_references(project_path)

    sections = []

    # Add references
    refs_text = format_references_for_prompt(refs)
    if refs_text:
        sections.append("# Project References\n\n" + refs_text)

    # Add template content if specified
    if refs.get("template"):
        template_text = format_template_for_prompt(refs["template"], repo_root)
        if template_text:
            sections.append("# Template Guidelines\n\n" + template_text)

        # Add examples
        examples_text = format_examples_for_prompt(refs["template"], repo_root)
        if examples_text:
            sections.append(examples_text)

    # Add shared resources
    shared_text = format_shared_resources_for_prompt(repo_root)
    if shared_text:
        sections.append("# Content Creation Guidelines\n\n" + shared_text)

    return "\n\n" + "=" * 60 + "\n\n".join(sections)


# Utility function for testing
if __name__ == "__main__":
    import sys

    logging.basicConfig(level=logging.INFO)

    if len(sys.argv) > 1:
        project_path = Path(sys.argv[1])
    else:
        print("Usage: python reference_loader.py <project_path>")
        print("\nAvailable templates:")
        for t in list_available_templates():
            variants = list_template_variants(t)
            if variants:
                print(f"  {t}/")
                for v in variants:
                    print(f"    {v}")
            else:
                print(f"  {t}")
        sys.exit(0)

    refs = load_project_references(project_path)
    print("References loaded:")
    print(json.dumps({k: v if k not in ["brief"] else f"[{len(v)} chars]" for k, v in refs.items()}, indent=2))

    print("\n" + "=" * 60)
    print("Full prompt context:")
    print(get_full_prompt_context(project_path))
