import os
import sys
from cpbox.tool import file
from pathlib import Path
from tibase.core import tibaselog

from dotenv import dotenv_values

import logging
logger = logging.getLogger(__name__)

def detect_base_dir(base_dir=Path.cwd()):
    # 先从当前目录开始，如果当前目录有 .env 文件，则返回当前目录
    # 否则，向上遍历目录，直到找到 .env 文件或到达根目录
    # 如果运行脚本，则从脚本所在目录开始
    base_dir = Path.cwd()
    if hasattr(sys.modules['__main__'], '__file__'):
        base_dir = Path(sys.argv[0]).resolve().parent
    while True:
        if (base_dir / '.env').exists():
            return base_dir
        elif base_dir == base_dir.parent:
            return None
        else:
            base_dir = base_dir.parent

def auto_load_dot_env(base_dir: Path):
    env_dict = dotenv_values(base_dir / '.env')
    if (base_dir / '.env.local').exists():
        env_local_dict = dotenv_values(base_dir / '.env.local')
        env_dict.update(env_local_dict)

    for key, value in env_dict.items():
        if value is None:
            continue
        if key in os.environ:
            continue
        os.environ[key] = value

    return env_dict


class EnvConfig(dict):

    def __init__(self, data):
        dict.__init__(self, data or {})

    def _to_type(self, raw, item_type, default, strict):
        if raw is None:
            return default
        if item_type is bool:
            value = str(raw).lower()
            if value in ('true', '1', 'yes', 'y', 'on'):
                return True
            if value in ('false', '0', 'no', 'n', 'off'):
                return False
            return default
        try:
            return item_type(raw)
        except ValueError:
            if strict:
                raise
            return default

    def get_bool(self, key, default=False):
        return self._to_type(self.get(key, None), bool, default, strict=False)

    def get_int(self, key, default=0, strict=True):
        return self._to_type(self.get(key, None), int, default, strict)

    def get_float(self, key, default=0.0, strict=True):
        return self._to_type(self.get(key, None), float, default, strict)

    def get_list(self, key, item_type=str, sep=',', default=None):
        raw = self.get(key, None)
        if raw is None:
            return default if default is not None else []
        items = str(raw).split(sep)
        items = [item.strip() for item in items]
        items = [item for item in items if item]
        return [self._to_type(item, item_type, default, strict=True) for item in items]


def _detect_app_env(env_config):
    app_env = os.getenv('APP_ENV') or env_config.get('APP_ENV') or env_config.get('ENV') or 'prod'
    app_env = str(app_env).lower()
    if app_env not in ('dev', 'test', 'pre', 'prod'):
        return 'prod'
    return app_env


class _GlobalInfo:

    def __init__(self):
        self._root_dir = detect_base_dir()
        self._env_config = EnvConfig(auto_load_dot_env(self._root_dir))

_global_info = _GlobalInfo()

from tibase.core import cls
class GContext(cls.Singleton):

    def __init__(self):
        super().__init__()

    def _init_once(self):
        self.root_dir = _global_info._root_dir
        self.env_config = _global_info._env_config
        self.app_env = _detect_app_env(self.env_config)
        self.env_config['APP_ENV'] = self.app_env
        self.is_dev_mode = self.app_env in ('dev', 'test')

        self._init_logger()

        self.output_dir = self.root_dir / 'output'
        file.ensure_dir(self.output_dir)

        self.workspace_dir = self.output_dir / 'workspace'
        file.ensure_dir(self.workspace_dir)

    def _init_logger(self):
        tibaselog.log_manager.init_from_env(self.env_config)
        logs_dir = self.root_dir / 'output' / 'logs'
        app_name = self.env_config.get('APP_NAME', 'tiapp')
        tibaselog.log_manager.add_file_logger(app_name, logs_dir)

    # ==================== 延迟加载的工具模块 ====================

    @property
    def tools(self):
        """延迟加载工具模块"""
        from tibase.core import tools
        return tools

    @property
    def log_mgr(self):
        return tibaselog.log_manager

gcontext = GContext()
