import ast
import unittest
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
SCRIPT_PATHS = sorted((ROOT / "scripts").glob("*.py")) + sorted(
    (ROOT / ".gitea" / "ci").glob("*.py")
)


class ScriptLineEndingTests(unittest.TestCase):
    def test_script_text_writes_pin_lf_newlines(self):
        offenders: list[str] = []
        for path in SCRIPT_PATHS:
            tree = ast.parse(path.read_text(encoding="utf-8"))
            for node in ast.walk(tree):
                if not isinstance(node, ast.Call):
                    continue
                if is_write_text_without_lf_newline(node):
                    offenders.append(f"{path.relative_to(ROOT)}:{node.lineno}")
                elif is_text_open_for_writing_without_newline(node):
                    offenders.append(f"{path.relative_to(ROOT)}:{node.lineno}")

        self.assertEqual(offenders, [])


def has_lf_newline_keyword(node: ast.Call) -> bool:
    newline = next(
        (keyword for keyword in node.keywords if keyword.arg == "newline"),
        None,
    )
    return (
        newline is not None
        and isinstance(newline.value, ast.Constant)
        and newline.value.value == "\n"
    )


def is_write_text_without_lf_newline(node: ast.Call) -> bool:
    func = node.func
    return (
        isinstance(func, ast.Attribute)
        and func.attr == "write_text"
        and not has_lf_newline_keyword(node)
    )


def is_text_open_for_writing_without_newline(node: ast.Call) -> bool:
    func = node.func
    if isinstance(func, ast.Name):
        is_open = func.id == "open"
    elif isinstance(func, ast.Attribute):
        is_open = func.attr == "open"
    else:
        is_open = False
    if not is_open:
        return False

    mode_node = None
    if len(node.args) >= 2:
        mode_node = node.args[1]
    for keyword in node.keywords:
        if keyword.arg == "mode":
            mode_node = keyword.value
            break

    if mode_node is None:
        return False
    if not isinstance(mode_node, ast.Constant) or not isinstance(mode_node.value, str):
        return False

    mode = mode_node.value
    writes_text = any(flag in mode for flag in ("w", "a", "x")) and "b" not in mode
    return writes_text and not has_lf_newline_keyword(node)


if __name__ == "__main__":
    unittest.main()
