"""Google Sheets transport layer implementation"""

from typing import Any, Dict, List, Optional
import json
import pickle
from datetime import datetime

from googleapiclient.discovery import build
from ..transport_base import BaseTransportLayer
from ...environment import Environment


class GSheetsTransport(BaseTransportLayer):
    """Google Sheets API transport layer"""
    
    # STATIC Attributes
    is_keystore = False  # Sheets not ideal for storing keys
    is_notification_layer = False  # Users don't check sheets regularly
    is_html_compatible = False  # Sheets format, not HTML
    is_reply_compatible = False  # No native reply mechanism
    guest_submit = False  # Requires authentication to write
    guest_read_file = True  # Can make sheets public
    guest_read_folder = False  # N/A for sheets
    
    # Syft spreadsheet name
    SYFT_SHEET_PREFIX = "SyftClient_"
    
    def __init__(self, email: str):
        """Initialize Sheets transport"""
        super().__init__(email)
        self.sheets_service = None
        self.drive_service = None
        self.credentials = None
        self._setup_verified = False
        
    @property
    def api_is_active_by_default(self) -> bool:
        """Sheets API requires manual activation"""
        return False
        
    @property
    def login_complexity(self) -> int:
        """Sheets requires same auth as GDrive"""
        if self.is_setup():
            return 0
        if self._cached_credentials:
            return 0  # Already logged in
            
        if self.environment == Environment.COLAB:
            return 1  # Can reuse GDrive auth in Colab
        else:
            return 2  # OAuth2 flow required
    
    def setup(self, credentials: Optional[Dict[str, Any]] = None) -> bool:
        """Setup Sheets transport with OAuth2 credentials or Colab auth"""
        # If already setup and verified, skip
        if self._setup_verified:
            return True
            
        try:
            # Check if we're in Colab and can use automatic auth
            if self.environment == Environment.COLAB:
                try:
                    from google.colab import auth as colab_auth
                    colab_auth.authenticate_user()
                    # Build services without explicit credentials in Colab
                    self.sheets_service = build('sheets', 'v4')
                    self.drive_service = build('drive', 'v3')
                    self.credentials = None  # No explicit credentials in Colab
                except ImportError:
                    # Fallback to regular credentials if Colab auth not available
                    if credentials is None:
                        return False
                    if not credentials or 'credentials' not in credentials:
                        return False
                    self.credentials = credentials['credentials']
                    self.sheets_service = build('sheets', 'v4', credentials=self.credentials)
                    self.drive_service = build('drive', 'v3', credentials=self.credentials)
            else:
                # Regular OAuth2 flow
                if credentials is None:
                    return False
                if not credentials or 'credentials' not in credentials:
                    return False
                self.credentials = credentials['credentials']
                self.sheets_service = build('sheets', 'v4', credentials=self.credentials)
                self.drive_service = build('drive', 'v3', credentials=self.credentials)
            
            # Mark as setup verified
            self._setup_verified = True
            
            return True
        except Exception as e:
            print(f"[DEBUG] GSheets setup error: {e}")
            import traceback
            traceback.print_exc()
            return False
    
    def is_setup(self) -> bool:
        """Check if Sheets transport is ready"""
        # First check if we're cached as setup
        if self.is_cached_as_setup():
            return True
            
        # In Colab, we can always set up on demand
        if self.environment == Environment.COLAB:
            try:
                from google.colab import auth as colab_auth
                return True  # Can authenticate on demand
            except ImportError:
                pass
            
        # Otherwise check normal setup
        return self.sheets_service is not None and self.drive_service is not None
    
    def send(self, recipient: str, data: Any, subject: str = "Syft Data") -> bool:
        """Write data to a Google Sheet and share"""
        if not self.sheets_service or not self.drive_service:
            return False
            
        try:
            # Create spreadsheet
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            spreadsheet_name = f"{self.SYFT_SHEET_PREFIX}{subject.replace(' ', '_')}_{timestamp}"
            
            spreadsheet = {
                'properties': {
                    'title': spreadsheet_name
                }
            }
            
            spreadsheet = self.sheets_service.spreadsheets().create(
                body=spreadsheet, fields='spreadsheetId'
            ).execute()
            
            spreadsheet_id = spreadsheet.get('spreadsheetId')
            
            # Prepare data for sheets
            values = []
            
            if isinstance(data, str):
                # Split string into lines
                values = [[line] for line in data.split('\n')]
            elif isinstance(data, dict):
                # Convert dict to key-value pairs
                values = [['Key', 'Value']]
                for key, value in data.items():
                    values.append([str(key), str(value)])
            elif isinstance(data, list):
                # Handle list of dicts (common for tabular data)
                if data and isinstance(data[0], dict):
                    # Use dict keys as headers
                    headers = list(data[0].keys())
                    values = [headers]
                    for row in data:
                        values.append([str(row.get(h, '')) for h in headers])
                else:
                    # Simple list
                    values = [[str(item)] for item in data]
            else:
                # For complex types, pickle and store as base64
                pickled = pickle.dumps(data)
                import base64
                b64_data = base64.b64encode(pickled).decode('utf-8')
                values = [
                    ['Type', 'Pickled Data'],
                    [str(type(data).__name__), b64_data]
                ]
            
            # Write data to sheet
            body = {'values': values}
            self.sheets_service.spreadsheets().values().update(
                spreadsheetId=spreadsheet_id,
                range='A1',
                valueInputOption='RAW',
                body=body
            ).execute()
            
            # Share with recipient
            if recipient and '@' in recipient:
                permission = {
                    'type': 'user',
                    'role': 'reader',
                    'emailAddress': recipient
                }
                
                self.drive_service.permissions().create(
                    fileId=spreadsheet_id,
                    body=permission,
                    sendNotificationEmail=True
                ).execute()
            
            return True
            
        except Exception as e:
            print(f"Error creating sheet: {e}")
            return False
    
    def receive(self, limit: int = 10) -> List[Dict[str, Any]]:
        """Read data from shared Google Sheets"""
        if not self.sheets_service or not self.drive_service:
            return []
            
        messages = []
        
        try:
            # Find sheets shared with me
            query = "mimeType='application/vnd.google-apps.spreadsheet' and sharedWithMe=true and trashed=false"
            
            results = self.drive_service.files().list(
                q=query,
                pageSize=limit,
                fields="files(id, name, createdTime, owners)",
                orderBy="createdTime desc"
            ).execute()
            
            files = results.get('files', [])
            
            for file in files:
                # Check if it's a Syft sheet
                is_syft = file['name'].startswith(self.SYFT_SHEET_PREFIX)
                
                message = {
                    'id': file['id'],
                    'name': file['name'],
                    'from': file['owners'][0]['emailAddress'] if file.get('owners') else 'Unknown',
                    'date': file['createdTime'],
                    'is_syft': is_syft,
                    'data': None
                }
                
                # Read data from Syft sheets
                if is_syft:
                    try:
                        result = self.sheets_service.spreadsheets().values().get(
                            spreadsheetId=file['id'],
                            range='A:Z'  # Get all columns
                        ).execute()
                        
                        values = result.get('values', [])
                        
                        # Try to reconstruct original data format
                        if values:
                            if len(values[0]) == 2 and values[0] == ['Type', 'Pickled Data']:
                                # Pickled data
                                if len(values) > 1:
                                    import base64
                                    b64_data = values[1][1]
                                    pickled = base64.b64decode(b64_data)
                                    message['data'] = pickle.loads(pickled)
                            elif len(values) > 1 and len(values[0]) > 1:
                                # Tabular data - convert back to list of dicts
                                headers = values[0]
                                data = []
                                for row in values[1:]:
                                    row_dict = {}
                                    for i, header in enumerate(headers):
                                        row_dict[header] = row[i] if i < len(row) else ''
                                    data.append(row_dict)
                                message['data'] = data
                            else:
                                # Raw values
                                message['data'] = values
                    except:
                        pass
                
                messages.append(message)
                
        except Exception as e:
            print(f"Error retrieving sheets: {e}")
            
        return messages
    
    def create_public_sheet(self, sheet_name: str, data: List[List[str]]) -> Optional[str]:
        """Create a publicly accessible sheet and return its URL"""
        if not self.sheets_service or not self.drive_service:
            return None
            
        try:
            # Create spreadsheet
            spreadsheet = {
                'properties': {'title': sheet_name}
            }
            
            spreadsheet = self.sheets_service.spreadsheets().create(
                body=spreadsheet, fields='spreadsheetId,spreadsheetUrl'
            ).execute()
            
            spreadsheet_id = spreadsheet.get('spreadsheetId')
            
            # Write data
            if data:
                body = {'values': data}
                self.sheets_service.spreadsheets().values().update(
                    spreadsheetId=spreadsheet_id,
                    range='A1',
                    valueInputOption='RAW',
                    body=body
                ).execute()
            
            # Make public
            permission = {
                'type': 'anyone',
                'role': 'reader'
            }
            
            self.drive_service.permissions().create(
                fileId=spreadsheet_id,
                body=permission
            ).execute()
            
            return spreadsheet.get('spreadsheetUrl')
            
        except:
            return None