#!/usr/bin/env python3
"""
数据管理服务 - 统一的数据访问层（driver）

职责：
- 管理 DatabaseManager（唯一持有者）
- 初始化数据库和表结构
- 提供统一的数据访问 API（get_table 等）
- 协调各个 DataService

表名定义：
-   表名由 DataManager 配合 PathManager 发现（core/tables、userspace/extensions/tables），
  core 表须 sys_ 前缀，userspace 表无前缀限制；get_table(table_name) 使用实际表名字符串。

架构：
- DataManager: 数据访问层入口，管理 DB 和 DataServices
- DataServices: 数据服务层（stock, macro, calendar, ui_transit）
  - StockService: 股票数据服务（K线、股票列表、标签、财务等）
  - MacroService: 宏观经济数据服务
  - CalendarService: 交易日历服务
  - CorporateFinanceService: 企业财务数据服务（StockService 的子服务）
"""
from typing import Dict, List, Any, Optional, Union, TYPE_CHECKING, Type
import logging
import threading
import importlib
import importlib.util
import pkgutil
import inspect
from pathlib import Path

from core.infra.db import DatabaseManager

if TYPE_CHECKING:
    from core.global_enums.enums import EntityType
# Loaders 已废弃，不再导入
# 所有功能已迁移到 data_services
from core.infra.project_context import ConfigManager
from core.utils.date.date_utils import DateUtils


logger = logging.getLogger(__name__)


class DataManager:
    """
    数据管理服务（数据访问总入口，driver）

    职责：
    - 唯一持有和管理 DatabaseManager
    - 初始化数据库、连接池、表结构（Base Tables + 策略表）
    - 提供统一的数据访问 API（get_table 等；表名即发现并注册后的实际表名）
    - 协调各 DataService（stock, macro, calendar, ui_transit）
    - 预留 Repository / 策略表 Model 的注册与访问能力（新架构方向）

    单例模式：
    - 单进程环境下：使用同一个实例（线程安全）
    - 多进程环境下：每个进程有独立的实例（进程间内存不共享）
    - 支持通过 force_new=True 强制创建新实例

    使用方式：
        from core.modules.data_manager import DataManager

        # 自动使用单例（推荐）
        data_mgr = DataManager(is_verbose=True)
        data_mgr.initialize()
        klines = data_mgr.stock.kline.load('000001.SZ', term='daily', adjust='qfq')

        # 强制创建新实例（不推荐，除非有特殊需求）
        data_mgr = DataManager(is_verbose=True, force_new=True)
    """
    
    # 单例实例（每个进程独立）
    _instance: Optional['DataManager'] = None
    _lock = threading.Lock()  # 单例 __new__ 锁
    _initialize_lock = threading.Lock()  # 首次 initialize 锁（BFF 并发 API 防双开 DuckDB）
    
    @classmethod
    def reset_instance(cls):
        """
        重置单例实例（主要用于测试或特殊场景）
        
        注意：此方法会清除当前的单例实例，下次创建时会重新初始化
        """
        with cls._lock:
            cls._instance = None
    
    @classmethod
    def get_instance(cls) -> Optional['DataManager']:
        """
        获取当前的单例实例（如果存在）
        
        Returns:
            DataManager 实例，如果不存在则返回 None
        """
        return cls._instance
    
    def __new__(cls, db: Optional[DatabaseManager] = None, is_verbose: bool = False, force_new: bool = False):
        """
        单例模式实现
        
        Args:
            db: 可选的 DatabaseManager 实例
            is_verbose: 是否输出详细日志
            force_new: 是否强制创建新实例（默认 False，使用单例）
        
        Returns:
            DataManager 实例
        """
        # 如果强制创建新实例，直接创建
        if force_new:
            instance = super().__new__(cls)
            return instance
        
        # 单例模式：如果实例不存在，创建新实例
        if cls._instance is None:
            with cls._lock:
                # 双重检查，避免多线程竞争
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, db: Optional[DatabaseManager] = None, is_verbose: bool = False, force_new: bool = False):
        """
        初始化数据管理器

        Args:
            db: 可选的 DatabaseManager 实例，如果提供则使用该实例，否则创建新实例
            is_verbose: 是否输出详细日志
            force_new: 是否强制创建新实例（单例模式下忽略）
        
        注意：
            - 初始化会自动调用 initialize()，无需手动调用
            - initialize() 是幂等的，多次调用只会执行一次
            - 单例模式下，如果实例已初始化，会复用现有实例的配置
        """
        # 单例模式：如果已经初始化，不重复初始化
        if hasattr(self, '_initialized') and self._initialized:
            # 如果提供了新的参数，更新配置（但保持已初始化的状态）
            if is_verbose and not self.is_verbose:
                self.is_verbose = is_verbose
            return
        
        self.is_verbose = is_verbose
        self.db = db
        self._initialized = False

        # DataService 主类（跨service协调器）
        self._data_service = None

        # Table 缓存（table_name -> Model 类）
        # Base Tables 自动发现，用户自定义 Tables 通过 register_table 注册
        self._table_cache: Dict[str, Type[Any]] = {}
        
        # 自动初始化（幂等，多次调用只执行一次）
        self.initialize()
    
    def initialize(self):
        """
        初始化数据管理器
        
        步骤：
        1. 创建并初始化 DatabaseManager（连接池）
        2. 创建所有 Base Tables
        3. 初始化所有 DataServices
        
        注意：
            - 此方法是幂等的，多次调用只会执行一次
            - 在 __init__ 中会自动调用，通常无需手动调用
        """
        if self._initialized:
            return

        with self._initialize_lock:
            if self._initialized:
                return

            try:
                from core.infra.project_context import ConfigManager

                db_cfg = ConfigManager.load_database_config()
                if str(db_cfg.get("database_type") or "").lower() == "duckdb":
                    from core.infra.db.engines.duckdb.process_pool_scope import (
                        wait_for_main_duckdb_worker_pool_end,
                    )

                    wait_for_main_duckdb_worker_pool_end()

                # 1. 初始化 DatabaseManager（只初始化连接池，不创建表）
                if self.db is None:
                    if self.is_verbose:
                        logger.info("🔧 初始化 DatabaseManager...")

                    self.db = DatabaseManager(is_verbose=self.is_verbose)
                    self.db.initialize()
                    # 设置为默认实例，便于 DbBaseModel 等自动获取 db
                    DatabaseManager.set_default(self.db)
                else:
                    if self.is_verbose:
                        logger.info("🔧 使用已提供的 DatabaseManager 实例...")

                # 2. 创建所有 Base Tables（业务逻辑）
                if self.db:
                    if self.is_verbose:
                        logger.info("🔧 创建 Base Tables...")
                    self.db.create_all_base_tables()
                elif self.is_verbose:
                    logger.info("ℹ️  只读模式，跳过 Base Tables 创建")

                # 3. 自动发现并缓存表（core/tables -> sys_*，userspace/extensions/tables -> cust_*）
                if self.is_verbose:
                    logger.info("🔧 自动发现表（core/tables + userspace/extensions/tables）...")
                self._discover_tables()

                # 4. 初始化 DataService（跨service协调器）
                if self.is_verbose:
                    logger.info("🔧 初始化 DataService...")
                from core.modules.data_manager.data_services import DataService
                self._data_service = DataService(self)
                try:
                    self._data_service.index.sync_list_from_config()
                except Exception as e:
                    logger.warning(
                        "sys_index_list 配置同步失败（可稍后由 renew 重试）: %s", e
                    )

                self._initialized = True

                if self.is_verbose:
                    logger.info("✅ DataManager 初始化完成")

            except Exception as e:
                logger.error("❌ DataManager 初始化失败: %s", e)
                raise

    # ------------------------------------------------------------------
    # Table 发现与注册
    # ------------------------------------------------------------------
    
    def register_table(self, table_folder_path: str, from_core: bool = False) -> Optional[Type[Any]]:
        """
        注册表（从文件夹路径加载，配合 PathManager 发现的目录）。
        
        表文件夹结构：schema.py + model.py，表名取自 schema["name"]。
        
        Args:
            table_folder_path: 表文件夹路径（core/tables/xxx 或 userspace/extensions/tables/xxx）
            from_core: 若为 True 表示来自 core/tables，则 schema["name"] 须以 sys_ 开头，否则跳过
        
        Returns:
            Model 类（继承自 DbBaseModel），若校验不通过或加载失败返回 None
        """
        from core.infra.db import DbBaseModel
        from core.infra.project_context import FileManager
        from core.infra.db.schema_manager import SchemaManager

        try:
            table_folder = Path(table_folder_path)
            if not table_folder.is_absolute():
                table_folder = Path.cwd() / table_folder
            
            if not table_folder.exists() or not table_folder.is_dir():
                logger.error(f"❌ 表文件夹不存在: {table_folder_path}")
                return None
            
            # 1. 加载 schema（仅 schema.py）
            schema_py = table_folder / "schema.py"
            if not schema_py.exists():
                logger.error(f"❌ 表文件夹中未找到 schema.py: {table_folder_path}")
                return None
            if self.db is not None and getattr(self.db, "schema_manager", None) is not None:
                schema = self.db.schema_manager.load_schema_from_python(str(schema_py))
            else:
                db_type = (
                    self.db.config.get("database_type", "postgresql")
                    if self.db is not None
                    else "postgresql"
                )
                schema = SchemaManager(database_type=db_type).load_schema_from_python(
                    str(schema_py)
                )
            if not schema:
                return None

            table_name = schema.get("name")
            if not table_name:
                logger.error(f"❌ schema 中未找到 name: {table_folder_path}")
                return None
            if from_core and not table_name.startswith("sys_"):
                if self.is_verbose:
                    logger.debug(f"⏭️  跳过 core 表（非 sys_ 前缀）: {table_name} ({table_folder_path})")
                return None

            if self.db is not None:
                self.db.register_table(table_name, schema)
            
            # 2. 查找并加载 model.py
            model_file_path = FileManager.find_file("model.py", table_folder, recursive=False)
            if not model_file_path:
                logger.error(f"❌ 表文件夹中未找到 model.py: {table_folder_path}")
                return None
            
            try:
                spec = importlib.util.spec_from_file_location("table_model", model_file_path)
                if spec is None or spec.loader is None:
                    logger.error(f"❌ 无法加载模块: {model_file_path}")
                    return None
                model_module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(model_module)
                
                model_class = None
                for name, obj in inspect.getmembers(model_module):
                    if (inspect.isclass(obj) and issubclass(obj, DbBaseModel) and obj != DbBaseModel):
                        model_class = obj
                        break
                if model_class is None:
                    logger.error(f"❌ 模块中未找到继承自 DbBaseModel 的类: {model_file_path}")
                    return None
                
                if table_name in self._table_cache and self._table_cache[table_name] != model_class:
                    logger.warning(
                        f"⚠️  覆盖已存在的 Table '{table_name}': "
                        f"{self._table_cache[table_name].__name__} -> {model_class.__name__}"
                    )
                self._table_cache[table_name] = model_class
                if self.is_verbose:
                    logger.info(f"✅ 注册 Table: {table_name} -> {model_class.__name__} ({table_folder_path})")
                return model_class
            except Exception as e:
                logger.error(f"❌ 加载模块失败: {model_file_path}, error={e}")
                return None
        except Exception as e:
            logger.error(f"❌ 注册 Table 失败: {table_folder_path}, error={e}")
            return None
    
    def _discover_tables(self):
        """
        配合 PathManager 递归发现 core/tables 与 userspace/extensions/tables 下的表并缓存。
        不依赖目录层级：递归查找所有 schema.py，以其所在目录为表目录并注册。
        - core/tables：仅注册 schema["name"] 以 sys_ 开头的表，否则跳过。
        - userspace/extensions/tables：表名无前缀限制，全部注册。
        仅在初始化时调用一次，结果缓存在 _table_cache 中。
        """
        from core.infra.project_context import PathManager

        def _dirs_with_schema(root: Path) -> set:
            """递归收集包含 schema.py 的目录。"""
            return {p.parent for p in root.rglob("schema.py") if p.is_file()}

        try:
            # 1. core/tables（仅接受 sys_ 前缀）
            core_tables_dir = PathManager.core() / "tables"
            if core_tables_dir.exists():
                for table_folder in sorted(_dirs_with_schema(core_tables_dir)):
                    self.register_table(str(table_folder), from_core=True)

            # 2. userspace/extensions/tables（表名无限制）
            userspace_tables_dir = PathManager.extensions_tables()
            if userspace_tables_dir.exists():
                for table_folder in sorted(_dirs_with_schema(userspace_tables_dir)):
                    self.register_table(str(table_folder), from_core=False)

            if self.is_verbose:
                logger.info(f"✅ 自动发现并缓存了 {len(self._table_cache)} 个表")
        except Exception as e:
            logger.error(f"❌ 自动发现表失败: {e}")
            raise
    
    # ------------------------------------------------------------------
    # Table 访问（基础表 + 自定义表）
    # ------------------------------------------------------------------

    def get_table(self, table_name: str) -> Any:
        """
        获取指定表对应的 Model 实例（内部方法，仅供 DataService 使用）。
        
        表名由 DataManager 发现并注册（core 表 sys_ 前缀，userspace 表无限制）。
        
        Args:
            table_name: 实际表名，如 "sys_stock_list"。
            
        Returns:
            对应的 Model 实例（已自动绑定默认 db），如果未找到则返回 None
        """
        model_class = self._table_cache.get(table_name)

        if not model_class:
            logger.warning(f"⚠️  表 '{table_name}' 没有对应的 Model 类（可能未注册）")
            return None

        return model_class()

    def get_physical_table_name(self, logical_name: str) -> str:
        """
        返回逻辑表在当前数据库下的“物理表名”（用于直接写 SQL 时使用）。

        - PostgreSQL: 使用 pgsql_schema.table_name
        - MySQL: 暂时返回逻辑名本身
        """
        # 先获取 Model，看表是否已注册
        model = self.get_table(logical_name)
        if not model:
            raise ValueError(f"Unknown table: {logical_name}")

        db_type = self.db.config.get("database_type", "postgresql")

        if db_type == "postgresql":
            pg_cfg = self.db.config.get("postgresql", {})
            pg_schema = pg_cfg.get("pgsql_schema", "public")
            return f"{pg_schema}.{model.table_name}"

        # MySQL：目前直接返回逻辑名；后续如有表名前缀等需求再扩展
        return logical_name

    # ------------------------------------------------------------------
    # DataService 属性访问
    # ------------------------------------------------------------------
    
    @property
    def stock(self):
        """
        股票数据服务（属性访问）
        
        Returns:
            StockService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.stock
    
    @property
    def macro(self):
        """
        宏观经济数据服务（属性访问）
        
        Returns:
            MacroService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.macro
    
    @property
    def calendar(self):
        """
        日期服务（属性访问）
        
        Returns:
            CalendarService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.calendar
    
    @property
    def index(self):
        """
        指数数据服务（属性访问）
        
        Returns:
            IndexService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.index
    
    @property
    def db_cache(self):
        """
        数据库缓存服务（属性访问）
        
        Returns:
            DbCacheService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.db_cache

    @property
    def backup_restore(self):
        """
        跨表备份与恢复服务（属性访问）

        Returns:
            BackupAndRestoreService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service.backup_restore
    
    @property
    def service(self):
        """
        跨 service 协调器（属性访问），提供 data_mgr.stock / macro / calendar 等统一入口。

        Returns:
            DataService 实例
        """
        if not self._data_service:
            raise RuntimeError("DataManager 未初始化，请先调用 initialize()")
        return self._data_service