"""Code review classification mixin.

Extracted from code_review.py for maintainability.

Contains:
    ClassifyMixin:
        _classify — CHEAP classification of change type, complexity, and risk

Expected host attributes (provided by BaseWorkflow / its mixins):
    _call_llm          : async method (from LLMMixin)
    core_modules       : list[str]
    file_threshold     : int
    enable_auth_strategy : bool
    _needs_architect_review : bool
    _auth_mode_used    : str | None
    _gather_project_context : method (from ArchitectMixin)

Copyright 2025 Smart-AI-Memory
Licensed under the Apache License, Version 2.0
"""

from __future__ import annotations

import logging

from .base import ModelTier

logger = logging.getLogger(__name__)


def _count_target_lines(target_path: str) -> int:
    """Count lines of code for a file or directory path."""
    if not target_path:
        return 0
    try:
        from pathlib import Path

        from attune.models import count_lines_of_code

        target_obj = Path(target_path)
        if not target_obj.exists():
            return 0
        if target_obj.is_file():
            return count_lines_of_code(target_obj)
        if target_obj.is_dir():
            total = 0
            for py_file in target_obj.rglob("*.py"):
                try:
                    total += count_lines_of_code(py_file)
                except (OSError, UnicodeDecodeError):
                    pass
            return total
    except (OSError, ImportError):
        pass
    return 0


class ClassifyMixin:
    """Mixin providing the classification stage for code review."""

    def _get_code_to_review(self, input_data: dict) -> tuple[str | None, dict | None]:
        """Resolve code to review, returning (code, error_result) or (code, None)."""
        diff = input_data.get("diff", "")
        target = input_data.get("target", "")
        code = diff or target

        if code and code.strip() not in (".", "", "./"):
            return code, None

        project_context = self._gather_project_context()
        if project_context:
            input_data["is_project_review"] = True
            return project_context, None

        error_result = {
            "classification": "ERROR: No code provided for review",
            "error": True,
            "error_message": (
                "No code was provided for review. Please ensure you:\n"
                "1. Have a file open in the editor, OR\n"
                "2. Select a specific file to review, OR\n"
                '3. Provide code content directly via --input \'{"diff": "..."}\'\n\n'
                "Tip: Use 'Select File...' option in the workflow picker."
            ),
            "change_type": "none",
            "files_changed": [],
            "file_count": 0,
            "needs_architect_review": False,
            "is_core_module": False,
            "code_to_review": "",
        }
        return None, error_result

    def _apply_auth_strategy(self, target: str, diff: str) -> None:
        """Apply auth strategy integration for code review."""
        try:
            from attune.models import (
                get_auth_strategy,
                get_module_size_category,
            )

            target_path = target or diff
            total_lines = _count_target_lines(target_path)
            if total_lines <= 0:
                return

            strategy = get_auth_strategy()
            recommended_mode = strategy.get_recommended_mode(total_lines)
            self._auth_mode_used = recommended_mode.value

            size_category = get_module_size_category(total_lines)
            logger.info(
                "Code review target: %s (%s LOC, %s)",
                target_path,
                f"{total_lines:,}",
                size_category,
            )
            logger.info("Recommended auth mode: %s", recommended_mode.value)

            cost_estimate = strategy.estimate_cost(total_lines, recommended_mode)
            if recommended_mode.value == "subscription":
                logger.info("Cost: %s", cost_estimate["quota_cost"])
            else:
                logger.info("Cost: ~$%.4f", cost_estimate["monetary_cost"])

        except (AttributeError, ImportError, TypeError) as e:
            logger.warning("Auth strategy detection failed: %s", e)

    async def _classify(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
        """Classify the type of change."""
        files_changed = input_data.get("files_changed", [])

        code_to_review, error_result = self._get_code_to_review(input_data)
        if error_result is not None:
            return error_result, 0, 0

        if self.enable_auth_strategy:
            self._apply_auth_strategy(input_data.get("target", ""), input_data.get("diff", ""))

        system = """You are a code review classifier. Analyze the code and classify:
1. Change type: bug_fix, feature, refactor, docs, test, config, or security
2. Complexity: low, medium, high
3. Risk level: low, medium, high

Respond with a brief classification summary."""

        user_message = f"""Classify this code change:

Files: {", ".join(files_changed) if files_changed else "Not specified"}

Code:
{code_to_review[:4000]}"""

        response, input_tokens, output_tokens = await self._call_llm(
            tier,
            system,
            user_message,
            max_tokens=500,
        )

        # Parse response to determine if architect review needed
        is_high_complexity = "high" in response.lower() and (
            "complexity" in response.lower() or "risk" in response.lower()
        )
        is_core = (
            any(any(core in f for core in self.core_modules) for f in files_changed)
            if files_changed
            else False
        )

        self._needs_architect_review = (
            len(files_changed) >= self.file_threshold
            or is_core
            or is_high_complexity
            or input_data.get("is_core_module", False)
        )

        return (
            {
                "classification": response,
                "change_type": "feature",  # Will be refined by LLM
                "files_changed": files_changed,
                "file_count": len(files_changed),
                "needs_architect_review": self._needs_architect_review,
                "is_core_module": is_core,
                "code_to_review": code_to_review,
            },
            input_tokens,
            output_tokens,
        )
