from __future__ import annotations

import json
from dataclasses import dataclass

from ..core.config import AppConfig, AttendanceRules, PayrollRules
from ..core.logger import AppLogger
from ..database.orm import SalaryConfigORM
from ..repositories import SalaryConfigRepository

logger = AppLogger.get_logger(__name__)


DEFAULT_CONFIGS = [
    {
        "config_key": "attendance.on_work_time",
        "config_name": "上班时间",
        "config_group": "attendance",
        "config_value": "08:30",
        "value_type": "string",
        "remark": "用于迟到识别，格式HH:MM",
    },
    {
        "config_key": "attendance.off_work_time",
        "config_name": "下班时间",
        "config_group": "attendance",
        "config_value": "17:00",
        "value_type": "string",
        "remark": "加班按下班打卡超过该时间后向下取整",
    },
    {
        "config_key": "attendance.overtime_round_minutes",
        "config_name": "加班取整分钟",
        "config_group": "attendance",
        "config_value": "60",
        "value_type": "integer",
        "remark": "例如60表示20:30按3小时计算",
    },
    {
        "config_key": "attendance.standard_daily_hours",
        "config_name": "标准日工时",
        "config_group": "attendance",
        "config_value": "8",
        "value_type": "number",
        "remark": "计时工资按出勤天数折算工时",
    },
    {
        "config_key": "attendance.minor_late_minutes",
        "config_name": "轻微迟到分钟",
        "config_group": "attendance",
        "config_value": "5",
        "value_type": "integer",
        "remark": "小于等于该分钟数按轻微迟到统计",
    },
    {
        "config_key": "attendance.minor_late_free_times",
        "config_name": "轻微迟到免扣次数",
        "config_group": "attendance",
        "config_value": "3",
        "value_type": "integer",
        "remark": "5分钟内迟到3次内合格，超过后按次扣款",
    },
    {
        "config_key": "attendance.late_penalty",
        "config_name": "迟到扣款",
        "config_group": "attendance",
        "config_value": "30",
        "value_type": "number",
        "remark": "每次计扣迟到扣款金额",
    },
    {
        "config_key": "attendance.missing_card_penalty",
        "config_name": "缺卡扣款",
        "config_group": "attendance",
        "config_value": "20",
        "value_type": "number",
        "remark": "每次缺卡扣款金额",
    },
    {
        "config_key": "attendance.weekend_days",
        "config_name": "周末星期",
        "config_group": "attendance",
        "config_value": "[5, 6]",
        "value_type": "json",
        "remark": "Python weekday：周一0，周六5，周日6",
    },
    {
        "config_key": "attendance.legal_holidays",
        "config_name": "法定节假日",
        "config_group": "attendance",
        "config_value": "[]",
        "value_type": "json",
        "remark": "日期数组，例如[\"2026-10-01\"]",
    },
    {
        "config_key": "attendance.leave_keywords",
        "config_name": "请假关键字",
        "config_group": "attendance",
        "config_value": json.dumps(list(AttendanceRules.leave_keywords), ensure_ascii=False),
        "value_type": "json",
        "remark": "从钉钉单元格识别请假/调休类型",
    },
    {
        "config_key": "payroll.default_overtime_rate",
        "config_name": "工作日加班单价",
        "config_group": "payroll",
        "config_value": "0",
        "value_type": "number",
        "remark": "未维护员工单价时使用",
    },
    {
        "config_key": "payroll.default_weekend_overtime_rate",
        "config_name": "周末加班单价",
        "config_group": "payroll",
        "config_value": "0",
        "value_type": "number",
        "remark": "未维护员工周末单价时使用",
    },
    {
        "config_key": "payroll.default_holiday_overtime_rate",
        "config_name": "节假日加班单价",
        "config_group": "payroll",
        "config_value": "0",
        "value_type": "number",
        "remark": "未维护员工节假日单价时使用",
    },
]


@dataclass(frozen=True)
class SalaryConfigUpdate:
    config_name: str
    config_group: str
    config_value: str
    value_type: str
    is_enabled: bool
    remark: str


class SalaryConfigService:
    """系统配置服务：维护规则配置，并把数据库配置合成为 AppConfig。"""

    def __init__(self, repository: SalaryConfigRepository):
        self.repository = repository

    def ensure_defaults(self) -> int:
        created = self.repository.create_defaults(DEFAULT_CONFIGS)
        logger.info("系统默认配置初始化完成 created=%s", created)
        return created

    def list_configs(self, *, config_group: str | None = None) -> list[SalaryConfigORM]:
        configs = self.repository.list_configs(config_group=_clean_optional(config_group))
        logger.info("查询系统配置成功 count=%s group=%s", len(configs), config_group)
        return configs

    def update_config(self, config_key: str, values: SalaryConfigUpdate) -> SalaryConfigORM:
        self._parse_value(values.config_value, values.value_type)
        config = self.repository.upsert_config(
            config_key=config_key,
            config_name=values.config_name.strip(),
            config_group=values.config_group.strip(),
            config_value=values.config_value.strip(),
            value_type=values.value_type.strip(),
            is_enabled=values.is_enabled,
            remark=values.remark.strip(),
        )
        logger.info("更新系统配置成功 config_key=%s", config_key)
        return config

    def resolve_app_config(self, base_config: AppConfig) -> AppConfig:
        values = {
            config.config_key: self._parse_value(config.config_value, config.value_type)
            for config in self.repository.list_configs()
            if config.is_enabled
        }
        attendance = AttendanceRules(
            on_work_time=str(values.get("attendance.on_work_time", base_config.attendance.on_work_time)),
            off_work_time=str(values.get("attendance.off_work_time", base_config.attendance.off_work_time)),
            overtime_round_minutes=int(
                values.get("attendance.overtime_round_minutes", base_config.attendance.overtime_round_minutes)
            ),
            standard_daily_hours=float(
                values.get("attendance.standard_daily_hours", base_config.attendance.standard_daily_hours)
            ),
            minor_late_minutes=int(values.get("attendance.minor_late_minutes", base_config.attendance.minor_late_minutes)),
            minor_late_free_times=int(
                values.get("attendance.minor_late_free_times", base_config.attendance.minor_late_free_times)
            ),
            late_penalty=float(values.get("attendance.late_penalty", base_config.attendance.late_penalty)),
            missing_card_penalty=float(
                values.get("attendance.missing_card_penalty", base_config.attendance.missing_card_penalty)
            ),
            weekend_days=tuple(int(item) for item in values.get("attendance.weekend_days", base_config.attendance.weekend_days)),
            legal_holidays=tuple(str(item) for item in values.get("attendance.legal_holidays", base_config.attendance.legal_holidays)),
            leave_keywords=tuple(str(item) for item in values.get("attendance.leave_keywords", base_config.attendance.leave_keywords)),
        )
        payroll = PayrollRules(
            default_overtime_rate=float(
                values.get("payroll.default_overtime_rate", base_config.payroll.default_overtime_rate)
            ),
            default_weekend_overtime_rate=float(
                values.get("payroll.default_weekend_overtime_rate", base_config.payroll.default_weekend_overtime_rate)
            ),
            default_holiday_overtime_rate=float(
                values.get("payroll.default_holiday_overtime_rate", base_config.payroll.default_holiday_overtime_rate)
            ),
            employees=base_config.payroll.employees,
        )
        logger.info("已合并数据库规则配置到运行时计算配置")
        return AppConfig(attendance=attendance, payroll=payroll)

    def _parse_value(self, raw_value: str, value_type: str):
        if value_type == "integer":
            return int(raw_value)
        if value_type == "number":
            return float(raw_value)
        if value_type == "boolean":
            return str(raw_value).lower() in {"1", "true", "yes", "on"}
        if value_type == "json":
            return json.loads(raw_value or "null")
        return raw_value


def _clean_optional(value: str | None) -> str | None:
    if value is None:
        return None
    stripped = value.strip()
    return stripped or None
