"""C# source parser (regex-based)."""

from __future__ import annotations

import re

from codectx.parser.types import DependencyRecord, EntityRecord, ParseResult

_USING_RE = re.compile(r"^\s*using\s+(?:static\s+)?([\w.]+)\s*;", re.MULTILINE)
_NS_RE = re.compile(r"^\s*namespace\s+([\w.]+)", re.MULTILINE)
_CLASS_RE = re.compile(
    r"^(\s*)(?:public\s+|private\s+|protected\s+|internal\s+|sealed\s+|abstract\s+|static\s+|partial\s+)*"
    r"(class|interface|struct|record|enum)\s+(\w+)",
    re.MULTILINE,
)
_METHOD_RE = re.compile(
    r"^(\s*)(?:public\s+|private\s+|protected\s+|internal\s+|static\s+|override\s+|virtual\s+|async\s+|abstract\s+)*"
    r"(?:[\w<>\[\],\s]+)\s+(\w+)\s*\(([^)]*)\)\s*(?:where[^{;]+)?[{;]",
    re.MULTILINE,
)
_PROP_RE = re.compile(
    r"^(\s*)(?:public\s+|private\s+|protected\s+|internal\s+|static\s+)*"
    r"(?:[\w<>\[\],?]+)\s+(\w+)\s*\{\s*(?:get|set)",
    re.MULTILINE,
)
_FIELD_RE = re.compile(
    r"^(\s*)(?:public\s+|private\s+|protected\s+|internal\s+|static\s+|readonly\s+|const\s+)+"
    r"(?:[\w<>\[\],?]+)\s+(\w+)\s*(?:=|;)",
    re.MULTILINE,
)
_DOC_RE = re.compile(r"^\s*///\s*(.*)", re.MULTILINE)


def _line_number(content: str, pos: int) -> int:
    return content[:pos].count("\n") + 1


def _doc_before(content: str, pos: int) -> str:
    lines = content[:pos].splitlines()
    docs = []
    for line in reversed(lines):
        stripped = line.strip()
        if stripped.startswith("///"):
            docs.append(stripped[3:].strip())
        else:
            break
    return " ".join(reversed(docs))


def parse_csharp(file_path: str, content: str) -> ParseResult:
    result = ParseResult(file_path=file_path, language="csharp")

    for m in _USING_RE.finditer(content):
        ns = m.group(1)
        result.dependencies.append(DependencyRecord(
            path=ns,
            type="using",
            line_number=_line_number(content, m.start()),
        ))

    namespace = ""
    for m in _NS_RE.finditer(content):
        namespace = m.group(1)

    for m in _CLASS_RE.finditer(content):
        kind = m.group(2)
        name = m.group(3)
        qname = f"{namespace}.{name}" if namespace else name
        ln = _line_number(content, m.start())
        docs = _doc_before(content, m.start())
        result.entities.append(EntityRecord(
            name=name,
            type=kind,
            qualified_name=qname,
            start_line=ln,
            end_line=ln,
            docs=docs,
            language="csharp",
        ))

    for m in _METHOD_RE.finditer(content):
        name = m.group(2)
        if name in {"if", "while", "for", "foreach", "switch", "catch", "finally", "using", "lock", "return"}:
            continue
        ln = _line_number(content, m.start())
        docs = _doc_before(content, m.start())
        indent = m.group(1)
        vis = "public" if "public" in content[m.start():m.start() + 80] else "private"
        result.entities.append(EntityRecord(
            name=name,
            type="method",
            qualified_name=name,
            signature=f"{name}({m.group(3)})",
            start_line=ln,
            end_line=ln,
            docs=docs,
            visibility=vis,
            language="csharp",
        ))

    for m in _PROP_RE.finditer(content):
        name = m.group(2)
        ln = _line_number(content, m.start())
        result.entities.append(EntityRecord(
            name=name,
            type="property",
            qualified_name=name,
            start_line=ln,
            end_line=ln,
            language="csharp",
        ))

    return result
