from django.conf import settings
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
import oss2
import os
from urllib.parse import urljoin
import logging

logger = logging.getLogger(__name__)

@deconstructible
class OSSStorage(Storage):
    """
    Alibaba Cloud OSS storage backend for Django
    """
    
    def __init__(self, bucket_name=None, endpoint=None, access_key_id=None, access_key_secret=None, base_dir=''):
        self.bucket_name = bucket_name or settings.AUDIO_SERVICE_CONFIG['aliyun']['oss_bucket']
        self.endpoint = endpoint or settings.AUDIO_SERVICE_CONFIG['aliyun']['oss_endpoint']
        self.access_key_id = access_key_id or settings.AUDIO_SERVICE_CONFIG['aliyun']['oss_key_id']
        self.access_key_secret = access_key_secret or settings.AUDIO_SERVICE_CONFIG['aliyun']['oss_key_secret']
        self.base_dir = base_dir.rstrip('/')
        self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)
        self.bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
        self.host = settings.AUDIO_SERVICE_CONFIG['aliyun']['oss_host']
    
    def _get_key(self, name):
        """
        Get the full path (key) in OSS
        """
        if self.base_dir:
            return f"{self.base_dir}/{name}"
        return name
    
    def _open(self, name, mode='rb'):
        """
        Open a file from OSS
        """
        logger.debug(f"Opening file {name} from OSS")
        key = self._get_key(name)
        try:
            content = self.bucket.get_object(key)
            content.name = name
            content.mode = mode
            return content
        except oss2.exceptions.NoSuchKey:
            logger.error(f"File {key} not found in OSS")
            raise FileNotFoundError(f"File {name} does not exist")
    
    def _save(self, name, content):
        """
        Save a file to OSS
        """
        logger.debug(f"Saving file {name} to OSS")
        key = self._get_key(name)
        
        # If the file has a file attribute, use that
        if hasattr(content, 'file'):
            content = content.file
        
        # Convert to bytes if necessary
        if hasattr(content, 'read'):
            content_str = content.read()
            if isinstance(content_str, str):
                content_bytes = content_str.encode('utf-8')
            else:
                content_bytes = content_str
            self.bucket.put_object(key, content_bytes)
        else:
            self.bucket.put_object(key, content)
        
        return name
    
    def exists(self, name):
        """
        Check if a file exists in OSS
        """
        key = self._get_key(name)
        try:
            self.bucket.get_object_meta(key)
            return True
        except oss2.exceptions.NoSuchKey:
            return False
    
    def delete(self, name):
        """
        Delete a file from OSS
        """
        key = self._get_key(name)
        try:
            self.bucket.delete_object(key)
        except oss2.exceptions.NoSuchKey:
            pass
    
    def url(self, name):
        """
        Get the URL of a file
        """
        key = self._get_key(name)
        return urljoin(self.host, key)
    
    def size(self, name):
        """
        Get the size of a file
        """
        key = self._get_key(name)
        try:
            obj = self.bucket.get_object_meta(key)
            return obj.content_length
        except oss2.exceptions.NoSuchKey:
            raise FileNotFoundError(f"File {name} does not exist")
    
    def get_modified_time(self, name):
        """
        Get the last modified time of a file
        """
        from datetime import datetime
        key = self._get_key(name)
        try:
            obj = self.bucket.get_object_meta(key)
            return datetime.fromtimestamp(obj.last_modified)
        except oss2.exceptions.NoSuchKey:
            raise FileNotFoundError(f"File {name} does not exist")
    
    def get_available_name(self, name, max_length=None):
        """
        Return a filename that's free on the target storage system
        """
        # If the filename already exists, add an underscore and a number until the filename doesn't exist
        name = self._get_valid_name(name)
        if self.exists(name):
            dir_name, file_name = os.path.split(name)
            file_root, file_ext = os.path.splitext(file_name)
            count = 1
            
            while self.exists(name):
                # file.jpg -> file_1.jpg, file_2.jpg, etc.
                name = os.path.join(dir_name, f"{file_root}_{count}{file_ext}")
                count += 1
        
        return name
    
    def _get_valid_name(self, name):
        """
        Return a filename that's suitable for use with the storage system
        """
        # Replace spaces with underscores and remove any special characters
        import re
        s = name.replace(' ', '_')
        s = re.sub(r'[^\w\-\.]', '', s)
        return s 