"""Placement simplification: Rewarded ads use the CHAT placement (Chat Unlock).

Banner keeps the placement dropdown (landing/marketplace/sidebar); Rewarded hides
it and is always stored as the ``chat`` placement — the single home of the
watch-to-unlock flow. There is no separate ``rewarded`` placement. Backend
architecture is unchanged.
"""

import os
import shutil
import tempfile
import unittest
from pathlib import Path

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 (
    Advertisement,
    AdvertisementPlacement,
    AdvertisementType,
    User,
    UserRole,
)


class AdPlacementUiTests(unittest.TestCase):
    def setUp(self):
        self.app = create_app("testing")
        self.ctx = self.app.app_context()
        self.ctx.push()
        db.create_all()
        self._upload_dir = Path(tempfile.mkdtemp())
        self.app.config["ADVERTISEMENT_UPLOAD_FOLDER"] = self._upload_dir
        u = User(email="a@c.edu", password_hash=generate_password_hash("Passw0rd!"),
                 is_verified=True, role=UserRole.SUPER_ADMIN)
        db.session.add(u)
        db.session.commit()
        self.client = self.app.test_client()
        self.client.post("/login", data={"email": "a@c.edu", "password": "Passw0rd!"})

    def tearDown(self):
        shutil.rmtree(self._upload_dir, ignore_errors=True)
        db.session.remove()
        db.drop_all()
        self.ctx.pop()

    def _form(self, **overrides):
        data = {"title": "Advert", "advertisement_type": "banner", "placement": "landing",
                "media_type": "image", "priority": "5", "is_active": "y", "submit": "Save",
                "icon": "bi-stars"}
        data.update(overrides)
        return data

    def _post_new(self, **overrides):
        return self.client.post("/admin/advertisements/new", data=self._form(**overrides),
                                content_type="multipart/form-data", follow_redirects=True)

    def _get(self, title):
        return db.session.scalar(db.select(Advertisement).filter_by(title=title))

    # --- UI hooks --------------------------------------------------------------
    def test_form_has_placement_hooks_and_card(self):
        html = self.client.get("/admin/advertisements/new").get_data(as_text=True)
        self.assertIn("data-ad-placement-field", html)            # dropdown wrapper (Banner)
        self.assertIn("data-ad-placement-rewarded", html)         # read-only card (Rewarded)
        self.assertIn("Chat Unlock", html)
        self.assertIn("Automatically assigned", html)
        self.assertIn("unlock additional chat messages", html)
        # The read-only card is hidden by default; JS reveals it only for Rewarded.
        self.assertIn('col-md-6 d-none" data-ad-placement-rewarded', html)

    def test_placement_dropdown_excludes_chat(self):
        # Chat is reserved for rewarded ads and assigned automatically, so it must
        # not be an admin-selectable banner placement.
        html = self.client.get("/admin/advertisements/new").get_data(as_text=True)
        self.assertIn('value="landing"', html)
        self.assertIn('value="sidebar"', html)
        self.assertIn('value="marketplace"', html)
        # No 'chat' placement option (it is assigned automatically for rewarded ads).
        # 'value="rewarded"' still exists — but as an advertisement *type*, not a placement.
        self.assertNotIn('value="chat"', html)

    # --- Banner keeps the selector --------------------------------------------
    def test_banner_placement_selectable_and_stored(self):
        self._post_new(title="BM", advertisement_type="banner", placement="marketplace")
        ad = self._get("BM")
        self.assertEqual(ad.placement, AdvertisementPlacement.MARKETPLACE)

    def test_edit_banner_preselects_its_placement(self):
        self._post_new(title="BS", advertisement_type="banner", placement="sidebar")
        ad = self._get("BS")
        html = self.client.get(f"/admin/advertisements/{ad.public_id}/edit").get_data(as_text=True)
        self.assertIn('<option selected value="sidebar">', html)

    # --- Rewarded is fixed to the chat placement ------------------------------
    def test_rewarded_stores_chat_placement(self):
        self._post_new(title="RW", advertisement_type="rewarded", placement="landing", media_type="image")
        ad = self._get("RW")
        self.assertIsNotNone(ad)
        self.assertEqual(ad.placement, AdvertisementPlacement.CHAT)

    def test_rewarded_crafted_placement_normalised(self):
        # Any submitted (banner) placement is corrected to chat for a rewarded ad.
        self._post_new(title="RC", advertisement_type="rewarded", placement="marketplace", media_type="image")
        ad = self._get("RC")
        self.assertIsNotNone(ad)
        self.assertEqual(ad.placement, AdvertisementPlacement.CHAT)

    def test_banner_crafted_chat_placement_rejected(self):
        # 'chat' is not a valid banner placement choice, so a crafted banner+chat
        # request fails validation and no advertisement is created.
        self._post_new(title="BC", advertisement_type="banner", placement="chat")
        self.assertIsNone(self._get("BC"))

    def test_list_shows_chat_unlock_placement_label(self):
        # The admin list placement badge reads "Chat Unlock", never "Rewarded".
        self._post_new(title="RL", advertisement_type="rewarded", placement="landing", media_type="image")
        html = self.client.get("/admin/advertisements").get_data(as_text=True)
        self.assertIn("Chat Unlock", html)

    def test_edit_rewarded_shows_chat_card(self):
        self._post_new(title="RE", advertisement_type="rewarded", placement="landing", media_type="image")
        ad = self._get("RE")
        self.assertEqual(ad.placement, AdvertisementPlacement.CHAT)
        html = self.client.get(f"/admin/advertisements/{ad.public_id}/edit").get_data(as_text=True)
        self.assertIn("data-ad-placement-rewarded", html)           # read-only card present
        self.assertIn("Chat Unlock", html)

    # --- backward compatibility ------------------------------------------------
    def test_changing_rewarded_to_banner_uses_chosen_placement(self):
        # Create rewarded (placement forced chat), then edit to banner+marketplace.
        self._post_new(title="Flip", advertisement_type="rewarded", placement="landing", media_type="image")
        ad = self._get("Flip")
        self.assertEqual(ad.placement, AdvertisementPlacement.CHAT)
        self.client.post(f"/admin/advertisements/{ad.public_id}/edit",
                         data=self._form(title="Flip", advertisement_type="banner", placement="marketplace",
                                         media_type="image"),
                         content_type="multipart/form-data", follow_redirects=True)
        db.session.refresh(ad)
        self.assertEqual(ad.advertisement_type, AdvertisementType.BANNER)
        self.assertEqual(ad.placement, AdvertisementPlacement.MARKETPLACE)

    def test_changing_banner_to_rewarded_forces_chat(self):
        self._post_new(title="Up", advertisement_type="banner", placement="landing")
        ad = self._get("Up")
        self.client.post(f"/admin/advertisements/{ad.public_id}/edit",
                         data=self._form(title="Up", advertisement_type="rewarded", placement="landing",
                                         media_type="image"),
                         content_type="multipart/form-data", follow_redirects=True)
        db.session.refresh(ad)
        self.assertEqual(ad.advertisement_type, AdvertisementType.REWARDED)
        self.assertEqual(ad.placement, AdvertisementPlacement.CHAT)


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