"""
File Storage Service - Abstract storage backend
Supports local filesystem (dev) and Cloudinary (production) with easy switching
"""
import os
import uuid
from pathlib import Path
from werkzeug.utils import secure_filename
from flask import current_app, url_for

from ..extensions import db
from ..models.file_upload import FileUpload, StorageBackend, FileCategory


class StorageService:
    """
    Universal storage service that abstracts storage backend.
    Switch between local/cloudinary by changing STORAGE_BACKEND env var.
    """
    
    def __init__(self):
        self.backend = os.getenv('STORAGE_BACKEND', 'local').lower()
        self._dirs_ensured = False
    
    def _ensure_upload_dirs(self):
        """Ensure upload directories exist for local storage (lazy initialization)"""
        if self._dirs_ensured:
            return
            
        if self.backend == 'local':
            upload_folder = current_app.config.get('UPLOAD_FOLDER')
            if upload_folder:
                # Create main upload folder
                Path(upload_folder).mkdir(parents=True, exist_ok=True)
                
                # Create category subfolders
                for category in FileCategory:
                    category_path = Path(upload_folder) / category.value
                    category_path.mkdir(exist_ok=True)
                
                self._dirs_ensured = True
    
    def upload_file(self, file, user_id, category=FileCategory.OTHER, 
                    message_id=None, job_id=None, contract_id=None):
        """Upload file to configured backend"""
        # Ensure directories exist (lazy initialization)
        self._ensure_upload_dirs()
        
        # Validate file
        if not file or not file.filename:
            raise ValueError("No file provided")
        
        # Validate file size
        file.seek(0, 2)  # Seek to end
        file_size = file.tell()
        file.seek(0)  # Reset to beginning
        
        max_size = current_app.config.get('MAX_CONTENT_LENGTH', 16 * 1024 * 1024)
        if file_size > max_size:
            raise ValueError(f"File too large. Max size: {max_size / (1024*1024)}MB")
        
        # Validate file type
        if not self._allowed_file(file.filename):
            raise ValueError(f"File type not allowed. Allowed: {current_app.config.get('ALLOWED_EXTENSIONS')}")
        
        # Generate unique filename
        original_filename = secure_filename(file.filename)
        unique_filename = self._generate_unique_filename(original_filename)
        
        # Upload based on backend
        if self.backend == 'local':
            return self._upload_local(file, unique_filename, original_filename, user_id, 
                                     category, file_size, message_id, job_id, contract_id)
        elif self.backend == 'cloudinary':
            return self._upload_cloudinary(file, unique_filename, original_filename, user_id, 
                                          category, file_size, message_id, job_id, contract_id)
        else:
            raise ValueError(f"Unsupported storage backend: {self.backend}")
    
    def _upload_local(self, file, filename, original_filename, user_id, category, 
                     file_size, message_id, job_id, contract_id):
        """Upload file to local filesystem"""
        upload_folder = current_app.config.get('UPLOAD_FOLDER')
        category_folder = Path(upload_folder) / category.value
        
        # Save file
        file_path = category_folder / filename
        file.save(str(file_path))
        
        # Generate URL (relative path for local dev)
        public_url = url_for('files.serve_file', category=category.value, filename=filename, _external=True)
        
        # Get MIME type
        file_type = file.content_type or self._guess_mime_type(filename)
        
        # Create database record
        file_record = FileUpload(
            user_id=user_id,
            filename=filename,
            original_filename=original_filename,
            file_type=file_type,
            file_size=file_size,
            storage_backend=StorageBackend.LOCAL,
            storage_path=str(file_path),
            public_url=public_url,
            category=category,
            message_id=message_id,
            job_id=job_id,
            contract_id=contract_id
        )
        
        # Get image dimensions if it's an image
        if file_type and file_type.startswith('image/'):
            try:
                from PIL import Image
                img = Image.open(file_path)
                file_record.width = img.width
                file_record.height = img.height
            except Exception:
                pass  # Skip if PIL not available or image corrupted
        
        db.session.add(file_record)
        db.session.commit()
        
        return file_record
    
    def _upload_cloudinary(self, file, filename, original_filename, user_id, category, 
                          file_size, message_id, job_id, contract_id):
        """Upload file to Cloudinary"""
        try:
            import cloudinary
            import cloudinary.uploader
            
            # Configure Cloudinary
            cloudinary.config(
                cloud_name=os.getenv('CLOUDINARY_CLOUD_NAME'),
                api_key=os.getenv('CLOUDINARY_API_KEY'),
                api_secret=os.getenv('CLOUDINARY_API_SECRET')
            )
            
            # Upload to Cloudinary with folder organization
            folder = f"rateright/{category.value}"
            upload_result = cloudinary.uploader.upload(
                file,
                folder=folder,
                public_id=filename.rsplit('.', 1)[0],  # Remove extension
                resource_type='auto',
                use_filename=True,
                unique_filename=True
            )
            
            # Create database record
            file_record = FileUpload(
                user_id=user_id,
                filename=filename,
                original_filename=original_filename,
                file_type=upload_result.get('resource_type', 'raw'),
                file_size=file_size,
                storage_backend=StorageBackend.CLOUDINARY,
                storage_path=upload_result['public_id'],
                public_url=upload_result['secure_url'],
                category=category,
                message_id=message_id,
                job_id=job_id,
                contract_id=contract_id,
                width=upload_result.get('width'),
                height=upload_result.get('height'),
                cloud_metadata={
                    'cloudinary_public_id': upload_result['public_id'],
                    'cloudinary_version': upload_result.get('version'),
                    'cloudinary_format': upload_result.get('format'),
                    'cloudinary_url': upload_result['url']
                }
            )
            
            db.session.add(file_record)
            db.session.commit()
            
            return file_record
            
        except ImportError:
            raise RuntimeError("Cloudinary not installed. Run: pip install cloudinary")
        except Exception as e:
            raise RuntimeError(f"Cloudinary upload failed: {str(e)}")
    
    def delete_file(self, file_id):
        """Delete a file from storage and database"""
        file_record = FileUpload.query.get(file_id)
        if not file_record:
            raise ValueError("File not found")
        
        # Delete from storage backend
        if file_record.storage_backend == StorageBackend.LOCAL:
            self._delete_local(file_record)
        elif file_record.storage_backend == StorageBackend.CLOUDINARY:
            self._delete_cloudinary(file_record)
        
        # Soft delete in database
        file_record.soft_delete()
        
        return True
    
    def _delete_local(self, file_record):
        """Delete file from local filesystem"""
        try:
            if file_record.storage_path and os.path.exists(file_record.storage_path):
                os.remove(file_record.storage_path)
        except Exception as e:
            current_app.logger.error(f"Failed to delete local file: {e}")
    
    def _delete_cloudinary(self, file_record):
        """Delete file from Cloudinary"""
        try:
            import cloudinary
            import cloudinary.uploader
            
            public_id = file_record.storage_path
            if public_id:
                cloudinary.uploader.destroy(public_id)
        except Exception as e:
            current_app.logger.error(f"Failed to delete Cloudinary file: {e}")
    
    def get_file_url(self, file_id, transformations=None):
        """
        Get URL for a file, optionally with transformations (Cloudinary only)
        
        Args:
            file_id: FileUpload ID
            transformations: Dict of Cloudinary transformations (e.g., {'width': 300, 'crop': 'fill'})
        
        Returns:
            URL string
        """
        file_record = FileUpload.query.get(file_id)
        if not file_record:
            raise ValueError("File not found")
        
        if file_record.storage_backend == StorageBackend.CLOUDINARY and transformations:
            try:
                import cloudinary
                return cloudinary.CloudinaryImage(file_record.storage_path).build_url(**transformations)
            except ImportError:
                pass  # Fall through to return original URL
        
        return file_record.public_url
    
    def _allowed_file(self, filename):
        """Check if file extension is allowed"""
        allowed = current_app.config.get('ALLOWED_EXTENSIONS', set())
        return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed
    
    def _generate_unique_filename(self, original_filename):
        """Generate unique filename to prevent collisions"""
        ext = original_filename.rsplit('.', 1)[1].lower() if '.' in original_filename else ''
        unique_id = str(uuid.uuid4())
        return f"{unique_id}.{ext}" if ext else unique_id
    
    def _guess_mime_type(self, filename):
        """Guess MIME type from filename"""
        import mimetypes
        mime_type, _ = mimetypes.guess_type(filename)
        return mime_type or 'application/octet-stream'


# Global instance
storage_service = StorageService()
