from __future__ import annotations

from sqlalchemy import create_engine, event, inspect, text
from sqlalchemy.engine import make_url
from sqlalchemy.orm import sessionmaker

from ..core.logger import AppLogger
from ..core.settings import get_settings
from .orm import Base

settings = get_settings()
logger = AppLogger.get_logger(__name__)
# SQLite 默认限制跨线程连接；FastAPI 请求线程会复用连接池，需要显式放开。
connect_args = {"check_same_thread": False} if settings.database_url.startswith("sqlite") else {}
engine = create_engine(settings.database_url, connect_args=connect_args)


@event.listens_for(engine, "connect")
def _set_mysql_session_timezone(dbapi_connection, connection_record) -> None:
    """MySQL 会话统一使用北京时间，避免 CURRENT_TIMESTAMP 少 8 小时。"""
    if not settings.database_url.startswith("mysql"):
        return

    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SET time_zone = '+08:00'")
    finally:
        cursor.close()


SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)

USER_PROFILE_COLUMNS = {
    "avatar_url": {
        "mysql": "VARCHAR(512) NOT NULL DEFAULT '' COMMENT '用户头像URL或静态资源路径'",
        "sqlite": "VARCHAR(512) NOT NULL DEFAULT ''",
    },
    "email": {
        "mysql": "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '用户邮箱'",
        "sqlite": "VARCHAR(128) NOT NULL DEFAULT ''",
    },
    "phone": {
        "mysql": "VARCHAR(32) NOT NULL DEFAULT '' COMMENT '用户手机号'",
        "sqlite": "VARCHAR(32) NOT NULL DEFAULT ''",
    },
    "department": {
        "mysql": "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '用户所属部门'",
        "sqlite": "VARCHAR(128) NOT NULL DEFAULT ''",
    },
    "position_title": {
        "mysql": "VARCHAR(128) NOT NULL DEFAULT '' COMMENT '用户职位或岗位名称'",
        "sqlite": "VARCHAR(128) NOT NULL DEFAULT ''",
    },
}

PAYROLL_RESULT_COLUMNS = {
    "salary_month": {
        "mysql": "VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份，格式YYYY-MM'",
        "sqlite": "VARCHAR(7) NOT NULL DEFAULT ''",
    },
    "salary_mode": {
        "mysql": "VARCHAR(32) NOT NULL DEFAULT 'monthly' COMMENT '薪资模式快照'",
        "sqlite": "VARCHAR(32) NOT NULL DEFAULT 'monthly'",
    },
    "attendance_days": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '出勤天数'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "absence_days": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '缺勤天数'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "actual_work_hours": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '实际工作工时'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "workday_overtime_hours": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '工作日剩余可计费加班工时'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "weekend_overtime_hours": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '周末剩余可计费加班工时'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "holiday_overtime_hours": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '法定节假日剩余可计费加班工时'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "commission_amount": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '手工维护的提成费用'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "base_pay": {
        "mysql": "FLOAT NULL COMMENT '按薪资模式计算后的基础工资'",
        "sqlite": "FLOAT NULL",
    },
    "weekend_overtime_rate": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '周末加班小时单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "holiday_overtime_rate": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '法定节假日加班小时单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
}

SALARY_PROFILE_COLUMNS = {
    "salary_mode": {
        "mysql": "VARCHAR(32) NOT NULL DEFAULT 'monthly' COMMENT '薪资模式：monthly包月、hourly计时、piecework计件、probation试用期'",
        "sqlite": "VARCHAR(32) NOT NULL DEFAULT 'monthly'",
    },
    "hourly_rate": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '计时工资小时单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "piece_quantity": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '计件数量'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "piece_unit_price": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '计件单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "probation_type": {
        "mysql": "VARCHAR(32) NOT NULL DEFAULT 'ratio' COMMENT '试用期计算方式：ratio固定比例、fixed固定金额'",
        "sqlite": "VARCHAR(32) NOT NULL DEFAULT 'ratio'",
    },
    "probation_ratio": {
        "mysql": "FLOAT NOT NULL DEFAULT 1 COMMENT '试用期工资比例'",
        "sqlite": "FLOAT NOT NULL DEFAULT 1",
    },
    "probation_salary": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '试用期固定工资'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "weekend_overtime_rate": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '周末加班费小时单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
    "holiday_overtime_rate": {
        "mysql": "FLOAT NOT NULL DEFAULT 0 COMMENT '法定节假日加班费小时单价'",
        "sqlite": "FLOAT NOT NULL DEFAULT 0",
    },
}


def init_database() -> None:
    """创建 MySQL 库和缺失表；当前项目暂不引入迁移工具。"""
    logger.info("数据库初始化开始 database_url=%s", _safe_database_url(settings.database_url))
    try:
        _ensure_database_exists()
        Base.metadata.create_all(bind=engine)
        _ensure_user_profile_columns()
        _ensure_salary_profile_columns()
        _ensure_payroll_result_columns()
    except Exception:
        logger.exception("数据库初始化失败")
        raise
    logger.info("数据库初始化完成")


def _ensure_user_profile_columns() -> None:
    """为已有 users 表补齐个人资料字段；create_all 不会修改旧表结构。"""
    existing_columns = {column["name"] for column in inspect(engine).get_columns("users")}
    missing_columns = [
        (column_name, definitions)
        for column_name, definitions in USER_PROFILE_COLUMNS.items()
        if column_name not in existing_columns
    ]
    if not missing_columns:
        return

    dialect = "mysql" if settings.database_url.startswith("mysql") else "sqlite"
    with engine.begin() as connection:
        for column_name, definitions in missing_columns:
            column_sql = definitions[dialect]
            connection.execute(text(f"ALTER TABLE users ADD COLUMN `{column_name}` {column_sql}"))
            logger.info("users 表字段已补齐 column=%s", column_name)


def _ensure_payroll_result_columns() -> None:
    """为已有 payroll_results 表补齐工资结果新增字段。"""
    if not inspect(engine).has_table("payroll_results"):
        return

    existing_columns = {column["name"] for column in inspect(engine).get_columns("payroll_results")}
    missing_columns = [
        (column_name, definitions)
        for column_name, definitions in PAYROLL_RESULT_COLUMNS.items()
        if column_name not in existing_columns
    ]
    if not missing_columns:
        return

    dialect = "mysql" if settings.database_url.startswith("mysql") else "sqlite"
    with engine.begin() as connection:
        for column_name, definitions in missing_columns:
            column_sql = definitions[dialect]
            connection.execute(text(f"ALTER TABLE payroll_results ADD COLUMN `{column_name}` {column_sql}"))
            logger.info("payroll_results 表字段已补齐 column=%s", column_name)


def _ensure_salary_profile_columns() -> None:
    """为已有 salary_profiles 表补齐薪资模式和加班费单价字段。"""
    if not inspect(engine).has_table("salary_profiles"):
        return

    existing_columns = {column["name"] for column in inspect(engine).get_columns("salary_profiles")}
    missing_columns = [
        (column_name, definitions)
        for column_name, definitions in SALARY_PROFILE_COLUMNS.items()
        if column_name not in existing_columns
    ]
    if not missing_columns:
        return

    dialect = "mysql" if settings.database_url.startswith("mysql") else "sqlite"
    with engine.begin() as connection:
        for column_name, definitions in missing_columns:
            column_sql = definitions[dialect]
            connection.execute(text(f"ALTER TABLE salary_profiles ADD COLUMN `{column_name}` {column_sql}"))
            logger.info("salary_profiles 表字段已补齐 column=%s", column_name)


def _ensure_database_exists() -> None:
    if not settings.database_url.startswith("mysql"):
        return

    url = make_url(settings.database_url)
    if not url.database:
        return

    database_name = url.database
    admin_url = url.set(database=None)
    admin_engine = create_engine(admin_url)
    safe_database_name = database_name.replace("`", "``")
    try:
        with admin_engine.begin() as connection:
            connection.execute(
                text(
                    f"CREATE DATABASE IF NOT EXISTS `{safe_database_name}` "
                    "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
                )
            )
        logger.info("MySQL 数据库检查完成 database=%s", database_name)
    finally:
        admin_engine.dispose()


def _safe_database_url(database_url: str) -> str:
    try:
        return str(make_url(database_url).render_as_string(hide_password=True))
    except Exception:
        return "***invalid-database-url***"
