"""Project builder for creating multi-target builds."""

import logging
from pathlib import Path
from typing import Any

import jinja2
from jinja2.sandbox import SandboxedEnvironment

from .document_builder import DocumentBuilder
from .models import BuildConfig, BuildTarget, ClaudeDocument

# Configure logging
logger = logging.getLogger(__name__)


class ProjectBuilder:
    """Handles building multiple project targets from documents."""

    def __init__(self, document_builder: DocumentBuilder, templates_dir: Path):
        """Initialize the project builder.

        Args:
            document_builder: DocumentBuilder instance for creating documents
            templates_dir: Directory containing Jinja2 templates
        """
        self.document_builder = document_builder
        self.templates_dir = Path(templates_dir)
        logger.info(f"Initializing ProjectBuilder with templates_dir={templates_dir}")

        self.env = SandboxedEnvironment(
            loader=jinja2.FileSystemLoader(templates_dir),
            trim_blocks=True,
            lstrip_blocks=False,
        )
        logger.debug("Initialized sandboxed Jinja2 environment")

    def render_document(self, document: ClaudeDocument) -> str:
        """Render the document using the Jinja2 template."""
        logger.debug("Rendering document with CLAUDE.md.j2 template")
        template = self.env.get_template("CLAUDE.md.j2")
        return template.render(doc=document)

    def build_and_save(self, output_path: Path) -> None:
        """Build the document and save it to the output path."""
        logger.info(f"Building and saving document to: {output_path}")
        document = self.document_builder.build_document()
        rendered = self.render_document(document)
        # Strip trailing whitespace/newlines for consistency with reference
        rendered = rendered.rstrip() + "\n"

        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_text(rendered, encoding="utf-8")
        logger.info(f"Document saved successfully to: {output_path}")

    def build_project(self, config: BuildConfig) -> None:
        """Build all targets in the project configuration."""
        logger.info(f"Building project with {len(config.targets)} targets")
        for target in config.targets:
            logger.debug(f"Building target: {target.name}")
            self._build_target(target, config.output_base_dir)
        logger.info("Project build completed successfully")

    def _build_target(self, target: BuildTarget, base_dir: Path) -> None:
        """Build a single target."""
        logger.debug(
            f"Building target '{target.name}' with template '{target.template_name}'"
        )

        # Get the data builder method
        if not hasattr(self, target.data_builder):
            raise ValueError(f"Data builder '{target.data_builder}' does not exist")

        data_builder = getattr(self, target.data_builder)
        if not callable(data_builder):
            raise ValueError(f"Data builder '{target.data_builder}' is not callable")

        # Build the data
        data = data_builder()

        # Render using the template
        rendered = self._render_template(target.template_name, data)

        # Apply post-processing if provided
        if target.post_process:
            rendered = target.post_process(rendered)

        # Determine output path
        output_path = base_dir / target.output_path

        # Save the file
        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_text(rendered, encoding="utf-8")
        logger.info(f"Target '{target.name}' built successfully: {output_path}")

    def _render_template(self, template_name: str, data: Any) -> str:
        """Render a template with the given data."""
        logger.debug(f"Rendering template: {template_name}")
        template = self.env.get_template(template_name)
        if isinstance(data, ClaudeDocument):
            return template.render(doc=data)
        else:
            return template.render(data=data)

    def build_document(self) -> ClaudeDocument:
        """Build the complete Claude document from snippets."""
        return self.document_builder.build_document()

    def build_settings_data(self) -> dict[str, Any]:
        """Build settings.json data."""
        document = self.build_document()
        return document.settings

    def build_hook_data(self) -> dict[str, Any]:
        """Build hook script data."""
        document = self.build_document()
        return {
            "project_name": "ai-forge",
            "python_version": "3.12+",
            "build_commands": ["just build", "just validate"],
            "hooks": document.hooks,
        }

    def build_hook_placeholder_data(self) -> dict[str, Any]:
        """Build placeholder data for hook files."""
        return {
            "placeholder_message": "# TODO: Add your hook implementation here",
            "description": "Claude hook script placeholder",
        }

    def build_command_placeholder_data(self) -> dict[str, Any]:
        """Build placeholder data for command files."""
        return {
            "placeholder_message": "<!-- TODO: Add your command documentation here -->",
            "description": "Claude command documentation placeholder",
        }

    def build_project_charter_data(self) -> dict[str, Any]:
        """Build data for PROJECT_CHARTER.md."""
        return {
            "project_name": "ai-forge",
            "description": "Project charter template",
        }

    def build_task_board_data(self) -> dict[str, Any]:
        """Build data for TASK_BOARD.md."""
        return {
            "project_name": "ai-forge",
            "description": "Task board template",
        }

    def build_artifacts_placeholder_data(self) -> dict[str, Any]:
        """Build data for artifacts directory placeholder."""
        return {
            "description": "Artifacts directory placeholder",
        }

    def build_contracts_placeholder_data(self) -> dict[str, Any]:
        """Build data for contracts directory placeholder."""
        return {
            "description": "Contracts directory placeholder",
        }

    @staticmethod
    def create_default_config() -> BuildConfig:
        """Create a default build configuration with all targets."""

        def claude_md_postprocess(content: str) -> str:
            """Post-process CLAUDE.md content."""
            return content.rstrip() + "\n"

        targets = [
            BuildTarget(
                name="claude_md",
                output_path=Path("CLAUDE.md"),
                template_name="CLAUDE.md.j2",
                data_builder="build_document",
                post_process=claude_md_postprocess,
            ),
            BuildTarget(
                name="settings",
                output_path=Path(".claude/settings.json"),
                template_name="settings.json.j2",
                data_builder="build_settings_data",
            ),
            BuildTarget(
                name="project_charter",
                output_path=Path("PROJECT_CHARTER.md"),
                template_name="PROJECT_CHARTER.md.j2",
                data_builder="build_project_charter_data",
            ),
            BuildTarget(
                name="task_board",
                output_path=Path("TASK_BOARD.md"),
                template_name="TASK_BOARD.md.j2",
                data_builder="build_task_board_data",
            ),
            BuildTarget(
                name="artifacts_placeholder",
                output_path=Path("artifacts/README.md"),
                template_name="artifacts_placeholder.md.j2",
                data_builder="build_artifacts_placeholder_data",
            ),
            BuildTarget(
                name="contracts_placeholder",
                output_path=Path("contracts/README.md"),
                template_name="contracts_placeholder.md.j2",
                data_builder="build_contracts_placeholder_data",
            ),
        ]

        # Add hook files dynamically - we'll copy them directly
        document_builder = DocumentBuilder(Path("snippets"))
        hooks = document_builder.load_hooks()
        for hook in hooks:

            def create_hook_postprocess(hook_content: str):
                def postprocess(content: str) -> str:
                    return hook_content

                return postprocess

            targets.append(
                BuildTarget(
                    name=f"hook_{hook.name}",
                    output_path=Path(
                        f".claude/hooks/{hook.name}.{hook.file_path.suffix.lstrip('.')}"
                    ),
                    template_name="hook_placeholder.py.j2"
                    if hook.language == "python"
                    else "hook_placeholder.sh.j2",
                    data_builder="build_hook_placeholder_data",
                    post_process=create_hook_postprocess(hook.content),
                )
            )

        # Add command files dynamically - we'll copy them directly
        commands = document_builder.load_commands()
        for command in commands:

            def create_command_postprocess(command_content: str):
                def postprocess(content: str) -> str:
                    return command_content

                return postprocess

            targets.append(
                BuildTarget(
                    name=f"command_{command.name}",
                    output_path=Path(f".claude/commands/{command.name}.md"),
                    template_name="command_placeholder.md.j2",
                    data_builder="build_command_placeholder_data",
                    post_process=create_command_postprocess(command.content),
                )
            )

        return BuildConfig(targets=targets)

    @staticmethod
    def create_claude_md_only_config() -> BuildConfig:
        """Create a config that only builds CLAUDE.md (for backward compatibility)."""

        def claude_md_postprocess(content: str) -> str:
            """Post-process CLAUDE.md content."""
            return content.rstrip() + "\n"

        targets = [
            BuildTarget(
                name="claude_md",
                output_path=Path("CLAUDE.md"),
                template_name="CLAUDE.md.j2",
                data_builder="build_document",
                post_process=claude_md_postprocess,
            )
        ]

        return BuildConfig(targets=targets)
