package xiaoqu.home.open.constant;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 业务规则注册表.
 *
 * 所有核心业务规则在此集中定义，作为系统业务逻辑的"单一事实来源"。
 *
 * [PROTECTED] 此文件为红区文件，AI 不可直接修改。
 * 修改需审批人：项目经理 + 技术负责人。
 *
 * 使用方式：
 * 1. 业务代码中引用此处常量，禁止硬编码业务规则值
 * 2. 系统启动时调用 validateAll() 校验规则完整性
 * 3. 新增业务规则须同步添加校验方法和单元测试
 *
 * @author SmartClean Team
 * @since 2026-04-15
 */
public final class BusinessRules {

	private BusinessRules() {
		throw new UnsupportedOperationException("常量类不可实例化");
	}

	// =====================================================================
	// 一、工资计算规则 (BR-SALARY)
	// =====================================================================

	/** BR-SALARY-001: 法定月计薪天数（劳动法规定） */
	public static final BigDecimal MONTHLY_WORK_DAYS = new BigDecimal("21.75");

	/** BR-SALARY-002: 平日加班费率（1.5 倍） */
	public static final BigDecimal WEEKDAY_OVERTIME_RATE = new BigDecimal("1.5");

	/** BR-SALARY-003: 周末加班费率（2.0 倍） */
	public static final BigDecimal WEEKEND_OVERTIME_RATE = new BigDecimal("2.0");

	/** BR-SALARY-004: 法定假日加班费率（3.0 倍） */
	public static final BigDecimal HOLIDAY_OVERTIME_RATE = new BigDecimal("3.0");

	/** BR-SALARY-005: 标准日工作时长（小时） */
	public static final BigDecimal STANDARD_DAILY_WORK_HOURS = new BigDecimal("8");

	/** BR-SALARY-006: 工资金额计算精度（小数位数） */
	public static final int SALARY_SCALE = 2;

	/** BR-SALARY-007: 工资金额舍入模式（四舍五入） */
	public static final RoundingMode SALARY_ROUNDING = RoundingMode.HALF_UP;

	/** BR-SALARY-008: 最低工资标准（元/月），按当地政策设定，需定期更新 */
	public static final BigDecimal MINIMUM_MONTHLY_WAGE = new BigDecimal("2690.00");

	// =====================================================================
	// 二、打卡考勤规则 (BR-ATTEND)
	// =====================================================================

	/** BR-ATTEND-001: 打卡地理围栏默认半径（米） */
	public static final int DEFAULT_FENCE_RADIUS_METERS = 200;

	/** BR-ATTEND-002: 防重复打卡时间窗口（分钟），同类型打卡在此时间内不可重复 */
	public static final int DUPLICATE_PUNCH_WINDOW_MINUTES = 30;

	/** BR-ATTEND-003: 迟到判定阈值（分钟），超过排班开始时间此值算迟到 */
	public static final int LATE_THRESHOLD_MINUTES = 15;

	/** BR-ATTEND-004: 早退判定阈值（分钟），早于排班结束时间此值算早退 */
	public static final int EARLY_LEAVE_THRESHOLD_MINUTES = 15;

	/** BR-ATTEND-005: 严重迟到阈值（分钟），超过此值算严重迟到 */
	public static final int SEVERE_LATE_THRESHOLD_MINUTES = 60;

	/** BR-ATTEND-006: 旷工判定阈值（分钟），超过此值未打卡算旷工 */
	public static final int ABSENT_THRESHOLD_MINUTES = 120;

	/** BR-ATTEND-007: GPS 定位超时时间（秒） */
	public static final int GPS_TIMEOUT_SECONDS = 15;

	/** BR-ATTEND-008: WiFi 辅助定位允许的最大偏差（米） */
	public static final int WIFI_LOCATION_MAX_DEVIATION_METERS = 100;

	/** BR-ATTEND-009: 只允许本人打卡，不可代打 */
	public static final boolean SELF_PUNCH_ONLY = true;

	/** BR-ATTEND-010: 打卡记录保留期限（天），超过此期限的记录可归档 */
	public static final int PUNCH_RECORD_RETENTION_DAYS = 365;

	// =====================================================================
	// 三、排班规则 (BR-SCHED)
	// =====================================================================

	/** BR-SCHED-001: 单人单日最大排班时长（小时） */
	public static final int MAX_DAILY_SCHEDULE_HOURS = 12;

	/** BR-SCHED-002: 两个排班之间最小休息时间（小时） */
	public static final int MIN_REST_HOURS_BETWEEN_SHIFTS = 8;

	/** BR-SCHED-003: 排班交接重叠容差（分钟），此范围内不算冲突 */
	public static final int SCHEDULE_HANDOVER_TOLERANCE_MINUTES = 0;

	/** BR-SCHED-004: 排班提前发布天数，排班表至少提前此天数发布 */
	public static final int SCHEDULE_PUBLISH_ADVANCE_DAYS = 3;

	/** BR-SCHED-005: 单人单周最大排班天数 */
	public static final int MAX_WEEKLY_SCHEDULE_DAYS = 6;

	/** BR-SCHED-006: 夜班定义起始时间（24 小时制） */
	public static final int NIGHT_SHIFT_START_HOUR = 22;

	/** BR-SCHED-007: 夜班定义结束时间（24 小时制） */
	public static final int NIGHT_SHIFT_END_HOUR = 6;

	// =====================================================================
	// 四、任务管理规则 (BR-TASK)
	// =====================================================================

	/** BR-TASK-001: 任务超时告警阈值（分钟），超过预计时长此值后触发告警 */
	public static final int TASK_OVERTIME_ALERT_MINUTES = 30;

	/** BR-TASK-002: 任务照片最少数量，完成任务时至少需上传此数量的照片 */
	public static final int TASK_MIN_PHOTO_COUNT = 1;

	/** BR-TASK-003: 任务照片最大数量 */
	public static final int TASK_MAX_PHOTO_COUNT = 9;

	/** BR-TASK-004: 任务评分满分 */
	public static final int TASK_MAX_SCORE = 100;

	/** BR-TASK-005: 任务合格分数线 */
	public static final int TASK_PASS_SCORE = 60;

	// =====================================================================
	// 五、巡检规则 (BR-PATROL)
	// =====================================================================

	/** BR-PATROL-001: 巡检点最小停留时间（秒），少于此时间判定为无效巡检 */
	public static final int PATROL_MIN_STAY_SECONDS = 30;

	/** BR-PATROL-002: 巡检轨迹记录间隔（秒） */
	public static final int PATROL_TRACK_INTERVAL_SECONDS = 10;

	/** BR-PATROL-003: 巡检异常自动上报，发现异常时自动生成工单 */
	public static final boolean PATROL_AUTO_REPORT_ANOMALY = true;

	// =====================================================================
	// 六、系统通用规则 (BR-SYS)
	// =====================================================================

	/** BR-SYS-001: 分页查询每页最大条数 */
	public static final int MAX_PAGE_SIZE = 100;

	/** BR-SYS-002: 分页查询默认每页条数 */
	public static final int DEFAULT_PAGE_SIZE = 20;

	/** BR-SYS-003: 数据删除方式 — 逻辑删除标记值（0=未删除, 1=已删除） */
	public static final int DELETED_FLAG = 1;
	public static final int NOT_DELETED_FLAG = 0;

	/** BR-SYS-004: 批量操作最大条数 */
	public static final int MAX_BATCH_SIZE = 500;

	/** BR-SYS-005: 导出 Excel 最大行数 */
	public static final int MAX_EXPORT_ROWS = 10000;

	/** BR-SYS-006: 文件上传最大大小（MB） */
	public static final int MAX_UPLOAD_SIZE_MB = 20;

	/** BR-SYS-007: 允许上传的图片格式 */
	public static final Set<String> ALLOWED_IMAGE_FORMATS = Collections.unmodifiableSet(
			new HashSet<>(Arrays.asList("jpg", "jpeg", "png", "bmp", "gif")));

	// =====================================================================
	// 七、龙虾助手/对话交互规则 (BR-CHAT)
	// =====================================================================

	/** BR-CHAT-001: 写操作需要二次确认 */
	public static final boolean WRITE_OPERATION_REQUIRES_CONFIRM = true;

	/** BR-CHAT-002: 闲聊不触发任何数据库操作 */
	public static final boolean CHITCHAT_NO_DB_OPERATION = true;

	/** BR-CHAT-003: 危险操作（删除/修改工资/修改权限）通过对话禁止执行 */
	public static final boolean DANGEROUS_OPERATION_BLOCKED_VIA_CHAT = true;

	// =====================================================================
	// 规则校验方法
	// =====================================================================

	/**
	 * 校验所有业务规则完整性.
	 * 建议在系统启动时调用，确保核心常量未被篡改。
	 *
	 * @throws AssertionError 如果任何规则被篡改
	 */
	public static void validateAll() {
		validateSalaryRules();
		validateAttendanceRules();
		validateScheduleRules();
		validateSystemRules();
	}

	/**
	 * 校验工资计算规则.
	 */
	public static void validateSalaryRules() {
		assertRule(MONTHLY_WORK_DAYS.compareTo(new BigDecimal("21.75")) == 0,
				"BR-SALARY-001", "月计薪天数", "21.75", MONTHLY_WORK_DAYS.toString());
		assertRule(WEEKDAY_OVERTIME_RATE.compareTo(new BigDecimal("1.5")) == 0,
				"BR-SALARY-002", "平日加班费率", "1.5", WEEKDAY_OVERTIME_RATE.toString());
		assertRule(WEEKEND_OVERTIME_RATE.compareTo(new BigDecimal("2.0")) == 0,
				"BR-SALARY-003", "周末加班费率", "2.0", WEEKEND_OVERTIME_RATE.toString());
		assertRule(HOLIDAY_OVERTIME_RATE.compareTo(new BigDecimal("3.0")) == 0,
				"BR-SALARY-004", "法定假日加班费率", "3.0", HOLIDAY_OVERTIME_RATE.toString());
		assertRule(STANDARD_DAILY_WORK_HOURS.compareTo(new BigDecimal("8")) == 0,
				"BR-SALARY-005", "标准日工作时长", "8", STANDARD_DAILY_WORK_HOURS.toString());
		assertRule(SALARY_SCALE == 2,
				"BR-SALARY-006", "工资精度", "2", String.valueOf(SALARY_SCALE));
		assertRule(SALARY_ROUNDING == RoundingMode.HALF_UP,
				"BR-SALARY-007", "舍入模式", "HALF_UP", SALARY_ROUNDING.toString());
	}

	/**
	 * 校验打卡考勤规则.
	 */
	public static void validateAttendanceRules() {
		assertRule(DEFAULT_FENCE_RADIUS_METERS == 200,
				"BR-ATTEND-001", "围栏半径", "200", String.valueOf(DEFAULT_FENCE_RADIUS_METERS));
		assertRule(DUPLICATE_PUNCH_WINDOW_MINUTES == 30,
				"BR-ATTEND-002", "防重窗口", "30", String.valueOf(DUPLICATE_PUNCH_WINDOW_MINUTES));
		assertRule(LATE_THRESHOLD_MINUTES == 15,
				"BR-ATTEND-003", "迟到阈值", "15", String.valueOf(LATE_THRESHOLD_MINUTES));
		assertRule(EARLY_LEAVE_THRESHOLD_MINUTES == 15,
				"BR-ATTEND-004", "早退阈值", "15", String.valueOf(EARLY_LEAVE_THRESHOLD_MINUTES));
		assertRule(SELF_PUNCH_ONLY,
				"BR-ATTEND-009", "本人打卡", "true", String.valueOf(SELF_PUNCH_ONLY));
	}

	/**
	 * 校验排班规则.
	 */
	public static void validateScheduleRules() {
		assertRule(MAX_DAILY_SCHEDULE_HOURS == 12,
				"BR-SCHED-001", "单日最大排班时长", "12", String.valueOf(MAX_DAILY_SCHEDULE_HOURS));
		assertRule(MIN_REST_HOURS_BETWEEN_SHIFTS == 8,
				"BR-SCHED-002", "最小休息时间", "8", String.valueOf(MIN_REST_HOURS_BETWEEN_SHIFTS));
		assertRule(MAX_WEEKLY_SCHEDULE_DAYS == 6,
				"BR-SCHED-005", "单周最大排班天数", "6", String.valueOf(MAX_WEEKLY_SCHEDULE_DAYS));
	}

	/**
	 * 校验系统通用规则.
	 */
	public static void validateSystemRules() {
		assertRule(MAX_PAGE_SIZE == 100,
				"BR-SYS-001", "最大分页条数", "100", String.valueOf(MAX_PAGE_SIZE));
		assertRule(DELETED_FLAG == 1,
				"BR-SYS-003", "逻辑删除标记", "1", String.valueOf(DELETED_FLAG));
		assertRule(NOT_DELETED_FLAG == 0,
				"BR-SYS-003", "未删除标记", "0", String.valueOf(NOT_DELETED_FLAG));
		assertRule(MAX_BATCH_SIZE == 500,
				"BR-SYS-004", "批量操作上限", "500", String.valueOf(MAX_BATCH_SIZE));
	}

	/**
	 * 断言业务规则.
	 */
	private static void assertRule(boolean condition, String ruleId, String ruleName,
			String expectedValue, String actualValue) {
		if (!condition) {
			throw new AssertionError(String.format(
					"业务规则被篡改！[%s] %s: 期望=%s, 实际=%s",
					ruleId, ruleName, expectedValue, actualValue));
		}
	}
}
