import pandas as pd


class KawaReporting:

    def generate_computation_views_report(self, from_ms=None, to_ms=None, principal_id=None, entity_type=None):
        """
        Returns a DataFrame of computation history records enriched with user, layout,
        dashboard and application information.

        :param from_ms: Optional start time filter in epoch milliseconds.
        :param to_ms: Optional end time filter in epoch milliseconds.
        :param principal_id: Optional user ID to filter.
        :param entity_type: Optional filter for the kind of layout computed.
                            One of 'layout', 'dashboard', or 'application'.
        """
        kawa = self._k
        params = {}
        if from_ms is not None:
            params['from'] = from_ms
        if to_ms is not None:
            params['to'] = to_ms
        if principal_id is not None:
            params['principalId'] = principal_id

        events = kawa.get(f'{kawa.kawa_api_url}/backoffice/computation-history', params=params) or []

        principals_by_id = {
            principal.get('id'): principal
            for principal in kawa.entities.principals().list_entities()
        }
        layouts_by_id = {
            layout.get('id'): layout
            for layout in kawa.entities.layouts().list_entities()
        }
        dashboards_by_id = {
            dashboard.get('id'): dashboard
            for dashboard in kawa.entities.dashboards().list_entities()
        }
        applications_by_id = {
            application.get('id'): application
            for application in kawa.entities.applications().list_entities()
        }

        rows = []
        for event in events:
            user_id = event.get('principalId')
            layout_id = event.get('layoutId')
            principal = principals_by_id.get(user_id, {})
            layout = layouts_by_id.get(layout_id, {})
            dashboard_id = layout.get('dashboardId')
            application_id = layout.get('applicationId')
            dashboard = dashboards_by_id.get(dashboard_id, {}) if dashboard_id else {}
            application = applications_by_id.get(application_id, {}) if application_id else {}

            if entity_type == 'dashboard' and not dashboard_id:
                continue
            if entity_type == 'application' and not application_id:
                continue
            if entity_type == 'layout' and (dashboard_id or application_id):
                continue

            rows.append({
                'user_id': user_id,
                'user_email': principal.get('email', user_id),
                'user_display_information': principal.get('displayInformation'),
                'layout_id': layout_id,
                'layout_display_information': layout.get('displayInformation'),
                'dashboard_id': dashboard_id,
                'dashboard_display_information': dashboard.get('displayInformation') if dashboard_id else None,
                'application_id': application_id,
                'application_display_information': application.get('displayInformation') if application_id else None,
                'timestamp': event.get('timestamp'),
            })

        return pd.DataFrame(rows)

    def __init__(self, kawa_client):
        self._k = kawa_client

    def generate_user_list_report(self):
        kawa = self._k
        principals = kawa.entities.principals().list_entities()
        principal_descriptions = []
        for principal in principals:
            principal_descriptions.append({
                'internal_id': principal.get('id'),
                'email': principal.get('email'),
                'name': principal.get('displayInformation').get('displayName'),
                'unique_id': principal.get('uniqueId'),
                'forbidden_data_source_types': principal.get('forbiddenDataSourceTypes'),
                'is_admin': principal.get('role') != 'READER_ROLE',
                'status': principal.get('status'),
                'global_permissions': principal.get('permissions'),
            })

        return pd.DataFrame(principal_descriptions)

    def generate_workspace_member_list_report(self):
        kawa = self._k
        workspaces = kawa.entities.workspaces().list_entities()
        principals = kawa.entities.principals().list_entities()

        principal_descriptions = {}
        for principal in principals:
            principal_description = {
                'internal_id': principal.get('id'),
                'email': principal.get('email'),
                'name': principal.get('displayInformation').get('displayName'),
                'unique_id': principal.get('uniqueId'),
                'forbidden_data_source_types': principal.get('forbiddenDataSourceTypes'),
                'is_admin': principal.get('role') != 'READER_ROLE',
                'status': principal.get('status'),
                'global_permissions': principal.get('permissions'),
            }
            principal_descriptions[principal.get('id')] = principal_description

        members = []
        for workspace in workspaces:
            for member in workspace.get('members'):
                member_id = member.get('principalId')

                member_description = principal_descriptions.get(member_id)
                if not member_description:
                    print(f'One member does not correspond to a registered user: {member_id}')
                else:
                    is_admin = member_description.get('is_admin', False)
                    members.append({
                        'workspace': workspace.get('displayInformation').get('displayName'),
                        'name': member_description.get('name'),
                        'email': member_description.get('email'),
                        'unique_id': member_description.get('unique_id'),
                        'is_admin': member_description.get('is_admin'),
                        'can_see_all_data': is_admin or 'MANAGE_DATA_SOURCES' in member.get('permissions'),
                        'can_manage_security': is_admin or 'ROW_LEVEL_SECURITY_POLICY' in member.get('permissions'),
                        'can_manage_users': is_admin or 'MANAGE_USERS' in member.get('permissions'),
                        'workspace_permissions': ','.join(member.get('permissions')),
                    })

        return pd.DataFrame(members)

    def generate_team_member_list_report(self):
        kawa = self._k
        teams = kawa.entities.teams().list_entities()
        principals = kawa.entities.principals().list_entities()

        principals_by_id = {}
        for principal in principals:
            principals_by_id[principal.get('id')] = {
                'id': principal.get('id'),
                'email': principal.get('email'),
                'name': principal.get('displayInformation', {}).get('displayName'),
                'unique_id': principal.get('uniqueId'),
                'status': principal.get('status'),
                'role': principal.get('role'),
            }

        teams_by_id = {t.get('id'): t.get('displayInformation', {}).get('displayName') for t in teams}

        result = []
        for team in teams:
            admins = set(team.get('admins', []))

            members = []
            for member_id in team.get('members', []):
                principal = principals_by_id.get(member_id)
                if not principal:
                    print(f'Team "{team.get("displayInformation", {}).get("displayName")}": '
                          f'member {member_id} does not correspond to a registered user')
                    continue
                members.append({
                    'id': principal.get('id'),
                    'email': principal.get('email'),
                    'name': principal.get('name'),
                    'unique_id': principal.get('unique_id'),
                    'status': principal.get('status'),
                    'role': principal.get('role'),
                    'is_team_admin': member_id in admins,
                })

            member_teams = []
            for mt_id in team.get('memberTeams', []):
                member_teams.append({
                    'id': mt_id,
                    'name': teams_by_id.get(mt_id, mt_id),
                })

            result.append({
                'id': team.get('id'),
                'name': team.get('displayInformation', {}).get('displayName'),
                'description': team.get('displayInformation', {}).get('description', ''),
                'team_type': team.get('teamType'),
                'is_everyone': team.get('isEveryone', False),
                'security_alias': team.get('securityAlias', ''),
                'members': members,
                'member_teams': member_teams,
            })

        return result
