from __future__ import annotations

from datetime import date, datetime

from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship

from ..core.timezone import now_shanghai


class Base(DeclarativeBase):
    pass


class UserORM(Base):
    """系统登录用户，role 决定菜单和操作权限。"""

    __tablename__ = "users"
    __table_args__ = {
        "comment": "系统用户表，保存登录账号、角色和启用状态",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="用户主键ID")
    username: Mapped[str] = mapped_column(
        String(64),
        unique=True,
        nullable=False,
        index=True,
        comment="登录用户名，全局唯一",
    )
    display_name: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="用户显示名称",
    )
    avatar_url: Mapped[str] = mapped_column(
        String(512),
        nullable=False,
        default="",
        comment="用户头像URL或静态资源路径",
    )
    email: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="用户邮箱",
    )
    phone: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="",
        comment="用户手机号",
    )
    department: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="用户所属部门",
    )
    position_title: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="用户职位或岗位名称",
    )
    password_hash: Mapped[str] = mapped_column(
        String(256),
        nullable=False,
        comment="密码哈希值，不保存明文密码",
    )
    role: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        index=True,
        comment="用户角色：superuser、manager、viewer",
    )
    is_active: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=True,
        comment="是否启用账号",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        comment="创建时间",
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )


class DepartmentORM(Base):
    """组织部门基础数据，支持父子级部门结构。"""

    __tablename__ = "departments"
    __table_args__ = {
        "comment": "部门表，维护组织架构中的部门及上下级关系",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="部门主键ID")
    parent_id: Mapped[int | None] = mapped_column(
        ForeignKey("departments.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="父级部门ID，顶级部门为空",
    )
    department_code: Mapped[str] = mapped_column(
        String(64),
        unique=True,
        nullable=False,
        index=True,
        comment="部门编码，全局唯一",
    )
    name: Mapped[str] = mapped_column(String(128), unique=True, nullable=False, index=True, comment="部门名称")
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="排序号，越小越靠前")
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True, comment="是否启用")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="部门备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )


class PositionORM(Base):
    """岗位基础数据，岗位隶属于一个部门。"""

    __tablename__ = "positions"
    __table_args__ = (
        UniqueConstraint("department_id", "name", name="uq_positions_department_name"),
        {
            "comment": "岗位表，维护部门下的职位/岗位",
            "mysql_charset": "utf8mb4",
            "mysql_collate": "utf8mb4_unicode_ci",
        },
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="岗位主键ID")
    department_id: Mapped[int] = mapped_column(
        ForeignKey("departments.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="关联部门ID",
    )
    position_code: Mapped[str] = mapped_column(
        String(64),
        unique=True,
        nullable=False,
        index=True,
        comment="岗位编码，全局唯一",
    )
    name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="岗位名称")
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="排序号，越小越靠前")
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True, comment="是否启用")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="岗位备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    department: Mapped[DepartmentORM] = relationship()


class EmployeeORM(Base):
    """员工基础档案，用于工资计算和薪资档案关联。"""

    __tablename__ = "employees"
    __table_args__ = {
        "comment": "员工基础档案表，维护员工编号、姓名、部门、岗位和钉钉用户ID",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="员工主键ID")
    employee_no: Mapped[str] = mapped_column(
        String(64),
        unique=True,
        nullable=False,
        index=True,
        comment="员工编号，全局唯一，系统自动生成，默认ZA前缀",
    )
    name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名")
    dingtalk_user_id: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        index=True,
        comment="钉钉用户ID，用于对接钉钉考勤",
    )
    department: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="部门名称")
    position: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="岗位名称")
    phone: Mapped[str] = mapped_column(String(32), nullable=False, default="", comment="手机号")
    employment_status: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="active",
        index=True,
        comment="员工状态：active在职、inactive离职",
    )
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="员工备注")
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        comment="创建时间",
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    salary_profile: Mapped["SalaryProfileORM | None"] = relationship(
        back_populates="employee",
        cascade="all, delete-orphan",
        uselist=False,
    )


class SalaryProfileORM(Base):
    """员工薪资档案，独立维护薪资模式、底薪、计时/计件规则和加班费单价。"""

    __tablename__ = "salary_profiles"
    __table_args__ = {
        "comment": "员工薪资档案表，维护薪资模式、底薪、计时计件规则、默认提成和加班费单价",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="薪资档案主键ID")
    employee_id: Mapped[int] = mapped_column(
        ForeignKey("employees.id", ondelete="CASCADE"),
        unique=True,
        nullable=False,
        index=True,
        comment="关联员工ID，一名员工一条当前薪资档案",
    )
    salary_mode: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="monthly",
        index=True,
        comment="薪资模式：monthly包月、hourly计时、piecework计件、probation试用期",
    )
    base_salary: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="基础工资或底薪")
    hourly_rate: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="计时工资小时单价")
    piece_quantity: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="计件数量")
    piece_unit_price: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="计件单价")
    probation_type: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="ratio",
        comment="试用期计算方式：ratio固定比例、fixed固定金额",
    )
    probation_ratio: Mapped[float] = mapped_column(Float, nullable=False, default=1, comment="试用期工资比例")
    probation_salary: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="试用期固定工资")
    commission_amount: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="默认提成金额，月度提成优先维护到commission_records",
    )
    overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="工作日加班费小时单价，独立于基础工资维护",
    )
    weekend_overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="周末加班费小时单价",
    )
    holiday_overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="法定节假日加班费小时单价",
    )
    effective_month: Mapped[str] = mapped_column(
        String(7),
        nullable=False,
        default="",
        comment="生效月份，格式YYYY-MM；为空表示长期当前档案",
    )
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="薪资备注")
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        comment="创建时间",
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    employee: Mapped[EmployeeORM] = relationship(back_populates="salary_profile")


class AttendanceRecordORM(Base):
    """标准化后的每日考勤记录，来自 Excel 或钉钉接口。"""

    __tablename__ = "attendance_record"
    __table_args__ = {
        "comment": "考勤记录表，保存标准化后的每日打卡、迟到、缺卡、请假和加班工时",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="考勤记录主键ID")
    job_id: Mapped[str | None] = mapped_column(
        ForeignKey("payroll_jobs.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联工资计算任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照")
    department: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="部门名称快照")
    work_date: Mapped[date] = mapped_column(Date, nullable=False, index=True, comment="考勤日期")
    first_punch: Mapped[str] = mapped_column(String(5), nullable=False, default="", comment="上班打卡时间HH:MM")
    last_punch: Mapped[str] = mapped_column(String(5), nullable=False, default="", comment="下班打卡时间HH:MM")
    attendance_status: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="present",
        index=True,
        comment="考勤状态：present出勤、absent缺勤、leave请假、missing缺卡",
    )
    late_minutes: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="当日迟到分钟合计")
    missing_card_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="当日缺卡次数")
    leave_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当日请假或调休工时")
    overtime_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当日打卡推算加班工时")
    raw_text: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="原始考勤单元格内容")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="excel", comment="来源：excel或dingtalk")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")


class LeaveRecordORM(Base):
    """请假/调休记录，统一折算为工时。"""

    __tablename__ = "leave_record"
    __table_args__ = {
        "comment": "请假记录表，保存事假、病假、年假、调休等统一工时记录",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="请假记录主键ID")
    job_id: Mapped[str | None] = mapped_column(
        ForeignKey("payroll_jobs.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联工资计算任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照")
    leave_type: Mapped[str] = mapped_column(String(32), nullable=False, index=True, comment="请假类型")
    start_text: Mapped[str] = mapped_column(String(32), nullable=False, default="", comment="原始开始时间文本")
    end_text: Mapped[str] = mapped_column(String(32), nullable=False, default="", comment="原始结束时间文本")
    hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="请假工时")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="excel", comment="来源：excel或dingtalk")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="approved", comment="状态：approved已生效")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")


class OvertimeRecordORM(Base):
    """标准化后的加班记录，按工作日、周末、节假日拆分。"""

    __tablename__ = "overtime_record"
    __table_args__ = {
        "comment": "加班记录表，保存每日加班工时、加班类型、单价和金额",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="加班记录主键ID")
    job_id: Mapped[str | None] = mapped_column(
        ForeignKey("payroll_jobs.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联工资计算任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照")
    work_date: Mapped[date] = mapped_column(Date, nullable=False, index=True, comment="加班日期")
    overtime_type: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="workday",
        index=True,
        comment="加班类型：workday工作日、weekend周末、holiday法定节假日",
    )
    hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="加班工时")
    rate: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="加班小时单价")
    amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="加班费金额")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="excel", comment="来源：excel或dingtalk")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="calculated", comment="状态：calculated已计算")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")


class AttendanceSyncJobORM(Base):
    """钉钉或 Excel 考勤同步批次，用于实时看板和月度核算追踪。"""

    __tablename__ = "attendance_sync_jobs"
    __table_args__ = {
        "comment": "考勤同步任务表，记录钉钉实时同步或Excel导入的批次状态",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[str] = mapped_column(String(32), primary_key=True, comment="同步任务ID，UUID十六进制字符串")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, index=True, comment="数据来源：dingtalk钉钉、excel表格")
    sync_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="today", index=True, comment="同步模式：today今日、month本月至今、range自定义范围")
    attendance_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True, comment="实时看板对应考勤日期")
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份，格式YYYY-MM")
    start_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="同步开始日期")
    end_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="同步结束日期")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="created", index=True, comment="任务状态：created、running、completed、failed")
    employee_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="本次同步涉及员工数量")
    record_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="本次同步获得或解析的考勤记录数量")
    error_message: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="任务失败时的错误信息")
    created_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="创建人用户名快照")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )


class SalaryPreviewORM(Base):
    """本月进行中的薪资预估结果，不作为最终工资发放依据。"""

    __tablename__ = "salary_preview"
    __table_args__ = (
        UniqueConstraint("sync_job_id", "employee_id", name="uq_salary_preview_sync_employee"),
        {
            "comment": "实时薪资预估表，保存当前同步批次下的员工级薪资预估",
            "mysql_charset": "utf8mb4",
            "mysql_collate": "utf8mb4_unicode_ci",
        },
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="薪资预估主键ID")
    sync_job_id: Mapped[str] = mapped_column(
        ForeignKey("attendance_sync_jobs.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="关联考勤同步任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照")
    department: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="部门名称快照")
    position: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="岗位名称快照")
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份，格式YYYY-MM")
    attendance_days: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="本月当前出勤天数")
    actual_work_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="本月当前实际工时")
    punch_overtime_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="打卡推算加班工时")
    leave_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="请假或调休工时")
    remaining_overtime_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="剩余加班工时")
    late_deduction: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前迟到扣款")
    missing_card_deduction: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前缺卡扣款")
    commission_amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前已录入提成")
    overtime_pay: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前加班费")
    estimated_gross_salary: Mapped[float | None] = mapped_column(Float, nullable=True, comment="当前预估应发工资")
    estimated_net_salary: Mapped[float | None] = mapped_column(Float, nullable=True, comment="当前预估实发工资")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="estimated", index=True, comment="预估状态：estimated已预估、warning有异常")
    note: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="预估备注或异常说明")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    sync_job: Mapped[AttendanceSyncJobORM] = relationship()
    employee: Mapped[EmployeeORM | None] = relationship()


class MonthlyPayrollRunORM(Base):
    """月度正式核算批次，支持复核、锁定和导出。"""

    __tablename__ = "monthly_payroll_runs"
    __table_args__ = {
        "comment": "月度工资核算批次表，记录每月工资计算、复核和锁定状态",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="月度核算批次主键ID")
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True, comment="工资月份，格式YYYY-MM")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="excel", comment="数据来源：excel或dingtalk")
    source_job_id: Mapped[str] = mapped_column(String(32), nullable=False, default="", index=True, comment="来源任务ID，可关联工资任务或同步任务")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft", index=True, comment="核算状态：draft、calculated、reviewed、locked、failed")
    employee_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="参与核算员工数量")
    gross_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="应发工资合计")
    net_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="实发工资合计")
    deduction_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="扣款合计")
    overtime_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="剩余加班工时合计")
    locked_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="锁定人用户名快照")
    locked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, comment="锁定时间")
    created_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="创建人用户名快照")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )


class PayrollExceptionORM(Base):
    """核算异常记录，例如缺员工、缺薪资档案、缺卡、请假无法识别。"""

    __tablename__ = "payroll_exceptions"
    __table_args__ = {
        "comment": "工资核算异常表，保存月度核算或实时同步过程中发现的待处理问题",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="异常记录主键ID")
    run_id: Mapped[int | None] = mapped_column(
        ForeignKey("monthly_payroll_runs.id", ondelete="CASCADE"),
        nullable=True,
        index=True,
        comment="关联月度核算批次ID",
    )
    sync_job_id: Mapped[str | None] = mapped_column(
        ForeignKey("attendance_sync_jobs.id", ondelete="CASCADE"),
        nullable=True,
        index=True,
        comment="关联考勤同步任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, default="", index=True, comment="员工姓名快照")
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份，格式YYYY-MM")
    exception_type: Mapped[str] = mapped_column(String(64), nullable=False, index=True, comment="异常类型编码")
    severity: Mapped[str] = mapped_column(String(32), nullable=False, default="warning", index=True, comment="严重级别：info、warning、error")
    message: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="异常说明")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="open", index=True, comment="处理状态：open待处理、resolved已处理、ignored已忽略")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    resolved_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, comment="处理完成时间")

    run: Mapped[MonthlyPayrollRunORM | None] = relationship()
    sync_job: Mapped[AttendanceSyncJobORM | None] = relationship()
    employee: Mapped[EmployeeORM | None] = relationship()


class CommissionRecordORM(Base):
    """月度提成或奖金记录，支持手工维护和 Excel 导入。"""

    __tablename__ = "commission_record"
    __table_args__ = {
        "comment": "提成记录表，保存销售提成、项目奖金、绩效奖励等月度金额",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="提成记录主键ID")
    employee_id: Mapped[int] = mapped_column(
        ForeignKey("employees.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="关联员工ID",
    )
    commission_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True, comment="提成月份，格式YYYY-MM")
    commission_type: Mapped[str] = mapped_column(String(64), nullable=False, default="销售提成", comment="提成类型")
    amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="提成金额")
    source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="manual", comment="来源：manual手工、excel导入")
    business_ref: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="业务单号或来源标识")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    employee: Mapped[EmployeeORM] = relationship()


class OperationLogORM(Base):
    """系统操作日志，用于审计用户在后台执行的关键动作。"""

    __tablename__ = "operation_logs"
    __table_args__ = {
        "comment": "系统操作日志表，记录用户登录、资料维护、工资计算、下载和系统管理动作",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="日志主键ID")
    user_id: Mapped[int | None] = mapped_column(
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="操作用户ID，匿名或用户删除后为空",
    )
    username: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        default="",
        index=True,
        comment="操作用户名快照",
    )
    role: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="",
        comment="操作用户角色快照",
    )
    module: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        index=True,
        comment="业务模块，例如auth、payroll、system",
    )
    action: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        index=True,
        comment="操作动作编码，例如login、payroll.excel.calculate",
    )
    target_type: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        default="",
        comment="操作对象类型，例如user、payroll_job、file",
    )
    target_id: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="操作对象ID或业务标识",
    )
    status: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        index=True,
        comment="操作状态：success或failed",
    )
    detail: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="操作详情或失败原因")
    ip_address: Mapped[str] = mapped_column(
        String(64),
        nullable=False,
        default="",
        comment="客户端IP地址",
    )
    user_agent: Mapped[str] = mapped_column(
        String(512),
        nullable=False,
        default="",
        comment="客户端浏览器User-Agent",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        index=True,
        comment="操作时间",
    )

    user: Mapped[UserORM | None] = relationship()


class PayrollJobORM(Base):
    """一次工资计算任务，记录来源、状态、输入输出文件。"""

    __tablename__ = "payroll_jobs"
    __table_args__ = {
        "comment": "工资计算任务表，记录每次Excel或钉钉实时计算任务",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[str] = mapped_column(String(32), primary_key=True, comment="任务ID，UUID十六进制字符串")
    source_type: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        comment="任务来源：excel或dingtalk",
    )
    status: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        default="created",
        comment="任务状态：running、completed、failed",
    )
    input_file: Mapped[str | None] = mapped_column(Text, comment="上传的原始Excel文件路径")
    output_file: Mapped[str | None] = mapped_column(Text, comment="导出的工资结果Excel文件路径")
    employee_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="本次任务计算的员工数量",
    )
    error_message: Mapped[str | None] = mapped_column(Text, comment="任务失败时的错误信息")
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        comment="创建时间",
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    results: Mapped[list["PayrollResultORM"]] = relationship(
        back_populates="job",
        cascade="all, delete-orphan",
    )


class PayrollResultORM(Base):
    """员工级工资计算结果，便于后续查询和审计。"""

    __tablename__ = "payroll_results"
    __table_args__ = {
        "comment": "员工工资计算结果表，保存任务下每个员工的加班、请假、扣款和工资结果",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="结果主键ID")
    job_id: Mapped[str] = mapped_column(
        ForeignKey("payroll_jobs.id"),
        nullable=False,
        index=True,
        comment="所属工资计算任务ID",
    )
    name: Mapped[str] = mapped_column(String(128), nullable=False, comment="员工姓名")
    department: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="部门名称")
    position: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="岗位名称")
    attendance_group: Mapped[str] = mapped_column(
        String(128),
        nullable=False,
        default="",
        comment="考勤组名称",
    )
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份，格式YYYY-MM")
    salary_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="monthly", comment="薪资模式快照")
    attendance_days: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="出勤天数")
    absence_days: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="缺勤天数")
    actual_work_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="实际工作工时")
    punch_overtime_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="根据下班打卡时间推算的加班工时",
    )
    leave_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="请假或调休工时",
    )
    remaining_overtime_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="剩余加班工时：打卡推算加班工时减请假调休工时",
    )
    workday_overtime_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="工作日剩余可计费加班工时",
    )
    weekend_overtime_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="周末剩余可计费加班工时",
    )
    holiday_overtime_hours: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="法定节假日剩余可计费加班工时",
    )
    minor_late_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="5分钟内迟到次数",
    )
    major_late_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="超过5分钟迟到次数",
    )
    penalized_late_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="实际计扣迟到次数",
    )
    late_deduction: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="迟到扣款金额",
    )
    missing_card_count: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        comment="缺卡次数",
    )
    missing_card_deduction: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="缺卡扣款金额",
    )
    total_deduction: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="扣款合计金额",
    )
    base_salary: Mapped[float | None] = mapped_column(Float, comment="员工底薪")
    base_pay: Mapped[float | None] = mapped_column(Float, comment="按薪资模式计算后的基础工资")
    commission_amount: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="手工维护的提成费用",
    )
    overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="加班小时单价",
    )
    weekend_overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="周末加班小时单价",
    )
    holiday_overtime_rate: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="法定节假日加班小时单价",
    )
    overtime_pay: Mapped[float] = mapped_column(
        Float,
        nullable=False,
        default=0,
        comment="加班费金额",
    )
    gross_salary: Mapped[float | None] = mapped_column(Float, comment="应发工资")
    net_salary: Mapped[float | None] = mapped_column(Float, comment="实发工资")
    note: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="备注")

    job: Mapped[PayrollJobORM] = relationship(back_populates="results")


class SalaryRecordORM(Base):
    """月度工资汇总记录，用于工资条、月报表和财务报表查询。"""

    __tablename__ = "salary_record"
    __table_args__ = {
        "comment": "工资记录表，保存员工月度工资汇总结果",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="工资记录主键ID")
    job_id: Mapped[str | None] = mapped_column(
        ForeignKey("payroll_jobs.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联工资计算任务ID",
    )
    employee_id: Mapped[int | None] = mapped_column(
        ForeignKey("employees.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="关联员工ID，未匹配员工时为空",
    )
    employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照")
    employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照")
    department: Mapped[str] = mapped_column(String(128), nullable=False, default="", index=True, comment="部门名称快照")
    salary_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True, comment="工资月份，格式YYYY-MM")
    salary_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="monthly", comment="薪资模式快照")
    base_pay: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="基础工资金额")
    commission_amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="提成金额")
    overtime_pay: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="加班费金额")
    deduction_amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="扣款合计金额")
    gross_salary: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="应发工资")
    net_salary: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="实发工资")
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="calculated", comment="状态：calculated已计算")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )

    details: Mapped[list["SalaryDetailORM"]] = relationship(
        back_populates="salary_record",
        cascade="all, delete-orphan",
    )


class SalaryDetailORM(Base):
    """工资明细记录，按工资项拆分基础工资、提成、加班费和扣款。"""

    __tablename__ = "salary_detail"
    __table_args__ = {
        "comment": "工资明细表，保存工资记录下的每个工资项",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="工资明细主键ID")
    salary_record_id: Mapped[int] = mapped_column(
        ForeignKey("salary_record.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="关联工资记录ID",
    )
    item_type: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        index=True,
        comment="工资项类型：earning收入、deduction扣款、stat统计",
    )
    item_name: Mapped[str] = mapped_column(String(64), nullable=False, comment="工资项名称")
    amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="金额")
    quantity: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="数量或工时")
    unit_price: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="单价")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="备注")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")

    salary_record: Mapped[SalaryRecordORM] = relationship(back_populates="details")


class SalaryConfigORM(Base):
    """系统配置中心，保存考勤规则、薪资规则和后续集成参数。"""

    __tablename__ = "salary_config"
    __table_args__ = {
        "comment": "系统配置表，保存可后台维护的考勤、薪资和集成规则",
        "mysql_charset": "utf8mb4",
        "mysql_collate": "utf8mb4_unicode_ci",
    }

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="配置主键ID")
    config_key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True, index=True, comment="配置键")
    config_name: Mapped[str] = mapped_column(String(128), nullable=False, comment="配置名称")
    config_group: Mapped[str] = mapped_column(String(64), nullable=False, index=True, comment="配置分组")
    config_value: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="配置值，按value_type解析")
    value_type: Mapped[str] = mapped_column(String(32), nullable=False, default="string", comment="值类型：string、number、integer、boolean、json")
    is_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, comment="是否启用")
    remark: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="配置说明")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=now_shanghai, comment="创建时间")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        default=now_shanghai,
        onupdate=now_shanghai,
        comment="更新时间",
    )
