"""IDOR / cross-tenant access control tests.

Verifies that a user in Company A cannot read Company B's data via
company_id parameters (URL or query string), and that the tenancy
helpers in app/utils/tenancy.py enforce membership.
"""
import os
import sys
import unittest
import json

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
os.environ.setdefault('DATABASE_URL', 'sqlite:///test_idor.db')
os.environ.setdefault('SECRET_KEY', 'test-secret-key-for-testing')

from app import create_app
from app.models import db, User, Company, UserCompany


class IDORTestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.app = create_app()
        cls.app.config['TESTING'] = True
        cls.app.config['RATELIMIT_ENABLED'] = False
        cls.client = cls.app.test_client()
        with cls.app.app_context():
            db.drop_all()
            db.create_all()

            company_a = Company(name='IDOR Test Co A')
            company_b = Company(name='IDOR Test Co B')
            db.session.add_all([company_a, company_b])
            db.session.flush()

            user_a = User(email='user_a@idor.test', full_name='User A')
            user_a.set_password('UserAPassword1!')
            super_admin = User(
                email='admin@idor.test', full_name='Super', role='super_admin'
            )
            super_admin.set_password('AdminPassword1!')
            db.session.add_all([user_a, super_admin])
            db.session.flush()

            db.session.add(UserCompany(
                user_id=user_a.id, company_id=company_a.id, role='owner'
            ))
            db.session.commit()

            cls.company_a_id = company_a.id
            cls.company_b_id = company_b.id
            cls.user_a_id = user_a.id

    def _login(self, email, password):
        resp = self.client.post(
            '/api/auth/login',
            data=json.dumps({'email': email, 'password': password}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 200, resp.get_data(as_text=True))

    def _logout(self):
        with self.client.session_transaction() as sess:
            sess.clear()

    # ── unauthenticated ──────────────────────────────────────────────

    def test_unauthenticated_kpis_denied(self):
        self._logout()
        resp = self.client.get(f'/dashboard/api/kpis/{self.company_a_id}')
        self.assertIn(resp.status_code, (401, 302))

    # ── tenancy helper semantics ─────────────────────────────────────

    def test_user_in_company_helper(self):
        from app.utils.tenancy import user_in_company
        with self.app.app_context():
            self.assertTrue(user_in_company(self.user_a_id, self.company_a_id))
            self.assertFalse(user_in_company(self.user_a_id, self.company_b_id))
            self.assertFalse(user_in_company(None, self.company_a_id))
            self.assertFalse(user_in_company(self.user_a_id, None))

    # ── cross-tenant API access ──────────────────────────────────────

    def test_member_can_access_own_company(self):
        self._login('user_a@idor.test', 'UserAPassword1!')
        resp = self.client.get(f'/dashboard/api/kpis/{self.company_a_id}')
        self.assertEqual(resp.status_code, 200)
        self._logout()

    def test_member_cannot_access_other_company(self):
        """User A must get 403 when requesting Company B's KPIs (IDOR)."""
        self._login('user_a@idor.test', 'UserAPassword1!')
        resp = self.client.get(f'/dashboard/api/kpis/{self.company_b_id}')
        self.assertEqual(resp.status_code, 403)
        self._logout()

    def test_member_cannot_access_other_company_forecast(self):
        self._login('user_a@idor.test', 'UserAPassword1!')
        resp = self.client.get(f'/dashboard/api/forecast/{self.company_b_id}')
        self.assertEqual(resp.status_code, 403)
        self._logout()

    def test_super_admin_bypasses_membership(self):
        self._login('admin@idor.test', 'AdminPassword1!')
        resp = self.client.get(f'/dashboard/api/kpis/{self.company_b_id}')
        self.assertEqual(resp.status_code, 200)
        self._logout()

    def test_nonexistent_company_id_denied_for_member(self):
        """Random/guessed company IDs are rejected, not leaked."""
        self._login('user_a@idor.test', 'UserAPassword1!')
        resp = self.client.get('/dashboard/api/kpis/00000000-0000-0000-0000-000000000000')
        self.assertEqual(resp.status_code, 403)
        self._logout()


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