import os
from typing import Optional


class CredentialError(Exception):
    pass


class Credentials:
    _instance: Optional['Credentials'] = None
    
    DEFAULT_OSS_ENDPOINT = 'https://oss-cn-shanghai.aliyuncs.com'
    DEFAULT_OSS_REGION = 'cn-shanghai'
    DEFAULT_SLS_ENDPOINT = 'cn-shanghai.log.aliyuncs.com'

    _required_fields = {
        'access_key': ['access_key_id', 'access_key_secret'],
        'oss': ['access_key_id', 'access_key_secret'],
        'sls': ['access_key_id', 'access_key_secret', 'sls_project', 'sls_logstore'],
        'mns': ['access_key_id', 'access_key_secret'],
    }
    
    def __init__(
        self,
        access_key_id: Optional[str] = None,
        access_key_secret: Optional[str] = None,
        oss_endpoint: str = DEFAULT_OSS_ENDPOINT,
        oss_region: str = DEFAULT_OSS_REGION,
        sls_endpoint: str = DEFAULT_SLS_ENDPOINT,
        sls_project: Optional[str] = None,
        sls_logstore: Optional[str] = None,
        mns_endpoint: Optional[str] = None,
    ):
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret
        self.oss_endpoint = oss_endpoint
        self.oss_region = oss_region
        self.sls_endpoint = sls_endpoint
        self.sls_project = sls_project
        self.sls_logstore = sls_logstore
        self.mns_endpoint = mns_endpoint
    
    def _check(self, service: str):
        """检查指定服务的必需配置是否存在"""
        missing = [f for f in self._required_fields[service] if not getattr(self, f)]
        if missing:
            raise CredentialError(f"{service.upper()} 配置缺失: {', '.join(missing)}")
    
    def _get(self, field: str):
        """获取配置值，若为空则抛出错误"""
        val = getattr(self, field)
        if not val:
            raise CredentialError(f"配置缺失: {field}")
        return val
    
    @classmethod
    def init(
        cls,
        access_key_id: Optional[str] = None,
        access_key_secret: Optional[str] = None,
        oss_endpoint: str = DEFAULT_OSS_ENDPOINT,
        oss_region: str = DEFAULT_OSS_REGION,
        sls_endpoint: str = DEFAULT_SLS_ENDPOINT,
        sls_project: Optional[str] = None,
        sls_logstore: Optional[str] = None,
        mns_endpoint: Optional[str] = None,
    ):
        cls._instance = cls(
            access_key_id=access_key_id,
            access_key_secret=access_key_secret,
            oss_endpoint=oss_endpoint,
            oss_region=oss_region,
            sls_endpoint=sls_endpoint,
            sls_project=sls_project,
            sls_logstore=sls_logstore,
            mns_endpoint=mns_endpoint,
        )
    
    @classmethod
    def init_from_env(cls):
        cls._instance = cls(
            access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
            access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
            oss_endpoint=os.environ.get('OSS_ENDPOINT', cls.DEFAULT_OSS_ENDPOINT),
            oss_region=os.environ.get('OSS_REGION', cls.DEFAULT_OSS_REGION),
            sls_endpoint=os.environ.get('SLS_ENDPOINT', cls.DEFAULT_SLS_ENDPOINT),
            sls_project=os.environ.get('SLS_PROJECT'),
            sls_logstore=os.environ.get('SLS_LOGSTORE'),
            mns_endpoint=os.environ.get('MNS_ENDPOINT'),
        )
    
    @classmethod
    def get(cls) -> 'Credentials':
        if cls._instance is None:
            raise CredentialError("凭证未初始化，请先调用 Credentials.init() 或 Credentials.init_from_env()")
        return cls._instance
    
    @classmethod
    def get_oss_config(cls) -> tuple[str, str, str, str]:
        creds = cls.get()
        creds._check('oss')
        return creds._get('access_key_id'), creds._get('access_key_secret'), creds.oss_endpoint, creds.oss_region
    
    @classmethod
    def get_sls_config(cls) -> tuple[str, str, str, str, str]:
        creds = cls.get()
        creds._check('sls')
        return creds._get('access_key_id'), creds._get('access_key_secret'), creds.sls_endpoint, creds._get('sls_project'), creds._get('sls_logstore')
    
    @classmethod
    def get_mns_config(cls) -> tuple[str, str, str]:
        creds = cls.get()
        creds._check('mns')
        return creds._get('access_key_id'), creds._get('access_key_secret'), creds.mns_endpoint # type:ignore
