import os
from nova_cli.local.ui import ui
from nova_cli.local.utils import check_syntax
from nova_cli.local.file_manager.path_ops import resolve_path, validate_path
from nova_cli.local.file_manager.git_ops import commit_changes


def normalize_line(line: str) -> str:
    """Removes all whitespace and lowercases to create a comparison fingerprint."""
    return "".join(line.split()).lower()


def fuzzy_replace(content: str, search_block: str, replace_block: str) -> tuple[bool, str]:
    """
    Attempts to find search_block within content by ignoring indentation/whitespace.
    Uses strict fingerprinting for robust matching.
    Returns: (Success, NewContent)
    """
    content_lines = content.splitlines()
    search_lines = search_block.strip().splitlines()

    norm_content = [normalize_line(line) for line in content_lines]
    norm_search = [normalize_line(line) for line in search_lines]

    while norm_search and not norm_search[-1]:
        norm_search.pop()

    if not norm_search:
        return False, content

    search_len = len(norm_search)

    match_index = -1
    for i in range(len(norm_content) - search_len + 1):
        if norm_content[i : i + search_len] == norm_search:
            match_index = i
            break

    if match_index == -1:
        return False, content

    new_lines = content_lines[:match_index]
    new_lines.extend(replace_block.splitlines())
    new_lines.extend(content_lines[match_index + search_len :])

    return True, "\n".join(new_lines) + "\n"


def apply_surgical_edit(filepath: str, search_block: str, replace_block: str) -> bool:
    """
    Reads file, finds search_block, replaces with replace_block.
    Supports exact match and fuzzy match (whitespace insensitive).
    Performs syntax check before saving.
    """
    full_path = filepath 
    
    if not os.path.isabs(full_path):
        try:
            full_path = validate_path(resolve_path(filepath))
        except Exception:
            full_path = os.path.abspath(filepath)

    if not os.path.exists(full_path):
        ui.print(f"[red]>> Edit Failed: File not found {filepath}[/red]")
        return False

    with open(full_path, "r", encoding="utf-8") as f:
        content = f.read()

    search_block_norm = search_block.replace("\r\n", "\n")
    content_norm = content.replace("\r\n", "\n")

    new_content = None
    method_used = ""

    if search_block_norm.strip() in content_norm:
        new_content = content_norm.replace(search_block_norm.strip(), replace_block.strip())
        method_used = "Exact Match"
    else:
        success, fuzzy_content = fuzzy_replace(content_norm, search_block_norm, replace_block.strip())
        if success:
            new_content = fuzzy_content
            method_used = "Robust Match"
        else:
            ui.print(f"[red]>> Edit Failed: SEARCH block not found in {os.path.basename(filepath)}[/red]")
            ui.print("[dim]   (Tip: The AI might have hallucinated indentation. Check file content.)[/dim]")
            return False

    is_valid, syntax_error = check_syntax(new_content, full_path)
    if not is_valid:
        ui.print("[bold red]>> Edit Rejected: Resulting code has Syntax Error.[/bold red]")
        ui.print(f"[red]>> {syntax_error}[/red]")
        return False

    with open(full_path, "w", encoding="utf-8") as f:
        f.write(new_content)

    ui.print(f"[green]>> Surgical Edit Applied ({method_used}): {os.path.basename(filepath)}[/green]")
    commit_changes(f"Refactored {os.path.basename(filepath)}", full_path, use_semantic=True)
    return True
