"""Run only against an isolated, seeded demonstration database, never live data.
Usage: python tests/http_smoke.py http://127.0.0.1:8089
"""
import http.cookiejar
import datetime
import re
import sys
import urllib.error
import urllib.parse
import urllib.request
import uuid

BASE = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:8089"
TODAY = datetime.date.today().isoformat()
FUTURE = (datetime.date.today() + datetime.timedelta(days=3)).isoformat()
checks = 0

def expect(condition, message):
    global checks
    if not condition:
        raise AssertionError(message)
    checks += 1
    print("PASS", message)

class Client:
    def __init__(self, role):
        self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
        self.last = self.get("?page=login")[1]
        self.post("login", {"email": role + "@demo.school", "password": "SchoolDemo!2026"})
        expect("Welcome back" in self.last, role + " can sign in")

    def get(self, path):
        try:
            with self.opener.open(BASE + "/" + path) as r:
                self.last = r.read().decode("utf-8-sig")
                return r.status, self.last
        except urllib.error.HTTPError as e:
            self.last = e.read().decode("utf-8-sig")
            return e.code, self.last

    def post(self, action, values, return_to="?page=dashboard"):
        token = re.search(r'name="csrf" value="([a-f0-9]+)"', self.last)
        if not token:
            self.get("?page=account")
            token = re.search(r'name="csrf" value="([a-f0-9]+)"', self.last)
        data = {"csrf": token.group(1), "action": action, "return_to": return_to, **values}
        req = urllib.request.Request(BASE + "/?page=dashboard", urllib.parse.urlencode(data).encode())
        with self.opener.open(req) as r:
            self.last = r.read().decode("utf-8-sig")
        return self.last

admin = Client("admin")
for page in ["students", "student&id=1", "attendance", "fees", "academics", "academics&id=3", "results", "notices", "reports", "settings", "users", "notifications", "audit", "account", "statement&id=1", "report_card&id=1", "receipt&id=1"]:
    code, body = admin.get("?page=" + page)
    expect(code == 200 and "temporarily unavailable" not in body and "Fatal error" not in body and "Warning:" not in body, "administrator page " + page)
for kind in ["students", "attendance", "finance", "results"]:
    code, body = admin.get("?page=export&kind=" + kind)
    expect(code == 200 and len(body) > 100, "CSV export " + kind)

parent = Client("parent")
code, body = parent.get("?page=students")
expect("Anesu Moyo" in body and "Tanaka Moyo" in body and "Tadiwa Chirwa" not in body, "parent sees only linked children")
for page in ["student&id=2", "statement&id=2", "report_card&id=2", "settings", "users", "academics", "reports", "audit", "notifications", "receipt&id=2"]:
    code, body = parent.get("?page=" + page)
    expect(code == 403, "parent denied " + page)
parent.get("?page=account")
body = parent.post("school.save", {"name": "Attempted change"})
expect("does not have permission" in body, "parent mutation blocked on server")

student = Client("student")
code, body = student.get("?page=students")
expect("Anesu Moyo" in body and "Tanaka Moyo" not in body, "student sees own profile only")
code, body = student.get("?page=report_card&id=1")
expect("Science" not in body and "Mathematics" in body, "draft marks hidden from report card")

teacher = Client("teacher")
code, body = teacher.get("?page=fees")
expect(code == 403, "teacher denied financial records")
teacher.get("?page=account")
body = teacher.post("payment.create", {"student_id": "1"})
expect("does not have permission" in body, "teacher cannot record payments")

cashier = Client("cashier")
key = uuid.uuid4().hex
payload = {"student_id": "1", "amount": "12.34", "currency": "USD", "method": "cash", "reference": "TEST-" + key, "paid_on": TODAY, "idempotency_key": key}
cashier.get("?page=fees")
body = cashier.post("payment.create", payload, "?page=fees")
expect("Payment saved. Receipt" in body, "cash payment saved")
receipt = re.search(r'Payment saved\. Receipt (RC-[A-Z0-9-]+)', body).group(1)
body = cashier.post("payment.create", payload, "?page=fees")
expect("No duplicate was created" in body, "payment retry is idempotent")
row_html = next(r for r in re.findall(r'<tr\b[^>]*>(.*?)</tr>', body, re.S) if receipt in r)
payment_id = re.search(r'page=receipt&id=(\d+)', row_html).group(1)
payload_bad = {**payload, "amount": "-1", "idempotency_key": uuid.uuid4().hex}
body = cashier.post("payment.create", payload_bad, "?page=fees")
expect("positive amount" in body, "negative payment rejected")

bursar = Client("bursar")
bursar.get("?page=fees")
body = bursar.post("payment.reverse", {"payment_id": payment_id, "reason": "Integration test correction"}, "?page=fees")
expect("Payment reversed; original receipt retained" in body, "independent reviewer can reverse receipt")
code, body = admin.get("?page=receipt&id=" + payment_id)
expect(code == 403, "reversed receipt cannot be presented as confirmed")

# A transfer must be independently verified before changing an account balance.
cashier.get("?page=fees")
key2 = uuid.uuid4().hex
body = cashier.post("payment.create", {**payload, "method": "bank_transfer", "reference": "TEST-TRANSFER-" + key2, "idempotency_key": key2}, "?page=fees")
expect("recorded as unverified" in body, "bank transfer remains pending")
bursar.get("?page=fees")
pending_form = re.search(r'<form[^>]*>.*?name="action" value="payment.verify".*?</form>', bursar.last, re.S).group(0)
pending_id = re.findall(r'name="payment_id" value="(\d+)"', pending_form)[-1]
body = bursar.post("payment.verify", {"payment_id": pending_id}, "?page=fees")
expect("Payment verified and student balance updated" in body, "reviewer verifies pending transfer")

# Marks workflow: teacher edits; head reviews and publishes; repeated publication is blocked.
teacher.get("?page=academics&id=3")
class_students = [1, 7, 13, 19]
marks_payload = {"assessment_id": "3"}
for i in class_students:
    marks_payload.update({f"marks[{i}][status]": "marked", f"marks[{i}][score]": "80", f"marks[{i}][comment]": "Verified test mark"})
body = teacher.post("marks.save", marks_payload, "?page=academics&id=3")
expect("Draft marks saved" in body, "teacher saves marks for assigned class")
body = teacher.post("assessment.submit", {"assessment_id": "3"}, "?page=academics&id=3")
expect("submitted to the head" in body, "teacher submits complete assessment")
head = Client("head")
head.get("?page=academics&id=3")
body = head.post("assessment.review", {"assessment_id": "3"}, "?page=academics&id=3")
expect("Assessment reviewed" in body, "head reviews assessment")
body = head.post("assessment.publish", {"assessment_id": "3"}, "?page=academics&id=3")
expect("Results published" in body, "head publishes results and queues emails")
code, body = student.get("?page=report_card&id=1")
expect("Science" in body and "Verified test mark" in body, "student sees newly published result")
head.get("?page=academics&id=3")
body = head.post("assessment.publish", {"assessment_id": "3"}, "?page=academics&id=3")
expect("Review this assessment before publishing" in body, "repeat publication rejected")

teacher.get("?page=attendance")
body = teacher.post("attendance.save", {"class_id": "1", "attendance_date": TODAY, "attendance[1][status]": "late", "attendance[1][arrival_time]": "08:10", "attendance[1][reason]": "Integration test"}, "?page=attendance&class_id=1&date=" + TODAY)
expect("attendance record(s) saved" in body, "confirmed late arrival saved")
body = teacher.post("attendance.save", {"class_id": "1", "attendance_date": TODAY, "attendance[1][status]": "late", "attendance[1][arrival_time]": "08:10", "attendance[1][reason]": "Integration test"}, "?page=attendance&class_id=1&date=" + TODAY)
expect("0 attendance record(s) saved" in body, "unchanged attendance produces no repeat alert")
body = teacher.post("attendance.save", {"class_id": "1", "attendance_date": FUTURE, "attendance[1][status]": "present"}, "?page=attendance")
expect("no later than today" in body, "future attendance rejected")

admin.get("?page=account")
bad_csrf = urllib.request.Request(BASE + "/", urllib.parse.urlencode({"action": "notice.create", "csrf": "invalid"}).encode())
try:
    admin.opener.open(bad_csrf)
    expect(False, "invalid CSRF rejected")
except urllib.error.HTTPError as e:
    expect(e.code == 419, "invalid CSRF rejected")
print(f"\n{checks} HTTP and workflow checks passed.")
