"""Premium Management v2 tests (admin-configurable payment / plans / features)."""

import os
import unittest

os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"

from werkzeug.security import generate_password_hash

from app import create_app
from app.extensions import db
from app.models import Notification, PremiumFeature, PremiumPlan, User
from app.models.enums import PremiumRequestStatus, UserRole
from app.premium import config, services


class PremiumConfigTests(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, role=UserRole.STUDENT):
        u = User(is_verified=True, email=email, password_hash=generate_password_hash("Passw0rd!"), role=role)
        db.session.add(u)
        db.session.commit()
        return u

    def _login(self, email):
        c = self.app.test_client()
        c.post("/login", data={"email": email, "password": "Passw0rd!"})
        return c

    # --- defaults / dynamic config ---
    def test_defaults_are_seeded(self):
        self.assertEqual(config.get_payment_settings().active_upi_id, "studentbazaar@upi")
        self.assertEqual([p.name for p in config.get_plans()], ["Monthly", "Yearly"])
        self.assertEqual(len(config.get_features()), 6)

    def test_admin_updates_upi_and_page_reflects_it(self):
        config.update_payment_settings(active_upi_id="fresh@upi", enable_upi=True,
                                       enable_bank=True, enable_cash=True)
        self._user("s@c.edu")
        html = self._login("s@c.edu").get("/app/premium/").get_data(as_text=True)
        self.assertIn("fresh@upi", html)
        self.assertNotIn("studentbazaar@upi", html)

    def test_disabling_payment_method_hides_it(self):
        config.update_payment_settings(enable_upi=False, enable_bank=True, enable_cash=True,
                                       active_upi_id="x@upi")
        methods = [m for m, _ in config.get_enabled_payment_methods()]
        self.assertNotIn("upi", methods)
        self.assertIn("bank_transfer", methods)
        self.assertIn("cash", methods)

    def test_plans_loaded_dynamically(self):
        config.create_plan(name="Semester", duration_days=120, price=499, sort_order=3)
        names = [p.name for p in config.get_plans(enabled_only=True)]
        self.assertIn("Semester", names)
        # request form offers the new plan
        self.assertIn("Semester", [name for name, _ in config.plan_choices()])

    def test_features_loaded_dynamically(self):
        before = len(config.get_features())
        config.create_feature(title="Eco Seller", icon="bi-tree", display_order=9)
        self.assertEqual(len(config.get_features()), before + 1)

    def test_disabled_plan_not_offered(self):
        plan = config.get_plans()[0]
        config.update_plan(plan, enabled=False)
        self.assertNotIn(plan.name, [name for name, _ in config.plan_choices()])

    # --- QR ---
    def test_qr_generated_when_upi_enabled(self):
        config.update_payment_settings(active_upi_id="pay@upi", enable_upi=True)
        uri = config.upi_qr_data_uri()
        self.assertIsNotNone(uri)
        self.assertTrue(uri.startswith("data:image/png;base64,"))

    def test_qr_hidden_when_upi_disabled(self):
        config.update_payment_settings(enable_upi=False)
        self.assertIsNone(config.upi_qr_data_uri())
        self.assertIsNone(config.upi_payment_uri())

    # --- adaptive validation (reuses existing form logic, driven by config) ---
    def test_reference_required_for_upi_not_cash(self):
        from app.premium.forms import PremiumRequestForm
        with self.app.test_request_context(method="POST", data={
            "plan_name": "Monthly", "payment_method": "upi", "transaction_reference": ""}):
            form = PremiumRequestForm()
            form.plan_name.choices = [("Monthly", "Monthly")]
            form.payment_method.choices = [("upi", "UPI"), ("cash", "Cash")]
            self.assertFalse(form.validate())
            self.assertIn("transaction_reference", form.errors)
        with self.app.test_request_context(method="POST", data={
            "plan_name": "Monthly", "payment_method": "cash", "transaction_reference": ""}):
            form = PremiumRequestForm()
            form.plan_name.choices = [("Monthly", "Monthly")]
            form.payment_method.choices = [("upi", "UPI"), ("cash", "Cash")]
            self.assertTrue(form.validate())  # cash needs no reference

    # --- notifications ---
    def test_notifications_on_submit_and_review(self):
        student = self._user("s@c.edu")
        admin = self._user("a@c.edu", UserRole.SUPER_ADMIN)
        req = services.create_premium_request(student, plan_name="Monthly",
                                               payment_method="upi", transaction_reference="TXN1")
        titles = [n.title for n in db.session.scalars(
            db.select(Notification).where(Notification.user_id == student.id)).all()]
        self.assertIn("Premium Request Submitted", titles)
        # rejection notifies with the reason and allows resubmission
        services.reject_premium_request(req, admin, reason="Reference not found")
        latest = db.session.scalar(db.select(Notification).where(
            Notification.user_id == student.id).order_by(Notification.id.desc()))
        self.assertEqual(latest.title, "Premium Request Update")
        self.assertIn("Reference not found", latest.message)
        self.assertEqual(req.review_note, "Reference not found")

    def test_approval_uses_configured_duration(self):
        student = self._user("s@c.edu")
        admin = self._user("a@c.edu", UserRole.SUPER_ADMIN)
        # legacy lowercase plan name still resolves (case-insensitive)
        req = services.create_premium_request(student, plan_name="monthly", payment_method="cash")
        sub = services.approve_premium_request(req, admin)
        self.assertEqual(req.status, PremiumRequestStatus.APPROVED)
        self.assertGreater((sub.end_date - sub.start_date).days, 27)  # ~30-day plan

    # --- receipt upload (Part 7) ---
    def _image_file(self, name="receipt.png"):
        import io
        from PIL import Image
        from werkzeug.datastructures import FileStorage
        buf = io.BytesIO()
        Image.new("RGB", (10, 10), "blue").save(buf, "PNG")
        buf.seek(0)
        return FileStorage(stream=buf, filename=name, content_type="image/png")

    def test_receipt_is_saved_and_attached(self):
        student = self._user("s@c.edu")
        path = services.save_premium_receipt(self._image_file())
        self.assertIsNotNone(path)
        req = services.create_premium_request(student, plan_name="Monthly",
                                              payment_method="upi", transaction_reference="TXN", receipt_path=path)
        self.assertEqual(req.receipt_path, path)

    def test_receipt_rejects_non_image(self):
        from werkzeug.datastructures import FileStorage
        import io
        bad = FileStorage(stream=io.BytesIO(b"not an image"), filename="x.png", content_type="image/png")
        with self.assertRaises(ValueError):
            services.save_premium_receipt(bad)

    # --- more info + resubmission (Parts 8 & 9) ---
    def test_more_info_status_notification_and_resubmission(self):
        student = self._user("s@c.edu")
        admin = self._user("a@c.edu", UserRole.SUPER_ADMIN)
        req = services.create_premium_request(student, plan_name="Monthly", payment_method="cash")
        services.request_more_info(req, admin, "Please send a clearer receipt.")
        self.assertEqual(req.status, PremiumRequestStatus.MORE_INFO)
        self.assertEqual(req.review_note, "Please send a clearer receipt.")
        latest_notif = db.session.scalar(db.select(Notification).where(
            Notification.user_id == student.id).order_by(Notification.id.desc()))
        self.assertEqual(latest_notif.title, "More Information Needed")
        self.assertIn("clearer receipt", latest_notif.message)
        # a MORE_INFO request is no longer pending, so the student can resubmit
        again = services.create_premium_request(student, plan_name="Monthly", payment_method="upi",
                                                transaction_reference="TXN2")
        self.assertEqual(again.status, PremiumRequestStatus.PENDING)

    def test_status_timeline_renders_for_each_state(self):
        student = self._user("s@c.edu")
        admin = self._user("a@c.edu", UserRole.SUPER_ADMIN)
        req = services.create_premium_request(student, plan_name="Monthly", payment_method="cash")
        client = self._login("s@c.edu")
        html = client.get("/app/premium/request/success").get_data(as_text=True)
        self.assertIn("premium-timeline", html)
        self.assertIn("Awaiting Verification", html)
        # after rejection the timeline shows the reason + resubmit CTA
        services.reject_premium_request(req, admin, reason="Could not verify payment")
        html = client.get("/app/premium/request/success").get_data(as_text=True)
        self.assertIn("Could not verify payment", html)
        self.assertIn("Submit a new request", html)

    # --- admin management page ---
    def test_admin_can_open_and_update_settings(self):
        self._user("a@c.edu", UserRole.SUPER_ADMIN)
        client = self._login("a@c.edu")
        self.assertEqual(client.get("/app/premium/admin/settings").status_code, 200)
        client.post("/app/premium/admin/settings/payment",
                    data={"active_upi_id": "adminpay@upi", "enable_upi": "1"})
        self.assertEqual(config.get_payment_settings().active_upi_id, "adminpay@upi")


if __name__ == "__main__":
    unittest.main()
