import os
import unittest

from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool

os.environ.setdefault("JWT_SECRET", "test-jwt-secret-value-with-32-chars")

from app.api.auth import router as auth_router
from app.core.database import Base, get_db
from app.core.models import Tenant, TenantStatus, User, UserRole, UserStatus
from app.core.security import get_permission_catalog, hash_password


class AuthPermissionsContractTests(unittest.TestCase):
    def setUp(self):
        self.engine = create_engine(
            "sqlite://",
            connect_args={"check_same_thread": False},
            poolclass=StaticPool,
        )
        self.SessionLocal = sessionmaker(bind=self.engine)
        Base.metadata.create_all(self.engine, tables=[Tenant.__table__, User.__table__])
        self.app = FastAPI()
        self.app.include_router(auth_router)

        # Seed a default tenant
        with Session(self.engine) as s:
            t = Tenant(company_code="test", name="Test Co", status=TenantStatus.active)
            s.add(t)
            s.commit()
            s.refresh(t)
            self._tenant_id = t.id

        def override_get_db():
            db = self.SessionLocal()
            try:
                yield db
            finally:
                db.close()

        self.app.dependency_overrides[get_db] = override_get_db
        self.addCleanup(self.app.dependency_overrides.clear)
        self.client = TestClient(self.app)

    def seed_user(self, **overrides) -> User:
        defaults = {
            "tenant_id": self._tenant_id,
            "username": "admin_user",
            "password_hash": hash_password("admin123"),
            "display_name": "Admin User",
            "role": UserRole.admin,
            "status": UserStatus.active,
            "allowed_permissions_json": None,
            "denied_permissions_json": None,
            "allowed_routes_json": None,
        }
        defaults.update(overrides)
        with Session(self.engine) as session:
            user = User(**defaults)
            session.add(user)
            session.commit()
            session.refresh(user)
            return user

    def test_login_response_expands_admin_permissions_for_clients(self):
        self.seed_user(
            denied_permissions_json=["settings.read"],
            allowed_permissions_json=["custom.export"],
        )

        response = self.client.post(
            "/api/auth/login",
            json={"company_code": "test", "username": "admin_user", "password": "admin123"},
        )

        self.assertEqual(200, response.status_code)
        payload = response.json()
        self.assertEqual("bearer", payload["token_type"])
        self.assertNotIn("*", payload["user"]["permissions"])
        self.assertIn("custom.export", payload["user"]["permissions"])
        self.assertIn("customers.write", payload["user"]["permissions"])
        self.assertNotIn("settings.read", payload["user"]["permissions"])
        expected_permissions = sorted(
            (set(get_permission_catalog()) - {"settings.read"}) | {"custom.export"}
        )
        self.assertEqual(expected_permissions, payload["user"]["permissions"])

    def test_me_response_includes_permission_overrides_and_allowed_routes(self):
        self.seed_user(
            username="viewer_user",
            password_hash=hash_password("viewer123"),
            role=UserRole.viewer,
            display_name="Viewer User",
            allowed_permissions_json=["invoices.send"],
            denied_permissions_json=["finance.read"],
            allowed_routes_json=["Dashboard", "Invoices"],
        )

        login_response = self.client.post(
            "/api/auth/login",
            json={"company_code": "test", "username": "viewer_user", "password": "viewer123"},
        )
        token = login_response.json()["access_token"]

        response = self.client.get(
            "/api/auth/me",
            headers={"Authorization": f"Bearer {token}"},
        )

        self.assertEqual(200, response.status_code)
        payload = response.json()
        self.assertEqual(["Dashboard", "Invoices"], payload["allowed_routes"])
        self.assertIn("invoices.send", payload["permissions"])
        self.assertIn("customers.read", payload["permissions"])
        self.assertNotIn("finance.read", payload["permissions"])
        self.assertNotIn("*", payload["permissions"])


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