"""
S3 folder management for org provider paths.

Manages S3 folder operations specifically for org provider paths in the
piedpiper-custom-billing structure, supporting connection recreation workflows.
"""

import boto3
from typing import Dict, List, Tuple, Any, Optional
from dataclasses import dataclass
from datetime import datetime

from src.services.environment_service import EnvironmentService
from src.log_context import get_logger


@dataclass
class S3ObjectInfo:
    """Information about an S3 object."""
    key: str
    size: int
    last_modified: datetime
    storage_class: str = "STANDARD"


@dataclass
class FolderDeletionResult:
    """Result of folder deletion operation."""
    org: str
    provider: str
    prefix: str
    objects_deleted: int
    total_size: int
    duration_seconds: float
    success: bool
    error_message: Optional[str] = None


class S3FolderManager:
    """
    Manages S3 folder operations for org provider paths.
    
    Specifically handles operations on piedpiper-custom-billing/{org}/{provider}/
    paths for connection recreation workflows.
    """
    
    def __init__(self, bucket: str, region: str = "us-east-1"):
        """
        Initialize S3 folder manager.
        
        Args:
            bucket: S3 bucket name
            region: AWS region
        """
        self.bucket = bucket
        self.region = region
        self.logger = get_logger(__name__)
        
        # Initialize S3 client
        env_service = EnvironmentService()
        env_service.validate_aws_credentials_and_access()
        
        self.s3_client = boto3.client('s3', region_name=region)
    
    def list_org_provider_objects(self, org: str, provider: str) -> List[S3ObjectInfo]:
        """
        List all objects in piedpiper-custom-billing/{org}/{provider}/.
        
        Args:
            org: Organization name
            provider: Provider name (AWS, Azure, GCP, etc.)
            
        Returns:
            List of S3ObjectInfo for all objects in the org provider folder
        """
        prefix = f"piedpiper-custom-billing/{org}/{provider}/"
        objects = []
        
        try:
            paginator = self.s3_client.get_paginator('list_objects_v2')
            pages = paginator.paginate(Bucket=self.bucket, Prefix=prefix)
            
            for page in pages:
                if 'Contents' in page:
                    for obj in page['Contents']:
                        objects.append(S3ObjectInfo(
                            key=obj['Key'],
                            size=obj['Size'],
                            last_modified=obj['LastModified'],
                            storage_class=obj.get('StorageClass', 'STANDARD')
                        ))
            
            self.logger.info(f"Listed {len(objects)} objects in {prefix}")
            return objects
            
        except Exception as e:
            self.logger.error(f"Failed to list objects in {prefix}: {e}")
            raise
    
    def estimate_deletion_impact(self, org_providers: List[Tuple[str, str]]) -> Dict[str, Any]:
        """
        Estimate total deletion impact across org/provider combinations.
        
        Args:
            org_providers: List of (org, provider) tuples
            
        Returns:
            Dictionary with impact summary
        """
        total_objects = 0
        total_size = 0
        folder_details = []
        
        for org, provider in org_providers:
            try:
                objects = self.list_org_provider_objects(org, provider)
                folder_size = sum(obj.size for obj in objects)
                
                folder_details.append({
                    "org": org,
                    "provider": provider,
                    "prefix": f"piedpiper-custom-billing/{org}/{provider}/",
                    "object_count": len(objects),
                    "total_size": folder_size,
                    "size_mb": round(folder_size / (1024 * 1024), 2),
                    "size_gb": round(folder_size / (1024 * 1024 * 1024), 2)
                })
                
                total_objects += len(objects)
                total_size += folder_size
                
            except Exception as e:
                self.logger.warning(f"Failed to estimate impact for {org}/{provider}: {e}")
                folder_details.append({
                    "org": org,
                    "provider": provider,
                    "prefix": f"piedpiper-custom-billing/{org}/{provider}/",
                    "object_count": 0,
                    "total_size": 0,
                    "size_mb": 0,
                    "size_gb": 0,
                    "error": str(e)
                })
        
        # Estimate deletion time (rough estimate: 100 objects per second)
        estimated_seconds = max(total_objects / 100, 5)  # Minimum 5 seconds
        estimated_minutes = round(estimated_seconds / 60, 1)
        
        return {
            "total_objects": total_objects,
            "total_size": total_size,
            "total_size_mb": round(total_size / (1024 * 1024), 2),
            "total_size_gb": round(total_size / (1024 * 1024 * 1024), 2),
            "estimated_seconds": estimated_seconds,
            "estimated_minutes": estimated_minutes,
            "folder_details": folder_details
        }
    
    def delete_org_provider_folder(self, org: str, provider: str, dry_run: bool = False) -> FolderDeletionResult:
        """
        Delete all objects in org provider folder.
        
        Args:
            org: Organization name
            provider: Provider name
            dry_run: If True, don't actually delete, just return what would be deleted
            
        Returns:
            FolderDeletionResult with deletion summary
        """
        start_time = datetime.now()
        prefix = f"piedpiper-custom-billing/{org}/{provider}/"
        
        try:
            # List all objects to delete
            objects = self.list_org_provider_objects(org, provider)
            
            if not objects:
                return FolderDeletionResult(
                    org=org,
                    provider=provider,
                    prefix=prefix,
                    objects_deleted=0,
                    total_size=0,
                    duration_seconds=0,
                    success=True
                )
            
            total_size = sum(obj.size for obj in objects)
            
            if dry_run:
                self.logger.info(f"DRY RUN: Would delete {len(objects)} objects ({total_size} bytes) from {prefix}")
                return FolderDeletionResult(
                    org=org,
                    provider=provider,
                    prefix=prefix,
                    objects_deleted=len(objects),
                    total_size=total_size,
                    duration_seconds=0,
                    success=True
                )
            
            # Perform batch deletion
            objects_deleted = 0
            
            # Process in batches of 1000 (S3 delete limit)
            batch_size = 1000
            for i in range(0, len(objects), batch_size):
                batch = objects[i:i + batch_size]
                
                # Prepare delete request
                delete_objects = {
                    'Objects': [{'Key': obj.key} for obj in batch]
                }
                
                # Execute batch delete
                response = self.s3_client.delete_objects(
                    Bucket=self.bucket,
                    Delete=delete_objects
                )
                
                # Count successful deletions
                deleted_count = len(response.get('Deleted', []))
                objects_deleted += deleted_count
                
                # Log errors if any
                if 'Errors' in response:
                    for error in response['Errors']:
                        self.logger.error(f"Failed to delete {error['Key']}: {error['Message']}")
            
            duration = (datetime.now() - start_time).total_seconds()
            
            self.logger.info(f"Deleted {objects_deleted} objects ({total_size} bytes) from {prefix} in {duration:.2f}s")
            
            return FolderDeletionResult(
                org=org,
                provider=provider,
                prefix=prefix,
                objects_deleted=objects_deleted,
                total_size=total_size,
                duration_seconds=duration,
                success=True
            )
            
        except Exception as e:
            duration = (datetime.now() - start_time).total_seconds()
            self.logger.error(f"Failed to delete objects from {prefix}: {e}")
            
            return FolderDeletionResult(
                org=org,
                provider=provider,
                prefix=prefix,
                objects_deleted=0,
                total_size=0,
                duration_seconds=duration,
                success=False,
                error_message=str(e)
            )
    
    def format_size(self, size_bytes: int) -> str:
        """Format size in bytes to human readable string."""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes / 1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes / (1024 * 1024):.1f} MB"
        else:
            return f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"