"""Ruby source parser (regex-based)."""

from __future__ import annotations

import re

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

_REQUIRE_RE = re.compile(r"^\s*require(?:_relative)?\s+['\"]([^'\"]+)['\"]", re.MULTILINE)
_CLASS_RE = re.compile(r"^(\s*)class\s+(\w+(?:::\w+)?)(?:\s*<\s*(\w+(?:::\w+)?))?", re.MULTILINE)
_MODULE_RE = re.compile(r"^(\s*)module\s+(\w+(?:::\w+)?)", re.MULTILINE)
_DEF_RE = re.compile(r"^(\s*)def\s+(self\.)?(\w+[?!]?)\s*(?:\(([^)]*)\))?", re.MULTILINE)
_ATTR_RE = re.compile(r"^\s*attr_(?:accessor|reader|writer)\s+(.*)", re.MULTILINE)
_CONST_RE = re.compile(r"^(\s*)([A-Z][A-Z0-9_]{2,})\s*=", re.MULTILINE)
_END_RE = re.compile(r"^\s*end\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[1:].strip())
        else:
            break
    return " ".join(reversed(docs))


def parse_ruby(file_path: str, content: str) -> ParseResult:
    result = ParseResult(file_path=file_path, language="ruby")
    lines = content.splitlines()
    n_lines = len(lines)

    for m in _REQUIRE_RE.finditer(content):
        path = m.group(1)
        is_local = "require_relative" in content[m.start():m.start() + 20] or path.startswith(".")
        result.dependencies.append(DependencyRecord(
            path=path,
            type="require",
            line_number=_line_number(content, m.start()),
            is_local=is_local,
        ))

    for m in _CLASS_RE.finditer(content):
        name = m.group(2)
        ln = _line_number(content, m.start())
        docs = _doc_before(content, m.start())
        result.entities.append(EntityRecord(
            name=name.split("::")[-1],
            type="class",
            qualified_name=name,
            docs=docs,
            start_line=ln,
            end_line=ln,
            language="ruby",
        ))

    for m in _MODULE_RE.finditer(content):
        name = m.group(2)
        ln = _line_number(content, m.start())
        result.entities.append(EntityRecord(
            name=name.split("::")[-1],
            type="module",
            qualified_name=name,
            start_line=ln,
            end_line=ln,
            language="ruby",
        ))

    for m in _DEF_RE.finditer(content):
        is_class_method = bool(m.group(2))
        name = m.group(3)
        params = m.group(4) or ""
        ln = _line_number(content, m.start())
        docs = _doc_before(content, m.start())
        result.entities.append(EntityRecord(
            name=name,
            type="method" if not is_class_method else "class_method",
            qualified_name=name,
            signature=f"def {name}({params})",
            docs=docs,
            start_line=ln,
            end_line=ln,
            language="ruby",
        ))

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

    return result
