"""Detecta metodos chamados estaticamente mas declarados como metodo de instancia.

PHP 8: "Non-static method X::y() cannot be called statically" = fatal.
Corrige: declara static (quando o metodo nao usa $this) e reporta os casos duvidosos.
"""
import re
from pathlib import Path

MART = Path(r"C:\projetos\apps\martinelli-php83")
IGNORAR = ("vendor/", "runtime/", "web/assets/", "nbproject/", ".git/", "web/plugins/")

CHAMADA = re.compile(r"\b([A-Z][A-Za-z0-9_]*)::([a-zA-Z_][A-Za-z0-9_]*)\s*\(")
CLASSE = re.compile(r"^\s*(?:final\s+|abstract\s+)?class\s+([A-Z][A-Za-z0-9_]*)\b", re.M)


def arquivos():
    for p in MART.rglob("*.php"):
        rel = p.relative_to(MART).as_posix()
        if any(s in rel for s in IGNORAR):
            continue
        yield p, rel


def main():
    textos = {}
    indice = {}
    chamadas = {}
    for p, rel in arquivos():
        t = p.read_bytes().decode("utf-8", errors="replace")
        textos[rel] = t
        for c in CLASSE.findall(t):
            indice.setdefault(c, rel)
        for cls, met in CHAMADA.findall(t):
            if met in ("class", "function", "parent", "self", "static", "instanceof", "const", "trait"):
                continue
            chamadas.setdefault((cls, met), set()).add(rel)

    correcoes, duvidosos = [], []
    for (cls, met), usos in sorted(chamadas.items()):
        rel_classe = indice.get(cls)
        if rel_classe is None:
            continue
        t = textos[rel_classe]
        decl = re.search(r"^([ \t]*)(public|protected|private)\s+(static\s+)?function\s+" + re.escape(met) + r"\s*\(",
                         t, re.M)
        if decl is None or decl.group(3):
            continue
        ini = decl.end()
        fim = t.find("\n    }", ini)
        corpo = t[ini:fim if fim > 0 else ini + 4000]
        if "$this" in corpo:
            duvidosos.append((cls, met, rel_classe, sorted(usos)[:4]))
        else:
            correcoes.append((rel_classe, cls, met))

    for rel_classe, cls, met in correcoes:
        p = MART / rel_classe
        t = p.read_bytes().decode("utf-8", errors="replace")
        padrao = re.compile(r"^([ \t]*)(public|protected|private)(\s+)function(\s+)" + re.escape(met) + r"(\s*\()", re.M)
        novo, n = padrao.subn(r"\1\2\3static function\4" + met + r"\5", t, count=1)
        if n:
            p.write_bytes(novo.encode("utf-8"))
            textos[rel_classe] = novo
            print(f"static: {cls}::{met} ({rel_classe})")

    print(f"\n--- correcoes: {len(correcoes)} | duvidosos: {len(duvidosos)}")
    for cls, met, rel, usos in duvidosos:
        print(f"  DUVIDA {cls}::{met} ({rel}) usos={usos}")


if __name__ == "__main__":
    main()
