import tempfile
import unittest
from pathlib import Path

from app import create_app
from app.extensions import db
from app.models import AccessKey, AdminUser, BatchCredential, DeliveryBatch
from app.security import hash_access_key


class PortalFlowTestCase(unittest.TestCase):
    def setUp(self):
        self.tempdir = tempfile.TemporaryDirectory()
        db_path = Path(self.tempdir.name) / "test.db"
        self.app = create_app(
            {
                "TESTING": True,
                "WTF_CSRF_ENABLED": False,
                "RATELIMIT_ENABLED": False,
                "SQLALCHEMY_DATABASE_URI": f"sqlite:///{db_path.as_posix()}",
                "SESSION_COOKIE_SECURE": False,
                "SECRET_KEY": "test-secret",
            }
        )
        self.client = self.app.test_client()

        with self.app.app_context():
            db.drop_all()
            db.create_all()

            admin = AdminUser(username="admin")
            admin.set_password("super-secret")
            db.session.add(admin)

            batch = DeliveryBatch(name="Batch One", description="Primary delivery batch", created_by=admin)
            db.session.add(batch)
            db.session.flush()

            credential = BatchCredential(batch=batch, sort_order=0)
            credential.value = "demo-api-key:demo-secret-number"
            db.session.add(credential)

            self.raw_key = "DLV-demo-test-key-123456"
            access_key = AccessKey(
                batch=batch,
                key_hash=hash_access_key(self.raw_key),
                key_hint="DLV-demo...3456",
                max_views=1,
            )
            db.session.add(access_key)
            db.session.commit()
            self.batch_id = batch.id
            self.key_id = access_key.id

    def tearDown(self):
        with self.app.app_context():
            db.session.remove()
            db.drop_all()
            db.engine.dispose()
        self.tempdir.cleanup()

    def test_public_unlock_reveals_credentials(self):
        response = self.client.post(
            "/unlock",
            data={"access_key": self.raw_key},
            follow_redirects=True,
        )
        self.assertEqual(response.status_code, 200)
        self.assertIn(b"demo-api-key:demo-secret-number", response.data)

        with self.app.app_context():
            access_key = db.session.get(AccessKey, self.key_id)
            self.assertEqual(access_key.view_count, 1)
            self.assertTrue(access_key.is_exhausted)

    def test_used_up_key_cannot_unlock_again(self):
        first_client = self.app.test_client()
        second_client = self.app.test_client()

        first = first_client.post("/unlock", data={"access_key": self.raw_key}, follow_redirects=False)
        self.assertEqual(first.status_code, 302)

        second = second_client.post("/unlock", data={"access_key": self.raw_key}, follow_redirects=True)
        self.assertEqual(second.status_code, 403)
        self.assertIn(b"invalid, expired, revoked, or already used up", second.data)

    def test_admin_can_create_batch_and_generate_keys(self):
        login = self.client.post(
            "/admin/login",
            data={"username": "admin", "password": "super-secret", "remember": "y"},
            follow_redirects=True,
        )
        self.assertEqual(login.status_code, 200)
        self.assertIn(b"Delivery control center", login.data)

        response = self.client.post(
            "/admin/batches/new",
            data={
                "name": "Batch Two",
                "description": "Extra batch",
                "credentials": "alpha:111\nbeta:222",
                "key_count": 2,
                "max_views": 5,
                "key_notes": "promo",
            },
            follow_redirects=True,
        )
        self.assertEqual(response.status_code, 200)
        self.assertIn(b"Generated keys", response.data)
        self.assertIn(b"Batch Two", response.data)
        self.assertIn(b"DLV-", response.data)

        with self.app.app_context():
            created = DeliveryBatch.query.filter_by(name="Batch Two").first()
            self.assertIsNotNone(created)
            self.assertEqual(len(created.credentials), 2)
            self.assertEqual(len(created.access_keys), 2)


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