"""
Unified permission validation system for Slack to Google Chat migration.

This module provides comprehensive permission testing that validates all
required scopes and operations before starting migration.
"""

from __future__ import annotations

import datetime
import io
import logging
from dataclasses import dataclass
from typing import Any

from googleapiclient.errors import HttpError
from googleapiclient.http import MediaIoBaseUpload

from slack_chat_migrator.constants import HTTP_CONFLICT, SPACE_TYPE
from slack_chat_migrator.exceptions import PermissionCheckError
from slack_chat_migrator.services.chat_adapter import ChatAdapter
from slack_chat_migrator.services.drive_adapter import DriveAdapter
from slack_chat_migrator.utils.api import REQUIRED_SCOPES, get_gcp_service
from slack_chat_migrator.utils.logging import log_with_context


@dataclass
class PermissionCheckContext:
    """Lightweight context carrying only what PermissionValidator needs."""

    chat: ChatAdapter
    drive: DriveAdapter
    workspace_admin: str


class PermissionValidator:
    """
    Comprehensive permission validator for Google Chat and Drive APIs.

    This class tests all operations that the migration tool will perform,
    ensuring that all required scopes are properly configured before
    starting the actual migration process.
    """

    def __init__(self, migrator: PermissionCheckContext | Any) -> None:
        """
        Initialize the permission validator.

        Args:
            migrator: Any object exposing ``.chat``, ``.drive``, and
                ``.workspace_admin`` attributes — typically a
                ``SlackToChatMigrator`` or ``PermissionCheckContext``.
        """
        self.migrator = migrator
        self.permission_errors: list[str] = []
        self.test_resources: dict[str, Any] = {}

    def validate_all_permissions(self) -> bool:
        """
        Run comprehensive permission validation.

        Returns:
            True if all permissions are valid, False otherwise

        Raises:
            PermissionCheckError: If critical permissions are missing
        """
        log_with_context(
            logging.INFO, "🔍 Starting comprehensive permission validation..."
        )

        self.permission_errors = []
        self.test_resources = {}

        try:
            # Test each category of operations
            self._test_space_operations()
            self._test_member_operations()
            self._test_message_operations()
            self._test_drive_operations()

        except Exception as e:
            self.permission_errors.append(f"Critical validation error: {e}")
            log_with_context(logging.ERROR, f"Critical validation error: {e}")

        finally:
            # Always clean up test resources
            self._cleanup_test_resources()

        # Report results
        return self._report_results()

    def _test_space_operations(self) -> None:
        """Test all space-related operations."""
        log_with_context(logging.INFO, "Testing space operations...")

        # Test 1: Space creation (import mode)
        log_with_context(logging.INFO, "  • Testing space creation...")
        try:
            # Set space creation time to the past to allow for proper historical membership testing
            current_time = datetime.datetime.now(datetime.timezone.utc)
            space_create_time = (
                (current_time - datetime.timedelta(minutes=5))
                .isoformat()
                .replace("+00:00", "Z")
            )

            test_space = {
                "displayName": "Permission Test Space",
                "spaceType": SPACE_TYPE,
                "importMode": True,
                "createTime": space_create_time,
            }
            result = self.migrator.chat.create_space(test_space)
            space_name = result.get("name")
            self.test_resources["space"] = space_name
            self.test_resources["space_create_time"] = space_create_time
            log_with_context(logging.INFO, "    ✓ Space creation: PASSED")
        except HttpError as e:
            self.permission_errors.append(f"Space creation failed: {e}")
            log_with_context(logging.ERROR, "    ✗ Space creation: FAILED")
            return  # Can't continue without a test space

        # Test 2: Space listing
        log_with_context(logging.INFO, "  • Testing space listing...")
        try:
            self.migrator.chat.list_spaces(page_size=1)
            log_with_context(logging.INFO, "    ✓ Space listing: PASSED")
        except HttpError as e:
            self.permission_errors.append(f"Space listing failed: {e}")
            log_with_context(logging.ERROR, "    ✗ Space listing: FAILED")

        # Skip space history access test for import mode - no messages exist yet
        log_with_context(
            logging.INFO,
            "  • Skipping space history access test (no messages in import mode)",
        )
        log_with_context(
            logging.INFO,
            "    ✓ Space history access: SKIPPED (not applicable for import mode)",
        )

        # Skip space patch/update test for import mode - often restricted
        log_with_context(
            logging.INFO, "  • Skipping space update test (restricted in import mode)"
        )
        log_with_context(
            logging.INFO, "    ✓ Space update: SKIPPED (not applicable for import mode)"
        )

    def _test_member_operations(self) -> None:
        """Test member-related operations."""
        if "space" not in self.test_resources:
            log_with_context(
                logging.WARNING, "Skipping member tests - no test space available"
            )
            return

        log_with_context(logging.INFO, "Testing member operations...")

        # Test 5: Member listing (may be limited in import mode but still testable for permissions)
        log_with_context(logging.INFO, "  • Testing member listing...")
        try:
            self.migrator.chat.list_memberships(parent=self.test_resources["space"])
            log_with_context(logging.INFO, "    ✓ Member listing: PASSED")
        except HttpError as e:
            if "insufficient authentication scopes" in str(e).lower():
                self.permission_errors.append(
                    f"Member listing failed: Missing 'chat.memberships.readonly' scope. Error: {e}"
                )
                log_with_context(
                    logging.ERROR, "    ✗ Member listing: FAILED (missing scope)"
                )
            elif "import mode" in str(e).lower() or "not available" in str(e).lower():
                # Member listing may be restricted in import mode, which is expected
                log_with_context(
                    logging.INFO,
                    "    ✓ Member listing: EXPECTED (limited in import mode)",
                )
            else:
                self.permission_errors.append(f"Member listing failed: {e}")
                log_with_context(logging.ERROR, "    ✗ Member listing: FAILED")

        # Test 6: Member creation (use historical membership for import mode spaces)
        log_with_context(logging.INFO, "  • Testing member creation...")
        try:
            # For import mode spaces, we need to create historical memberships
            # Use the space's create time as reference to ensure proper timing
            current_time = datetime.datetime.now(datetime.timezone.utc)
            space_create_time = self.test_resources.get("space_create_time")

            if space_create_time:
                # Parse the space create time and create membership after it
                space_create_dt = datetime.datetime.fromisoformat(
                    space_create_time.replace("Z", "+00:00")
                )
                # Create membership 1 minute after space creation, delete 2 minutes after
                past_create_time = (
                    (space_create_dt + datetime.timedelta(minutes=1))
                    .isoformat()
                    .replace("+00:00", "Z")
                )
                past_delete_time = (
                    (space_create_dt + datetime.timedelta(minutes=2))
                    .isoformat()
                    .replace("+00:00", "Z")
                )
            else:
                # Fallback if we don't have space create time
                past_create_time = (
                    (current_time - datetime.timedelta(minutes=3))
                    .isoformat()
                    .replace("+00:00", "Z")
                )
                past_delete_time = (
                    (current_time - datetime.timedelta(minutes=2))
                    .isoformat()
                    .replace("+00:00", "Z")
                )

            member_body = {
                "member": {
                    "name": f"users/{self.migrator.workspace_admin}",
                    "type": "HUMAN",
                },
                # Add both createTime and deleteTime for historical membership
                "createTime": past_create_time,
                "deleteTime": past_delete_time,
            }
            member_result = self.migrator.chat.create_membership(
                parent=self.test_resources["space"], body=member_body
            )
            self.test_resources["member"] = member_result.get("name")
            log_with_context(
                logging.INFO, "    ✓ Member creation: PASSED (historical membership)"
            )
        except HttpError as e:
            if e.resp.status == HTTP_CONFLICT:  # Already a member
                log_with_context(
                    logging.INFO, "    ✓ Member creation: PASSED (already member)"
                )
            elif "Adding normal memberships isn't supported" in str(e):
                # This error indicates import mode is working correctly
                log_with_context(
                    logging.INFO,
                    "    ✓ Member creation: EXPECTED (import mode requires historical memberships)",
                )
            else:
                self.permission_errors.append(f"Member creation failed: {e}")
                log_with_context(logging.ERROR, "    ✗ Member creation: FAILED")

    def _test_message_operations(self) -> None:
        """Test message-related operations."""
        if "space" not in self.test_resources:
            log_with_context(
                logging.WARNING, "Skipping message tests - no test space available"
            )
            return

        log_with_context(logging.INFO, "Testing message operations...")

        # Test 7: Message creation
        log_with_context(logging.INFO, "  • Testing message creation...")
        try:
            message_body = {"text": "Permission test message - will be cleaned up"}
            message_result = self.migrator.chat.create_message(
                parent=self.test_resources["space"], body=message_body
            )
            self.test_resources["message"] = message_result.get("name")
            log_with_context(logging.INFO, "    ✓ Message creation: PASSED")
        except HttpError as e:
            self.permission_errors.append(f"Message creation failed: {e}")
            log_with_context(logging.ERROR, "    ✗ Message creation: FAILED")

        # Skip import completion test - this is tested during actual migration
        # and causes cleanup issues with test spaces
        log_with_context(
            logging.INFO,
            "  • Skipping import completion test (tested during actual migration)",
        )
        log_with_context(
            logging.INFO,
            "    ✓ Import completion: SKIPPED (not needed for permission validation)",
        )

    def _test_drive_operations(self) -> None:
        """Test Drive-related operations."""
        log_with_context(logging.INFO, "Testing Drive operations...")

        # Test 10: Drive file creation and sharing
        log_with_context(logging.INFO, "  • Testing Drive file operations...")
        try:
            # Create a test file
            file_metadata = {"name": "permission-test.txt", "parents": []}
            media_body = MediaIoBaseUpload(
                io.BytesIO(b"Permission test file content"), mimetype="text/plain"
            )
            test_file = self.migrator.drive.create_file(
                body=file_metadata, media_body=media_body
            )
            file_id: str = test_file["id"]
            self.test_resources["drive_file"] = file_id

            # Test file sharing permissions
            permission_body = {"role": "reader", "type": "anyone"}
            self.migrator.drive.create_permission(file_id=file_id, body=permission_body)

            log_with_context(logging.INFO, "    ✓ Drive operations: PASSED")

        except HttpError as e:
            self.permission_errors.append(f"Drive operations failed: {e}")
            log_with_context(logging.ERROR, f"    ✗ Drive operations: FAILED - {e}")

    def _cleanup_test_resources(self) -> None:
        """Clean up all test resources."""
        log_with_context(logging.INFO, "Cleaning up test resources...")

        # Clean up Drive file
        if "drive_file" in self.test_resources:
            try:
                self.migrator.drive.delete_file(
                    file_id=self.test_resources["drive_file"]
                )
                log_with_context(logging.DEBUG, "Cleaned up test Drive file")
            except HttpError as e:
                log_with_context(logging.WARNING, f"Failed to clean up Drive file: {e}")

        # Clean up test space - simple deletion without import completion
        if "space" in self.test_resources:
            try:
                # Try to delete the space directly
                self.migrator.chat.delete_space(name=self.test_resources["space"])
                log_with_context(logging.DEBUG, "Cleaned up test space")
            except Exception as e:
                # Import mode spaces often cannot be deleted, which is expected
                if any(
                    keyword in str(e).lower()
                    for keyword in [
                        "insufficient authentication scopes",
                        "permission denied",
                        "cannot delete",
                        "import mode",
                    ]
                ):
                    log_with_context(
                        logging.DEBUG,
                        "Test space cleanup skipped (expected for import mode)",
                    )
                else:
                    log_with_context(
                        logging.WARNING, f"Failed to clean up test space: {e}"
                    )

    def _report_results(self) -> bool:
        """Report validation results and return success status."""
        if self.permission_errors:
            log_with_context(logging.ERROR, "❌ Permission validation FAILED:")
            for error in self.permission_errors:
                log_with_context(logging.ERROR, f"  • {error}")

            log_with_context(logging.ERROR, "")
            log_with_context(
                logging.ERROR, "Required scopes for domain-wide delegation:"
            )
            for scope in REQUIRED_SCOPES:
                log_with_context(logging.ERROR, f"  • {scope}")

            log_with_context(logging.ERROR, "")
            log_with_context(
                logging.ERROR,
                "Please ensure all scopes are granted to your service account in the Google Admin Console.",
            )
            log_with_context(
                logging.ERROR, "See the setup documentation for detailed instructions."
            )

            raise PermissionCheckError(
                f"Permission validation failed with {len(self.permission_errors)} errors. Migration cannot proceed."
            )

        else:
            log_with_context(logging.INFO, "✅ All permission validations PASSED!")
            log_with_context(logging.INFO, "Migration can proceed safely.")
            return True


def validate_permissions(migrator: Any) -> bool:
    """
    Convenience function to validate all permissions.

    Args:
        migrator: The SlackToChatMigrator instance

    Returns:
        True if all permissions are valid

    Raises:
        Exception: If critical permissions are missing
    """
    # Initialize API services before validation
    migrator._initialize_api_services()

    validator = PermissionValidator(migrator)
    return validator.validate_all_permissions()


def check_permissions_standalone(
    creds_path: str,
    workspace_admin: str,
    max_retries: int = 3,
    retry_delay: int = 2,
) -> bool:
    """
    Run permission checks without creating a full SlackToChatMigrator.

    This avoids the heavy init (export parsing, user mapping, channel loading)
    that SlackToChatMigrator performs, since permission checking only needs
    API service clients.

    The caller is responsible for configuring logging (e.g. via
    ``setup_logger``) before calling this function.

    Args:
        creds_path: Path to service account credentials JSON.
        workspace_admin: Email of workspace admin to impersonate.
        max_retries: Maximum API retry attempts (default 3).
        retry_delay: Delay in seconds between retries (default 2).

    Returns:
        True if all permissions are valid.

    Raises:
        Exception: If critical permissions are missing.
    """
    log_with_context(logging.INFO, "Running standalone permission check...")

    chat = get_gcp_service(
        creds_path,
        workspace_admin,
        "chat",
        "v1",
        max_retries=max_retries,
        retry_delay=retry_delay,
    )
    drive = get_gcp_service(
        creds_path,
        workspace_admin,
        "drive",
        "v3",
        max_retries=max_retries,
        retry_delay=retry_delay,
    )

    ctx = PermissionCheckContext(
        chat=ChatAdapter(chat),
        drive=DriveAdapter(drive),
        workspace_admin=workspace_admin,
    )

    validator = PermissionValidator(ctx)
    return validator.validate_all_permissions()
