"""
Simplified AI Fix Service

Single unified service for generating security fix guidance.
LLM decides strategy (quantum vs. general security) automatically.
"""

import logging
import os
from typing import Any, Dict, Optional, Tuple, List
from app.services.llm_service import generate_completion, LLMConfig
from app.config.config import AI_FIX_MOCK_MODE
from app.prompts.unified_prompt import build_unified_fix_prompt, build_code_gen_fix_prompt
from app.utils.patch_utils import find_git_repo_root, _to_repo_relative, rewrite_diff_headers, git_validate_diff, count_hunks

logger = logging.getLogger(__name__)


def _basic_single_file_diff_check(file_path: str, diff_text: str) -> Tuple[bool, List[str]]:
    """
    Structural check that the diff targets exactly ONE file and has at least one hunk.

    Path matching is intentionally skipped here — the LLM (especially in code-gen mode)
    may emit the real file path instead of the placeholder. Path normalisation is handled
    later by rewrite_diff_headers() before git apply --check.
    """
    warnings: List[str] = []
    if not diff_text or not isinstance(diff_text, str):
        return False, ["Empty or invalid diff text"]

    lines = [l.rstrip("\n") for l in diff_text.splitlines()]
    header_old = [l for l in lines if l.startswith("--- ")]
    header_new = [l for l in lines if l.startswith("+++ ")]

    # Must target exactly one file (no multi-file diffs)
    if len(header_old) != 1 or len(header_new) != 1:
        warnings.append("Diff must contain exactly one pair of ---/+++ headers")
        return False, warnings

    # Must have at least one hunk
    if not any(l.startswith("@@") for l in lines):
        warnings.append("No hunk headers (@@) found in diff")
        return False, warnings

    return True, warnings


def _mock_response(file_path: str) -> Dict[str, Any]:
    """Return a safe, static example diff for testing."""
    example_diff = (
        f"--- a/{file_path}\\n"
        f"+++ b/{file_path}\\n"
        f"@@ -1,3 +1,3 @@\\n"
        f"-# TODO: insecure usage here\\n"
        f"+# Security Fix: Use secure pattern instead\\n"
        f" def main():\\n"
        f"-    print('hello')\\n"
        f"+    print('hello world')\\n"
    )
    return {
        "success": True,
        "generated_fix": example_diff,
        "explanation": "Mock mode: demonstration diff for testing",
        "confidence": 50.0,
        "warnings": [],
    }


def generate_fix(
    file_path: str,
    file_content: str,
    finding: Dict[str, Any],
    llm_config: Optional[LLMConfig] = None,
    mode: str = "comment",
) -> Dict[str, Any]:
    """
    Generate AI-proposed security fix guidance.

    The LLM automatically:
    - Detects if vulnerability is quantum-related
    - Recommends appropriate fixes (PQC or standard security)
    - Generates comment-based guidance (doesn't modify code)

    Args:
        file_path: Path to the file being fixed
        file_content: Full content of the file
        finding: Scanner finding dictionary
        llm_config: LLM configuration (provider, model, api_key, base_url)

    Returns:
        Dict with success, generated_fix (diff), explanation, validation
    """
    # Allow mock mode for testing
    if AI_FIX_MOCK_MODE:
        logger.info("AI_FIX_MOCK_MODE enabled; returning mock response")
        return _mock_response(file_path)

    # LLM config required
    if not llm_config or not llm_config.api_key:
        logger.warning("No LLM configuration provided")
        return {
            "success": True,
            "generated_fix": None,
            "explanation": "No LLM configured. Configure your AI provider in Settings to enable fix generation.",
            "confidence": None,
            "warnings": ["llm_config_missing"],
        }

    # Build prompt based on mode
    if mode == "codegen":
        prompt = build_code_gen_fix_prompt(file_path, file_content, finding)
    else:
        prompt = build_unified_fix_prompt(file_path, file_content, finding)

    try:
        # Call LLM via unified service
        text = generate_completion(prompt, llm_config)
        if not text:
            return {
                "success": True,
                "generated_fix": None,
                "explanation": "Model returned no text; cannot generate fix.",
                "confidence": None,
                "warnings": ["empty_model_output"],
            }

        # Extract diff from response — find first "--- " header
        diff = text
        fence_start = text.find("--- ")
        if fence_start > -1:
            diff = text[fence_start:]
        # Strip trailing markdown code fences that LLMs often append
        stripped = diff.rstrip()
        if stripped.endswith("```"):
            last_fence = stripped.rfind("\n```")
            if last_fence > 0:
                diff = stripped[:last_fence].rstrip() + "\n"

        # Structural check only — path is normalised later by rewrite_diff_headers
        logger.info(f"LLM response length: {len(text)}, diff extracted at offset {fence_start}, diff length: {len(diff)}")
        valid_minimal, warnings = _basic_single_file_diff_check(file_path, diff)
        if not valid_minimal:
            logger.warning(f"Diff structural check failed: {warnings}. Diff preview: {diff[:200]!r}")
            return {
                "success": True,
                "generated_fix": None,
                "explanation": text.strip()[:4000],
                "confidence": None,
                "warnings": ["invalid_diff"] + warnings,
            }

        # Git-based validation (dry run)
        repo_root = find_git_repo_root(file_path)
        validation = {
            "valid": False,
            "method": "git",
            "reasons": [],
            "stats": None,
        }

        if not repo_root:
            validation["reasons"] = ["git_repo_not_found"]
            return {
                "success": True,
                "generated_fix": diff,
                "explanation": "Fix generated (Git repo not found for validation)",
                "confidence": None,
                "warnings": warnings,
                "validation": validation,
            }

        # Validate with git apply --check
        rel_path = _to_repo_relative(file_path, repo_root)
        normalized = rewrite_diff_headers(diff, rel_path)
        ok, reasons, added, removed = git_validate_diff(repo_root, normalized)

        if ok:
            validation["valid"] = True
            validation["reasons"] = []
            validation["stats"] = {
                "added": added or 0,
                "removed": removed or 0,
                "hunks": count_hunks(normalized),
            }
        else:
            validation["valid"] = False
            validation["reasons"] = reasons or ["git_validation_failed"]

        fix_explanation = (
            "AI-generated code replacement (review carefully before applying)"
            if mode == "codegen"
            else "Security fix guidance generated by AI (review before applying)"
        )
        return {
            "success": True,
            "generated_fix": diff,
            "explanation": fix_explanation,
            "confidence": None,
            "warnings": warnings,
            "validation": validation,
        }

    except Exception as e:
        logger.exception("Error generating AI fix")
        return {
            "success": False,
            "generated_fix": None,
            "explanation": f"Failed to generate fix: {str(e)}",
            "confidence": None,
            "warnings": ["generation_error"],
        }
