"""Student-verification analytics & immutable history tests."""

import os
import unittest
from datetime import timedelta

os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"

from werkzeug.security import generate_password_hash

from app import create_app
from app.auth import verification as v
from app.extensions import db
from app.models import (
    StudentVerificationHistory,
    StudentVerificationStatus,
    User,
    UserProfile,
    UserRole,
    VerificationHistoryAction,
)
from app.models.base import utcnow

S = StudentVerificationStatus
A = VerificationHistoryAction


class VerificationAnalyticsTests(unittest.TestCase):
    def setUp(self):
        self.app = create_app("testing")
        self.ctx = self.app.app_context()
        self.ctx.push()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.ctx.pop()

    def _user(self, email="s@c.edu", role=UserRole.STUDENT, name="Student One"):
        u = User(email=email, password_hash=generate_password_hash("Passw0rd!"), is_verified=True, role=role)
        u.profile = UserProfile(full_name=name)
        db.session.add(u)
        db.session.commit()
        return u

    def _admin(self):
        return self._user("admin@c.edu", UserRole.SUPER_ADMIN, "Admin")

    def _history(self, user):
        return db.session.scalars(
            db.select(StudentVerificationHistory)
            .where(StudentVerificationHistory.user_id == user.id)
            .order_by(StudentVerificationHistory.id)
        ).all()

    def _submit(self, u, sid="ID1"):
        v.submit_verification(u, "9876543210", "XYZ University", sid)

    # --- KPI counts ------------------------------------------------------------
    def test_counts(self):
        admin = self._admin()
        p = self._user("p@c.edu"); self._submit(p)
        ver = self._user("v@c.edu"); self._submit(ver); v.approve_verification(ver, admin)
        rej = self._user("r@c.edu"); self._submit(rej); v.reject_verification(rej, admin, "no")
        self.assertEqual(v.get_pending_count(), 1)
        self.assertEqual(v.get_verified_count(), 1)
        self.assertEqual(v.get_rejected_count(), 1)

    def test_verification_rate(self):
        admin = self._admin()
        for i in range(3):
            u = self._user(f"u{i}@c.edu"); self._submit(u, f"ID{i}")
            if i < 2:
                v.approve_verification(u, admin)
        self.assertEqual(v.get_verification_rate(), round(2 / 3 * 100, 1))  # 66.7

    def test_verification_rate_zero_when_none(self):
        self.assertEqual(v.get_verification_rate(), 0.0)

    def test_average_review_time(self):
        admin = self._admin()
        u = self._user("u@c.edu"); self._submit(u)
        u.profile.verification_submitted_at = utcnow() - timedelta(days=2)
        db.session.commit()
        v.approve_verification(u, admin)     # reviewed_at = now → ~2 days
        self.assertAlmostEqual(v.get_average_review_time(), 2.0, delta=0.05)

    def test_average_review_time_none_when_no_data(self):
        self.assertIsNone(v.get_average_review_time())

    def test_recent_activity_newest_first(self):
        admin = self._admin()
        u = self._user("u@c.edu", name="Alice Johnson"); self._submit(u)
        v.approve_verification(u, admin)
        recent = v.get_recent_verification_activity(limit=10)
        self.assertEqual(recent[0]["action"], "approved")     # newest first
        self.assertEqual(recent[1]["action"], "submitted")
        self.assertEqual(recent[1]["name"], "Alice Johnson")  # name resolved (no N+1)

    def test_dashboard_shape(self):
        d = v.get_verification_dashboard()
        for key in ("pending", "verified", "rejected", "verification_rate",
                    "avg_review_days", "recent_activity"):
            self.assertIn(key, d)

    # --- history creation ------------------------------------------------------
    def test_history_created_on_each_event(self):
        admin = self._admin()
        u = self._user("u@c.edu")
        self._submit(u, "2023CS100")
        h = self._history(u)
        self.assertEqual(len(h), 1)
        self.assertEqual(h[0].action, A.SUBMITTED)
        self.assertEqual(h[0].submitted_university, "XYZ University")
        self.assertEqual(h[0].submitted_student_id, "2023CS100")

        v.approve_verification(u, admin)
        h = self._history(u)
        self.assertEqual(len(h), 2)
        self.assertEqual(h[1].action, A.APPROVED)
        self.assertEqual(h[1].reviewed_by, admin.id)

        # verified user updates → UPDATED
        self._submit(u, "2023CS101")
        h = self._history(u)
        self.assertEqual(h[2].action, A.UPDATED)

        v.reject_verification(u, admin, "bad id")
        h = self._history(u)
        self.assertEqual(h[3].action, A.REJECTED)
        self.assertEqual(h[3].review_reason, "bad id")

    def test_history_is_immutable_earlier_rows_unchanged(self):
        admin = self._admin()
        u = self._user("u@c.edu")
        self._submit(u)
        first = self._history(u)[0]
        snapshot = (first.id, first.action, first.created_at, first.submitted_university)

        v.approve_verification(u, admin)
        rows = self._history(u)
        self.assertEqual(len(rows), 2)                                  # appended, not replaced
        self.assertEqual((rows[0].id, rows[0].action, rows[0].created_at,
                          rows[0].submitted_university), snapshot)       # original untouched

    # --- access control --------------------------------------------------------
    def test_student_view_excludes_moderation_admin_view_includes(self):
        admin = self._admin()
        u = self._user("u@c.edu")
        self._submit(u)
        v.reject_verification(u, admin, "secret reason")

        student = v.get_history_timeline(u, include_moderation=False)
        admin_view = v.get_history_timeline(u, include_moderation=True)

        self.assertNotIn("reviewer", student[0])       # never exposed to students
        self.assertNotIn("reason", student[0])
        self.assertEqual(admin_view[0]["action"], "rejected")   # newest first
        self.assertEqual(admin_view[0]["reason"], "secret reason")
        self.assertEqual(admin_view[0]["reviewer"], "Admin")

    def test_history_scoped_to_user(self):
        u1 = self._user("u1@c.edu"); self._submit(u1)
        u2 = self._user("u2@c.edu"); self._submit(u2)
        self.assertEqual(len(v.get_history_timeline(u1, include_moderation=False)), 1)

    # --- resubmission accuracy -------------------------------------------------
    def test_stats_accurate_after_resubmission(self):
        admin = self._admin()
        u = self._user("u@c.edu")
        self._submit(u)
        v.reject_verification(u, admin, "no")
        self.assertEqual(v.get_rejected_count(), 1)

        self._submit(u, "ID2")                         # resubmit → pending
        self.assertEqual(v.get_rejected_count(), 0)
        self.assertEqual(v.get_pending_count(), 1)

        v.approve_verification(u, admin)
        self.assertEqual(v.get_verified_count(), 1)
        self.assertEqual(v.get_verification_rate(), 100.0)   # one distinct submitter, now verified
        self.assertEqual(len(self._history(u)), 4)           # submit, reject, submit, approve

    # --- admin dashboard integration ------------------------------------------
    def test_admin_dashboard_shows_pending_verification_card(self):
        admin = self._admin()
        u = self._user("u@c.edu"); self._submit(u)
        client = self.app.test_client()
        client.post("/login", data={"email": "admin@c.edu", "password": "Passw0rd!"})
        html = client.get("/admin/analytics").get_data(as_text=True)
        self.assertIn("Pending Verifications", html)
        # links to the verification queue
        self.assertIn("/admin/verifications", html)

    def test_student_profile_history_hides_reviewer(self):
        reviewer = self._user("reviewer@c.edu", UserRole.SUPER_ADMIN, "Reviewer Bob")
        u = self._user("u@c.edu", name="Alice Johnson"); self._submit(u)
        v.approve_verification(u, reviewer)
        client = self.app.test_client()
        client.post("/login", data={"email": "u@c.edu", "password": "Passw0rd!"})
        html = client.get("/profile").get_data(as_text=True)
        self.assertIn("Verification History", html)
        self.assertIn("Approved", html)
        self.assertNotIn("Reviewer Bob", html)     # reviewer never shown to the student

    def test_admin_verification_page_shows_kpis(self):
        admin = self._admin()
        u = self._user("u@c.edu"); self._submit(u)
        client = self.app.test_client()
        client.post("/login", data={"email": "admin@c.edu", "password": "Passw0rd!"})
        html = client.get("/admin/verifications").get_data(as_text=True)
        self.assertIn("Verification Rate", html)
        self.assertIn("Recent verification activity", html)


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