import os
import io
import time
from googleapiclient.errors import HttpError
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload, MediaIoBaseUpload

# =========================================================
# --- REDUNDANCY SHIELD (DECORATOR) ---
# =========================================================
def with_redundancy(max_retries=3):
    """
    A master shield: Automatically retries Google Drive API calls if they fail due to 
    network glitches or Google Rate Limits (403 errors).
    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            retries = 0
            while retries < max_retries:
                try:
                    return func(*args, **kwargs)
                except HttpError as error:
                    # If it's a Rate Limit (403) or Server Error (5xx), wait and retry
                    if error.resp.status in [403, 500, 502, 503, 504]:
                        wait_time = (2 ** retries)  # Exponential backoff: waits 1s, 2s, 4s...
                        print(f"[Janu VFS] Network traffic heavy. Retrying in {wait_time}s...")
                        time.sleep(wait_time)
                        retries += 1
                    else:
                        # If it's a different error (like 404 Not Found), don't retry, just report it.
                        print(f"[Janu VFS API Error] {error.reason}")
                        return None
                except Exception as e:
                    print(f"[Janu VFS Critical Error] {str(e)}")
                    return None
            print(f"[Janu VFS] Failed after {max_retries} attempts.")
            return None
        return wrapper
    return decorator


# =========================================================
# --- JANU VIRTUAL FILE SYSTEM CORE ---
# =========================================================
class JanuDrive:
    """
    Virtual File System (VFS)
    A plug-and-play library to treat Google Drive like a local Linux hard drive.
    """
    SCOPES = ['https://www.googleapis.com/auth/drive']

    def __init__(self, master_id, token_path='token.json', creds_path='credentials.json'):
        self.master_id = master_id
        self.token_path = token_path
        self.creds_path = creds_path
        
        # 1. Auto-Authenticate
        self.service = self._authenticate()
        
        # 2. Smart Cache (Starts by knowing the root path)
        self.cache = {"/": self.master_id, "": self.master_id}
        print(f"[Janu VFS] Successfully connected to Master Drive!")

    def _authenticate(self):
        """Silently logs in, or generates a new token with beautiful error handling."""
        creds = None
        if os.path.exists(self.token_path):
            creds = Credentials.from_authorized_user_file(self.token_path, self.SCOPES)
            
        if not creds or not creds.valid:
            # 1. THE MISSING CREDENTIALS GUIDE
            if not os.path.exists(self.creds_path):
                print("\n" + "="*65)
                print(" SETUP REQUIRED: MISSING GOOGLE CREDENTIALS ")
                print("="*65)
                print(f" VFS could not find the '{self.creds_path}' file.")
                print("\nTo connect your app to Google Drive, follow these exact steps:")
                print("  1. Go to Google Cloud Console: https://console.cloud.google.com")
                print("  2. Create a new Project (e.g., 'My Project')")
                print("  3. Go to 'APIs & Services' -> 'Library' and enable 'Google Drive API'")
                print("  4. Go to 'Credentials' -> 'Create Credentials' -> 'OAuth client ID'")
                print("  5. Choose 'Desktop App', click Create, and DOWNLOAD the JSON file.")
                print(f"  6. Rename that downloaded file to '{self.creds_path}'")
                print("  7. Place it in this exact folder and run your code again.")
                print("="*65 + "\n")
                exit(1) 
                
            # 2. THE TOKEN GENERATION EXPLANATION
            print("\n" + "*"*65)
            print(" FIRST TIME SETUP: GENERATING ACCESS TOKEN ")
            print("*"*65)
            print("[VFS] open your web browser right now! with the below link .")
            print("[VFS] Please login with your Google Account and click 'Continue'.")
            print("[VFS] This securely creates your 'token.json' file so you don't")
            print("[VFS] have to login every after 7 days as  you run your code.")
            print("*"*65 + "\n")
            
            flow = InstalledAppFlow.from_client_secrets_file(self.creds_path, self.SCOPES)
            creds = flow.run_local_server(port=0)
            
            with open(self.token_path, 'w') as token:
                token.write(creds.to_json())
                print(f"\n[VFS] Success! Your access token is securely saved as '{self.token_path}'!")
                
        return build('drive', 'v3', credentials=creds)

    # =========================================================
    # --- THE PATH ENGINE (The Brain) ---
    # =========================================================
    @with_redundancy(max_retries=3)
    def _resolve_parent(self, cloud_path):
        """Translates a human path ('/Desktop/Code/test.py') into the parent folder's Google Drive ID."""
        parts = [p for p in cloud_path.split('/') if p]
        if not parts: return self.master_id
        
        parent_parts = parts[:-1] 
        current_id = self.master_id
        current_path = ""
        
        for part in parent_parts:
            current_path += "/" + part
            if current_path in self.cache:
                current_id = self.cache[current_path]
                continue
                
            query = f"name = '{part}' and '{current_id}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false"
            results = self.service.files().list(q=query, fields="files(id)").execute().get('files', [])
            
            if results:
                current_id = results[0]['id']
                self.cache[current_path] = current_id
            else:
                meta = {'name': part, 'mimeType': 'application/vnd.google-apps.folder', 'parents': [current_id]}
                folder = self.service.files().create(body=meta, fields='id').execute()
                current_id = folder.get('id')
                self.cache[current_path] = current_id
                
        return current_id

    @with_redundancy(max_retries=3)
    def _get_item_id(self, cloud_path):
        """Gets the exact Drive ID of a specific file or folder."""
        filename = os.path.basename(cloud_path)
        parent_id = self._resolve_parent(cloud_path)
        if not parent_id: return None
        
        query = f"name = '{filename}' and '{parent_id}' in parents and trashed = false"
        results = self.service.files().list(q=query, fields="files(id, mimeType)").execute().get('files', [])
        return results[0] if results else None

    # =========================================================
    # --- CORE USER FEATURES ---
    # =========================================================
    @with_redundancy(max_retries=3)
    def create_folder(self, cloud_path):
        """Creates an empty folder (e.g., '/Desktop/NewApp')."""
        self._resolve_parent(cloud_path + "/dummy_file") 
        print(f"[Janu VFS] Folder ensured: {cloud_path}")

    @with_redundancy(max_retries=3)
    def create_file(self, cloud_path):
        """Creates an empty text file in Drive."""
        self.upload_text(cloud_path, "")

    @with_redundancy(max_retries=3)
    def upload_text(self, cloud_path, text_content):
        """Uploads raw text directly into a file without needing a local hard drive file."""
        filename = os.path.basename(cloud_path)
        parent_id = self._resolve_parent(cloud_path)
        existing = self._get_item_id(cloud_path)
        
        media = MediaIoBaseUpload(io.BytesIO(text_content.encode('utf-8')), mimetype='text/plain', resumable=True)
        
        if existing:
            self.service.files().update(fileId=existing['id'], media_body=media).execute()
        else:
            meta = {'name': filename, 'parents': [parent_id]}
            self.service.files().create(body=meta, media_body=media).execute()
        print(f"[Janu VFS] Uploaded text to {cloud_path}")

    @with_redundancy(max_retries=3)
    def upload(self, local_file_path, cloud_path):
        """Uploads OR Updates a local file to Drive to prevent duplicates."""
        filename = os.path.basename(cloud_path)
        parent_id = self._resolve_parent(cloud_path)
        existing = self._get_item_id(cloud_path)
        
        media = MediaFileUpload(local_file_path, resumable=True)
        
        if existing:
            self.service.files().update(fileId=existing['id'], media_body=media).execute()
            print(f"[Janu VFS] Updated existing file: {cloud_path}")
        else:
            meta = {'name': filename, 'parents': [parent_id]}
            self.service.files().create(body=meta, media_body=media, fields='id').execute()
            print(f"[Janu VFS] Uploaded new file: {cloud_path}")

    @with_redundancy(max_retries=3)
    def retrieve(self, cloud_path, local_save_directory):
        """Downloads a file from Drive to the local computer."""
        item = self._get_item_id(cloud_path)
        if not item:
            print(f"[Janu VFS] Error: {cloud_path} does not exist.")
            return
            
        if item['mimeType'] == 'application/vnd.google-apps.folder':
            print(f"[Janu VFS] Error: Retrieve is for files, not whole folders yet.")
            return

        request = self.service.files().get_media(fileId=item['id'])
        os.makedirs(local_save_directory, exist_ok=True)
        local_path = os.path.join(local_save_directory, os.path.basename(cloud_path))
        
        with open(local_path, 'wb') as fh:
            downloader = MediaIoBaseDownload(fh, request)
            done = False
            while not done:
                status, done = downloader.next_chunk()
        print(f"[Janu VFS] Downloaded to: {local_path}")

    @with_redundancy(max_retries=3)
    def rename(self, cloud_path, new_name):
        """Changes the name of a file or folder in Drive."""
        item = self._get_item_id(cloud_path)
        if item:
            self.service.files().update(fileId=item['id'], body={'name': new_name}).execute()
            print(f"[Janu VFS] Renamed to {new_name}")
        else:
            print(f"[Janu VFS] Could not rename. '{cloud_path}' not found.")

    @with_redundancy(max_retries=3)
    def delete(self, cloud_path):
        """Moves a file or folder to the Drive trash."""
        item = self._get_item_id(cloud_path)
        if item:
            self.service.files().delete(fileId=item['id']).execute()
            print(f"[Janu VFS] Deleted {cloud_path}")
            self.clean_cache() 
        else:
            print(f"[Janu VFS] Could not delete. '{cloud_path}' not found.")

    @with_redundancy(max_retries=3)
    def list_dir(self, cloud_path="/"):
        """Returns a list of all files and folders inside a specific Drive path."""
        if cloud_path in ["/", ""]:
            folder_id = self.master_id
        else:
            item = self._get_item_id(cloud_path)
            if not item or 'folder' not in item['mimeType']:
                print(f"[Janu VFS] Error: '{cloud_path}' is not a valid folder.")
                return []
            folder_id = item['id']

        query = f"'{folder_id}' in parents and trashed = false"
        results = self.service.files().list(q=query, fields="files(id, name, mimeType)").execute().get('files', [])
        
        contents = []
        for f in results:
            f_type = 'folder' if 'folder' in f['mimeType'] else 'file'
            contents.append({'name': f['name'], 'type': f_type, 'id': f['id']})
            
        return contents

    def clean_cache(self):
        """Resets the smart memory."""
        self.cache = {"/": self.master_id, "": self.master_id}
        print("[Janu VFS] Smart cache wiped clean.")
