from __future__ import annotations

from collections.abc import Generator
from typing import Callable

from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.orm import Session

from ..core.logger import AppLogger
from ..core.permissions import has_permission
from ..core.security import TokenError, decode_access_token
from ..core.settings import AppSettings, get_settings
from ..database import SessionLocal
from ..database.orm import UserORM
from ..repositories import (
    CommissionRepository,
    EmployeeRepository,
    MonthlyPayrollRepository,
    OperationLogRepository,
    OrganizationRepository,
    PayrollRepository,
    RealtimeAttendanceRepository,
    ReportRepository,
    SalaryConfigRepository,
    SalaryProfileRepository,
    UserRepository,
)
from ..services import (
    AuthService,
    CommissionService,
    EmployeeService,
    MonthlyPayrollService,
    OperationLogService,
    OrganizationService,
    PayrollApplicationService,
    RealtimeAttendanceService,
    ReportService,
    SalaryConfigService,
    SalaryProfileService,
)
from ..io.storage import FileStorage

bearer_scheme = HTTPBearer(auto_error=False)
logger = AppLogger.get_logger(__name__)


def get_db_session() -> Generator[Session, None, None]:
    """为单次 HTTP 请求提供数据库会话，并在请求结束后关闭。"""
    session = SessionLocal()
    try:
        yield session
    finally:
        session.close()


def get_app_settings() -> AppSettings:
    return get_settings()


def get_payroll_repository(session: Session = Depends(get_db_session)) -> PayrollRepository:
    return PayrollRepository(session)


def get_monthly_payroll_repository(session: Session = Depends(get_db_session)) -> MonthlyPayrollRepository:
    return MonthlyPayrollRepository(session)


def get_realtime_attendance_repository(session: Session = Depends(get_db_session)) -> RealtimeAttendanceRepository:
    return RealtimeAttendanceRepository(session)


def get_commission_repository(session: Session = Depends(get_db_session)) -> CommissionRepository:
    return CommissionRepository(session)


def get_employee_repository(session: Session = Depends(get_db_session)) -> EmployeeRepository:
    return EmployeeRepository(session)


def get_report_repository(session: Session = Depends(get_db_session)) -> ReportRepository:
    return ReportRepository(session)


def get_salary_config_repository(session: Session = Depends(get_db_session)) -> SalaryConfigRepository:
    return SalaryConfigRepository(session)


def get_salary_profile_repository(session: Session = Depends(get_db_session)) -> SalaryProfileRepository:
    return SalaryProfileRepository(session)


def get_user_repository(session: Session = Depends(get_db_session)) -> UserRepository:
    return UserRepository(session)


def get_operation_log_repository(session: Session = Depends(get_db_session)) -> OperationLogRepository:
    return OperationLogRepository(session)


def get_organization_repository(session: Session = Depends(get_db_session)) -> OrganizationRepository:
    return OrganizationRepository(session)


def get_auth_service(
    repository: UserRepository = Depends(get_user_repository),
    settings: AppSettings = Depends(get_app_settings),
) -> AuthService:
    return AuthService(repository=repository, settings=settings)


def get_operation_log_service(
    repository: OperationLogRepository = Depends(get_operation_log_repository),
) -> OperationLogService:
    return OperationLogService(repository=repository)


def get_organization_service(
    repository: OrganizationRepository = Depends(get_organization_repository),
) -> OrganizationService:
    return OrganizationService(repository=repository)


def get_employee_service(
    repository: EmployeeRepository = Depends(get_employee_repository),
) -> EmployeeService:
    return EmployeeService(repository=repository)


def get_salary_profile_service(
    repository: SalaryProfileRepository = Depends(get_salary_profile_repository),
    employee_repository: EmployeeRepository = Depends(get_employee_repository),
) -> SalaryProfileService:
    return SalaryProfileService(repository=repository, employee_repository=employee_repository)


def get_commission_service(
    repository: CommissionRepository = Depends(get_commission_repository),
    employee_repository: EmployeeRepository = Depends(get_employee_repository),
    settings: AppSettings = Depends(get_app_settings),
) -> CommissionService:
    return CommissionService(
        repository=repository,
        employee_repository=employee_repository,
        storage=FileStorage(settings),
    )


def get_salary_config_service(
    repository: SalaryConfigRepository = Depends(get_salary_config_repository),
) -> SalaryConfigService:
    return SalaryConfigService(repository=repository)


def get_report_service(
    repository: ReportRepository = Depends(get_report_repository),
) -> ReportService:
    return ReportService(repository=repository)


def get_payroll_service(
    repository: PayrollRepository = Depends(get_payroll_repository),
    salary_repository: SalaryProfileRepository = Depends(get_salary_profile_repository),
    commission_repository: CommissionRepository = Depends(get_commission_repository),
    employee_repository: EmployeeRepository = Depends(get_employee_repository),
    config_repository: SalaryConfigRepository = Depends(get_salary_config_repository),
    settings: AppSettings = Depends(get_app_settings),
) -> PayrollApplicationService:
    return PayrollApplicationService(
        repository=repository,
        settings=settings,
        salary_repository=salary_repository,
        commission_repository=commission_repository,
        config_service=SalaryConfigService(config_repository),
        employee_repository=employee_repository,
    )


def get_realtime_attendance_service(
    repository: RealtimeAttendanceRepository = Depends(get_realtime_attendance_repository),
    payroll_service: PayrollApplicationService = Depends(get_payroll_service),
) -> RealtimeAttendanceService:
    return RealtimeAttendanceService(repository=repository, payroll_service=payroll_service)


def get_monthly_payroll_service(
    repository: MonthlyPayrollRepository = Depends(get_monthly_payroll_repository),
    payroll_service: PayrollApplicationService = Depends(get_payroll_service),
    employee_repository: EmployeeRepository = Depends(get_employee_repository),
) -> MonthlyPayrollService:
    return MonthlyPayrollService(
        repository=repository,
        payroll_service=payroll_service,
        employee_repository=employee_repository,
    )


def get_current_user(
    credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
    repository: UserRepository = Depends(get_user_repository),
    settings: AppSettings = Depends(get_app_settings),
) -> UserORM:
    """解析 Bearer token，并返回仍然有效的当前用户。"""
    if credentials is None:
        logger.error("认证失败 reason=missing_bearer_token")
        raise HTTPException(status_code=401, detail="请先登录")
    try:
        payload = decode_access_token(credentials.credentials, settings.auth_secret_key)
        user_id = int(payload["sub"])
    except (KeyError, TypeError, ValueError, TokenError) as exc:
        logger.error("认证失败 reason=invalid_or_expired_token")
        raise HTTPException(status_code=401, detail="登录状态无效或已过期") from exc
    user = repository.get_by_id(user_id)
    if user is None or not user.is_active:
        logger.error("认证失败 user_id=%s reason=user_not_found_or_inactive", user_id)
        raise HTTPException(status_code=401, detail="用户不存在或已停用")
    logger.info("认证成功 user_id=%s username=%s role=%s", user.id, user.username, user.role)
    return user


def require_permission(permission: str) -> Callable:
    """生成路由级权限依赖，让路由只声明所需权限。"""
    def dependency(user: UserORM = Depends(get_current_user)) -> UserORM:
        if not has_permission(user.role, permission):
            logger.error(
                "权限校验失败 user_id=%s username=%s role=%s permission=%s",
                user.id,
                user.username,
                user.role,
                permission,
            )
            raise HTTPException(status_code=403, detail="没有该操作权限")
        logger.info(
            "权限校验通过 user_id=%s username=%s role=%s permission=%s",
            user.id,
            user.username,
            user.role,
            permission,
        )
        return user

    return dependency
