import math
from unittest import TestCase
import unittest
from pygeai.organization.clients import OrganizationClient
from pygeai.core.common.exceptions import APIResponseError 


class TestOrgAccessControlProjectRolesIntegration(TestCase):
    __project_attrs = [
        'organizationId', 'organizationName', 'projectDescription', 'projectId', 'projectName', 'roles'
    ]    

    __role_attrs = [
        'externalId', 'id', 'name', 'origin', 'type'
    ]

    def setUp(self):
        """
        Set up the test environment.
        """
        self.organization_client = OrganizationClient(alias="org")
        self.project_id = "be4889df-cacc-4e6f-b3bb-153c4ac0d168"


    def __get_project_roles(self, project_id=None, **kwargs):
        """
        Helper method to call get_project_roles with default or custom parameters.
        """
        return self.organization_client.get_project_roles(
            project_id=project_id if project_id is not None else self.project_id,
            **kwargs
        )


    def test_get_project_roles_valid_project_id(self):
        """Test successful retrieval of project roles with valid project ID."""
        response = self.__get_project_roles()

        self.assertIn("count", response, "Response should have a 'count' key")        
        self.assertIn("pages", response, "Response should have a 'pages' key")
        
        for project in response["projects"]:            
            for project_attr in self.__project_attrs:
                self.assertIn(project_attr, project, f"Response should have a '{project_attr}' key")
                
                for role in project["roles"]: 
                    for role_attr in self.__role_attrs:
                        self.assertIn(role_attr, role, f"Response should have a '{role_attr}' key")


    def test_get_project_roles_with_pagination(self):
        """Test project roles retrieval with pagination parameters."""
        test_params = [
            {"start_page": 1, "page_size": 1},
            {"start_page": 1, "page_size": 2},
            {"start_page": 1, "page_size": 3},
        ]
        
        for params in test_params:
            with self.subTest(params=params):
                response = self.__get_project_roles(**params)

                expected_pages = math.ceil(response["count"] / params["page_size"]) 
                self.assertEqual(
                    response["pages"], expected_pages,
                    f"Expected {expected_pages} pages for params {params}, got {response['pages']}"
                )


    def test_get_project_roles_with_ordering(self):
        """Test project roles retrieval with different ordering parameters."""
        test_params = [
            {"order_key": "name", "order_direction": "asc"},
            {"order_key": "name", "order_direction": "desc"}
        ]
        
        for params in test_params:
            with self.subTest(params=params):
                response = self.__get_project_roles(**params)

                roles = response["projects"][0]["roles"]
                isOrdered = False
                if params.get("order_direction") == "asc":
                    isOrdered = all(
                        roles[i - 1]["name"] <= roles[i]["name"]
                        for i in range(1, len(roles))
                    )
                else:
                    isOrdered = all(
                        roles[i - 1]["name"] >= roles[i]["name"]
                        for i in range(1, len(roles))
                    )

                self.assertTrue(
                    isOrdered, 
                    f"Roles should be sorted in {'ascending' if params['order_direction'] == 'asc' else 'descending'} order by name"
                )


    def test_get_project_roles_with_role_types_filtering(self):
        """Test project roles retrieval with role types filtering."""
        test_params = [
            {"role_types": "backend"},
            {"role_types": "frontend"}
        ]
        
        for params in test_params:
            with self.subTest(params=params):
                response = self.__get_project_roles(**params)
                
                if response["count"] > 0:
                    for role in response["projects"][0]["roles"]:
                        self.assertEqual(
                            role["type"],
                            "Backend" if params["role_types"] == "backend" else "Frontend",
                            f"Expected role type to be {'Backend' if params['role_types'] == 'backend' else 'Frontend'} for params {params}"
                        )


    def test_get_project_roles_invalid_project_id(self):
        """Test error when using invalid project ID."""
        with self.assertRaises(APIResponseError) as exception:
            self.__get_project_roles(project_id="invalid-project-id")

        self.assertIn(
            "Organization or Project mismatch: verify the provided authorization",
            str(exception.exception)
        )


    def test_get_project_roles_empty_project_id(self):
        """Test error when project ID is empty."""
        with self.assertRaises(APIResponseError) as exception:
            self.__get_project_roles(project_id="")

        self.assertIn(
            "Organization or Project mismatch: verify the provided authorization",
            str(exception.exception)
        )


    def test_get_project_roles_invalid_role_types(self):
        """Test behavior with invalid role types."""
        invalid_role_types = ["invalid_role", "nonexistent", "bad_type"]
        
        for role_type in invalid_role_types:
            with self.subTest(role_type=role_type):
                # Some implementations might filter out invalid types, others might error
                with self.assertRaises(APIResponseError) as exception:
                    self.__get_project_roles(role_types=role_type)

                self.assertIn(
                   "Field roleTypes is out of range",
                    str(exception.exception),
                    f"Expected error about invalid role type for {role_type}"
                )

    @unittest.skip("Requires project_id when trying to send an invalid api_key")
    def test_get_project_roles_unauthorized_access(self):
        """Test error when user doesn't have access to the project."""
        # Create a client with invalid credentials
        unauthorized_client = OrganizationClient(api_key="invalid")
        
        with self.assertRaises(APIResponseError) as exception:
            unauthorized_client.get_project_roles(project_id=self.project_id)

        self.assertIn(
            "Unauthorized",
            str(exception.exception).lower(),
            "Expected authorization-related error message"
        )


    def test_get_project_roles_no_access_to_project(self):
        """Test error when user has valid credentials but no access to specific project."""
        # project Id that exists but it's not accessible by the api_key
        restricted_project_id = "6f5c69f1-232d-47d6-84f8-8bec87946bbc"
        
        with self.assertRaises(APIResponseError) as exception:
            self.__get_project_roles(project_id=restricted_project_id)
        print(str(exception.exception))
        self.assertIn(
            "Organization or Project mismatch: verify the provided authorization",
            str(exception.exception),
            "Expected access-related error message"
        )


    def test_get_project_roles_edge_case_parameters(self):
        """Test edge cases for parameters."""
        # Test maximum valid values
        response = self.__get_project_roles(
            start_page=999999,
            page_size=100
        )
        self.assertIsInstance(response, dict)