"""Method-level extraction from decompiled IL and C# output.

ilspycmd only supports type-level filtering (-t flag). When a type is
very large, clients need to extract individual methods from the full
type output. This module provides that post-processing step.

IL extraction relies on ECMA-335 standard delimiters:
  - Start: `.method` directive
  - End:   `} // end of method TypeName::MethodName`

C# extraction uses signature matching with brace-depth counting.
"""

import re


def extract_il_method(il_source: str, method_name: str) -> list[str]:
    """Extract method(s) by name from IL output.

    Splits on `.method` directives and matches against
    ``} // end of method ...::MethodName`` markers.
    Returns list of matching method blocks (handles overloads).

    Args:
        il_source: Full IL source code from ilspycmd
        method_name: Method name to extract (matched against end-of-method comment)

    Returns:
        List of IL method blocks as strings. Empty list if no matches.
    """
    if not il_source or not method_name:
        return []

    results: list[str] = []
    lines = il_source.split("\n")
    in_method = False
    current_block: list[str] = []
    brace_depth = 0
    found_opening = False

    # Pattern for end-of-method comment: } // end of method TypeName::MethodName
    end_pattern = re.compile(
        r"^\s*\}\s*//\s*end of method\s+\S+::" + re.escape(method_name) + r"\s*$"
    )
    # Pattern for .method directive start
    method_start = re.compile(r"^\s*\.method\s+")

    for line in lines:
        if not in_method:
            if method_start.match(line):
                in_method = True
                current_block = [line]
                brace_depth = line.count("{") - line.count("}")
                found_opening = brace_depth > 0
        else:
            current_block.append(line)
            brace_depth += line.count("{") - line.count("}")

            if not found_opening and brace_depth > 0:
                found_opening = True

            if end_pattern.match(line):
                results.append("\n".join(current_block))
                in_method = False
                current_block = []
                brace_depth = 0
            elif found_opening and brace_depth <= 0:
                # Reached closing brace without matching our method name —
                # this was a different method, discard it
                in_method = False
                current_block = []
                brace_depth = 0

    return results


def extract_csharp_method(cs_source: str, method_name: str) -> list[str]:
    """Extract method(s) by name from C# output.

    Finds method signatures matching the name, then uses
    brace-depth counting to find the closing brace.
    Returns list of matching method blocks (handles overloads).

    Args:
        cs_source: Full C# source code from ilspycmd
        method_name: Method name to extract

    Returns:
        List of C# method blocks as strings. Empty list if no matches.
    """
    if not cs_source or not method_name:
        return []

    results: list[str] = []
    lines = cs_source.split("\n")

    # Match method signatures — handles modifiers, return types, generics
    # Examples:
    #   public void DoElaborate(...)
    #   private static async Task<int> DoElaborate<T>(...)
    #   internal override bool DoElaborate(
    sig_pattern = re.compile(
        r"^(\s*)"  # leading whitespace (capture for indent level)
        r"(?:(?:public|private|protected|internal|static|virtual|override|"
        r"abstract|async|sealed|new|extern|unsafe|partial|readonly)\s+)*"
        r".+\s+"  # return type (greedy — handles generics with spaces like List<Dictionary<string, int>>)
        + re.escape(method_name)
        + r"\s*(?:<[^>]*>)?"  # optional generic type parameters
        r"\s*\(",  # opening paren
    )

    i = 0
    while i < len(lines):
        match = sig_pattern.match(lines[i])
        if match:
            # Found a method signature — collect until braces balance
            block: list[str] = [lines[i]]
            brace_depth = lines[i].count("{") - lines[i].count("}")
            j = i + 1

            # If the signature hasn't opened a brace yet, keep scanning
            # for the opening brace (multi-line signatures)
            found_opening = brace_depth > 0

            while j < len(lines):
                block.append(lines[j])
                brace_depth += lines[j].count("{") - lines[j].count("}")

                if not found_opening and brace_depth > 0:
                    found_opening = True

                if found_opening and brace_depth <= 0:
                    # Balanced — we have the full method body
                    results.append("\n".join(block))
                    i = j + 1
                    break

                j += 1
            else:
                # Reached end of file without balancing — include what we have
                if block:
                    results.append("\n".join(block))
                i = j
        else:
            i += 1

    return results
