#!/usr/bin/env python3
"""
Convert legacy # [DEF:...] / # [/DEF:...] annotations to #region/#endregion format.

ONLY modifies comment lines (starting with '#').
NEVER touches code, strings, imports, or non-comment lines.
Preserves original line endings (CRLF vs LF).
"""

from pathlib import Path
import re

SRC_DIR = Path(__file__).resolve().parent / "src"


def has_crlf(content: bytes) -> bool:
    """Check if content uses CRLF line endings."""
    return b'\r\n' in content


def process_file(filepath: Path) -> tuple[int, int]:
    """Process a single file. Returns (blocks_converted, metadata_merged)."""
    # Read as bytes to detect original line endings
    with open(filepath, "rb") as f:
        raw_bytes = f.read()

    original_crlf = has_crlf(raw_bytes)

    # Decode text (Python converts CRLF to LF automatically on decode in text mode,
    # but we decoded from bytes, so handle it manually)
    text = raw_bytes.decode('utf-8')
    if original_crlf:
        text = text.replace('\r\n', '\n')

    lines = text.split('\n')

    result = []
    blocks_converted = 0
    metadata_merged = 0
    i = 0
    n = len(lines)

    while i < n:
        line = lines[i]
        stripped = line.rstrip('\r')

        # Check for [SECTION: ...] or [/SECTION] lines
        if re.match(r'^#\s*\[SECTION:\s*.*\]\s*$', stripped):
            i += 1
            continue
        if re.match(r'^#\s*\[/SECTION\]\s*$', stripped):
            i += 1
            continue

        # Check for [/DEF:Name] or [/DEF:Name:Type]
        m = re.match(r'^#\s*\[/DEF:(\w+(?:\.\w+)*)(?::\w+)?\]\s*$', stripped)
        if m:
            name = m.group(1)
            result.append(f"# #endregion {name}")
            blocks_converted += 1
            i += 1
            continue

        # Check for [DEF:Name:Type]
        m = re.match(r'^#\s*\[DEF:(\w+(?:\.\w+)*):(\w+)\]\s*$', stripped)
        if m:
            name = m.group(1)
            typ = m.group(2)

            # Collect subsequent metadata lines
            complexity = None
            semantics = None
            metadata_lines = []  # other metadata lines to preserve
            j = i + 1

            while j < n:
                nxt = lines[j].rstrip('\r')

                # Stop if not a comment
                if not nxt.startswith('#'):
                    break
                # Stop if it's another DEF or SECTION
                if re.match(r'^#\s*\[', nxt):
                    break
                # Skip blank comment lines (just '#') but continue collecting
                if re.match(r'^#\s*$', nxt):
                    metadata_lines.append(lines[j])
                    j += 1
                    continue

                # Check for @COMPLEXITY
                cm = re.match(r'^#\s*@COMPLEXITY:\s*(\d+)\s*$', nxt)
                if cm:
                    complexity = int(cm.group(1))
                    j += 1
                    continue

                # Check for @SEMANTICS
                sm = re.match(r'^#\s*@SEMANTICS:\s*(.+?)\s*$', nxt)
                if sm:
                    semantics = sm.group(1).strip()
                    j += 1
                    continue

                # Skip @PARAM, @RETURN, @THROW
                if re.match(r'^#\s*@(PARAM|RETURN|THROW)\b', nxt):
                    j += 1
                    continue

                # Convert @PURPOSE -> @BRIEF
                pm = re.match(r'^#\s*@PURPOSE:\s*(.*?)\s*$', nxt)
                if pm:
                    metadata_lines.append(f"# @BRIEF {pm.group(1).strip()}")
                    j += 1
                    continue

                # Normalize @RELATION
                rm = re.match(r'^#\s*@RELATION:\s*(.+?)\s*$', nxt)
                if rm:
                    rel_text = rm.group(1).strip()
                    # Remove brackets ONLY from predicate (word before ->)
                    rel_text = re.sub(r'\[(\w+)\]\s*->', r'\1 ->', rel_text)
                    # Normalize whitespace around ->
                    rel_text = re.sub(r'\s*->\s*', ' -> ', rel_text)
                    metadata_lines.append(f"# @RELATION {rel_text}")
                    j += 1
                    continue

                # Pass through any other tags as-is
                if re.match(r'^#\s*@\w+', nxt):
                    metadata_lines.append(lines[j])
                    j += 1
                    continue

                # If it doesn't match any known pattern, keep it but stop collecting
                break

            # Build the header
            header_parts = [f"# #region {name}"]
            if complexity is not None:
                header_parts.append(f"[C:{complexity}]")
                metadata_merged += 1
            header_parts.append(f"[TYPE {typ}]")
            if semantics is not None:
                header_parts.append(f"[SEMANTICS {semantics}]")
                metadata_merged += 1

            result.append(" ".join(header_parts))
            # Add preserved metadata lines
            result.extend(metadata_lines)

            blocks_converted += 1
            i = j
            continue

        # Check for standalone @PURPOSE (not in a DEF block)
        pm = re.match(r'^#\s*@PURPOSE:\s*(.*?)\s*$', stripped)
        if pm:
            result.append(f"# @BRIEF {pm.group(1).strip()}")
            i += 1
            continue

        # Check for standalone @RELATION normalization
        rm = re.match(r'^#\s*@RELATION:\s*(.+?)\s*$', stripped)
        if rm:
            rel_text = rm.group(1).strip()
            # Remove brackets ONLY from predicate (word before ->)
            rel_text = re.sub(r'\[(\w+)\]\s*->', r'\1 ->', rel_text)
            # Normalize whitespace around ->
            rel_text = re.sub(r'\s*->\s*', ' -> ', rel_text)
            result.append(f"# @RELATION {rel_text}")
            i += 1
            continue

        # Skip @PARAM, @RETURN, @THROW lines (standalone)
        if re.match(r'^#\s*@(PARAM|RETURN|THROW)\b', stripped):
            i += 1
            continue

        # Pass through everything else unchanged
        result.append(line)
        i += 1

    # Build new content with preserved line endings
    new_text = '\n'.join(result)

    if new_text == text:
        return 0, 0

    # Convert back to CRLF if original had it
    if original_crlf:
        new_raw = new_text.replace('\n', '\r\n').encode('utf-8')
    else:
        new_raw = new_text.encode('utf-8')

    with open(filepath, "wb") as f:
        f.write(new_raw)
    return blocks_converted, metadata_merged


def main():
    total_blocks = 0
    total_merged = 0
    total_files = 0

    # Collect all Python files
    py_files = sorted(SRC_DIR.rglob("*.py"))

    for filepath in py_files:
        # Skip __pycache__, .venv, tests
        rel = filepath.relative_to(SRC_DIR)
        parts = list(rel.parts)
        if "__pycache__" in parts or ".venv" in parts:
            continue
        if any(p.startswith("__tests__") or p == "tests" for p in parts):
            continue

        try:
            blocks, merged = process_file(filepath)
            if blocks > 0 or merged > 0:
                total_files += 1
                total_blocks += blocks
                total_merged += merged
                print(f"  {rel}: {blocks} blocks, {merged} metadata merged")
        except Exception as e:
            print(f"  ERROR processing {rel}: {e}")

    print(f"\nTotal: {total_files} files processed, {total_blocks} DEF blocks converted, {total_merged} metadata merged")


if __name__ == "__main__":
    main()
