#!/usr/bin/env python3
"""Periksa rujukan ke anggota statis: Warna.x, ApiService.x, dan sejenisnya.

Kesalahan seperti `Warna.nila` pada aplikasi yang temanya tidak punya warna
itu tidak terlihat oleh pemeriksa argumen — nama kelasnya ada, jumlah
argumennya benar, yang tidak ada justru anggotanya. Compiler menolaknya, dan
itu baru ketahuan saat build gagal.

Pemakaian:
    python3 tools/cek-anggota.py mobile
    python3 tools/cek-anggota.py mobile_siswa
"""
import re, os, sys

AKAR = sys.argv[1] if len(sys.argv) > 1 else '.'

# Kelas yang isinya kita kenali: statis semua, tidak diwarisi dari mana pun
KELAS_DIPERIKSA = ['Warna', 'ApiService']

berkas = []
for root, _, fs in os.walk(os.path.join(AKAR, 'lib')):
    for f in fs:
        if f.endswith('.dart'):
            berkas.append(os.path.join(root, f))


def anggota_kelas(nama):
    """Kumpulkan anggota statis sebuah kelas dari seluruh berkas."""
    punya = set()
    for p in berkas:
        isi = open(p).read()
        m = re.search(r'class\s+' + nama + r'\b[^{]*\{', isi)
        if not m:
            continue

        # Ambil badan kelas dengan menyeimbangkan kurung kurawal
        i, dalam = m.end(), 1
        while i < len(isi) and dalam:
            if isi[i] == '{':
                dalam += 1
            elif isi[i] == '}':
                dalam -= 1
            i += 1
        badan = isi[m.end():i]

        # Tipe kembalian bisa berlapis seperti Future<Map<String, dynamic>>,
        # jadi polanya menerima kurung sudut bersarang sebelum nama anggota
        tipe = r'[\w<>,\s\?]+?'
        punya |= set(re.findall(r'static\s+(?:const|final)\s+' + tipe + r'\s*(\w+)\s*=', badan))
        punya |= set(re.findall(r'static\s+const\s+(\w+)\s*=', badan))
        punya |= set(re.findall(r'static\s+' + tipe + r'\s+(\w+)\s*\(', badan))
        punya |= set(re.findall(r'static\s+' + tipe + r'\s+get\s+(\w+)', badan))
    return punya


milik = {k: anggota_kelas(k) for k in KELAS_DIPERIKSA}
total = 0

for p in sorted(berkas):
    isi = open(p).read()
    temuan = []
    for kelas, punya in milik.items():
        if not punya:
            continue   # kelas tidak ada di proyek ini, lewati
        for m in re.finditer(r'(?<![\w.])' + kelas + r'\.(\w+)', isi):
            nama = m.group(1)
            if nama not in punya:
                baris = isi[:m.start()].count('\n') + 1
                temuan.append((baris, f"{kelas}.{nama} tidak ada pada kelas {kelas}"))
    if temuan:
        print(f"### {os.path.relpath(p, AKAR)}")
        for baris, pesan in sorted(set(temuan)):
            print(f"    baris {baris}: {pesan}")
            total += 1
        print()

ringkas = ', '.join(f"{k}: {len(v)} anggota" for k, v in milik.items() if v)
print(f"Diperiksa — {ringkas} | Rujukan tidak dikenal: {total}")
