/// 加班申请主表，对应 OA_OVERTIME 表。
class OvertimeApplyModel {
  final String jbNo; // JB_NO 加班单号（主键）
  final DateTime? jbDd; // JB_DD 申请日期
  final String salNo; // SAL_NO 申请人
  final String salName; // SAL_NAME 申请人姓名（瞬态）
  final String dep; // DEP 所属部门
  final String depName; // DEP_NAME 部门名称（瞬态）
  final String reason; // REASON 加班原因
  final String rem; // REM 备注
  final String usr; // USR 录入人
  final DateTime? recordDd; // RECORD_DD 录入日期
  final String chkMan; // CHK_MAN 审核人
  final DateTime? clsDate; // CLS_DATE 终审日期
  final bool isAuditApproved; // 瞬态：审批状态
  final double totalJbHours; // 明细加班时长合计（列表API返回）

  // ── 子表 ──
  final List<OvertimeApplyDetailModel> details;

  const OvertimeApplyModel({
    required this.jbNo,
    this.jbDd,
    this.salNo = '',
    this.salName = '',
    this.dep = '',
    this.depName = '',
    this.reason = '',
    this.rem = '',
    this.usr = '',
    this.recordDd,
    this.chkMan = '',
    this.clsDate,
    this.isAuditApproved = false,
    this.totalJbHours = 0,
    this.details = const [],
  });

  factory OvertimeApplyModel.fromJson(Map<String, dynamic> json) {
    return OvertimeApplyModel(
      jbNo: json['JB_NO'] as String? ?? '',
      jbDd: json['JB_DD'] != null
          ? DateTime.tryParse(json['JB_DD'] as String)
          : null,
      salNo: json['SAL_NO'] as String? ?? '',
      salName: json['SAL_NAME'] as String? ?? json['NAME'] as String? ?? '',
      dep: json['DEP'] as String? ?? '',
      depName: json['DEP_NAME'] as String? ?? '',
      reason: json['REASON'] as String? ?? '',
      rem: json['REM'] as String? ?? '',
      usr: json['USR'] as String? ?? '',
      recordDd: json['RECORD_DD'] != null
          ? DateTime.tryParse(json['RECORD_DD'] as String)
          : null,
      chkMan: json['CHK_MAN'] as String? ?? '',
      clsDate: json['CLS_DATE'] != null
          ? DateTime.tryParse(json['CLS_DATE'] as String)
          : null,
      isAuditApproved: (json['isAuditApproved'] as int?) == 1,
      totalJbHours: (json['TotalJbHours'] as num?)?.toDouble() ??
          (json['totalJbHours'] as num?)?.toDouble() ??
          0,
      details: _parseDetails(json['Details'] ?? json['details']),
    );
  }

  Map<String, dynamic> toJson() => {
    'JB_NO': jbNo,
    'JB_DD': jbDd?.toIso8601String(),
    'SAL_NO': salNo,
    'SAL_NAME': salName,
    'DEP': dep,
    'DEP_NAME': depName,
    'REASON': reason,
    'REM': rem,
    'USR': usr,
    'RECORD_DD': recordDd?.toIso8601String(),
    'CHK_MAN': chkMan,
    'CLS_DATE': clsDate?.toIso8601String(),
    'isAuditApproved': isAuditApproved ? 1 : 0,
    'details': details.map((d) => d.toJson()).toList(),
  };

  static List<OvertimeApplyDetailModel> _parseDetails(dynamic data) {
    if (data == null) return [];
    if (data is List) {
      return data
          .map((e) =>
              OvertimeApplyDetailModel.fromJson(e as Map<String, dynamic>))
          .toList();
    }
    return [];
  }

  OvertimeApplyModel copyWith({
    String? jbNo,
    DateTime? jbDd,
    String? salNo,
    String? salName,
    String? dep,
    String? depName,
    String? reason,
    String? rem,
    String? usr,
    DateTime? recordDd,
    String? chkMan,
    DateTime? clsDate,
    bool? isAuditApproved,
    List<OvertimeApplyDetailModel>? details,
  }) {
    return OvertimeApplyModel(
      jbNo: jbNo ?? this.jbNo,
      jbDd: jbDd ?? this.jbDd,
      salNo: salNo ?? this.salNo,
      salName: salName ?? this.salName,
      dep: dep ?? this.dep,
      depName: depName ?? this.depName,
      reason: reason ?? this.reason,
      rem: rem ?? this.rem,
      usr: usr ?? this.usr,
      recordDd: recordDd ?? this.recordDd,
      chkMan: chkMan ?? this.chkMan,
      clsDate: clsDate ?? this.clsDate,
      isAuditApproved: isAuditApproved ?? this.isAuditApproved,
      details: details ?? this.details,
    );
  }
}

/// 加班明细，对应 OA_OVERTIME_DETAIL 表。
class OvertimeApplyDetailModel {
  final int itm; // ITM 项次
  final String? jbNo; // JB_NO 加班单号
  final String salNo; // SAL_NO 员工代号
  final String salName; // 员工姓名（瞬态）
  final String dep; // DEP 部门
  final String depName; // DEP_NAME 部门名称（瞬态）
  final String
  jbType; // JB_TYPE: WORKING_DAY/REST_DAY/PUBLIC_HOLIDAY/SPECIAL_HOLIDAY/OTHER
  final DateTime? jbDate; // JB_DATE 加班日期
  final DateTime? startTime; // START_TIME 开始时间
  final DateTime? endTime; // END_TIME 结束时间
  final double jbHours; // JB_HOURS 加班时长
  final double jbDays; // JB_DAYS 加班天数
  final String attPeriod; // ATT_PERIOD 考勤周期 yyyy-MM
  final String reason; // REASON 事由
  final String
  compensationType; // COMPENSATION_TYPE: OVERTIME_PAY/COMPENSATORY_LEAVE/NO_COMPENSATION/OTHER
  final double compensationCount; // COMPENSATION_COUNT 折算补偿次数
  final String adr; // ADR 地点
  final String rem; // REM 备注
  final int dayOfWeek; // DAY_OF_WEEK 星期几标记位（0=无, 1=日~7=六）

  const OvertimeApplyDetailModel({
    this.itm = 0,
    this.jbNo = '',
    this.salNo = '',
    this.salName = '',
    this.dep = '',
    this.depName = '',
    this.jbType = 'WORKING_DAY',
    this.jbDate,
    this.startTime,
    this.endTime,
    this.jbHours = 0.0,
    this.jbDays = 0.0,
    this.attPeriod = '',
    this.reason = '',
    this.compensationType = 'OVERTIME_PAY',
    this.compensationCount = 0.0,
    this.adr = '',
    this.rem = '',
    this.dayOfWeek = 0,
  });

  factory OvertimeApplyDetailModel.fromJson(Map<String, dynamic> json) {
    return OvertimeApplyDetailModel(
      jbNo: json['JB_NO'] as String?,
      itm: json['ITM'] as int? ?? 0,
      salNo: json['SAL_NO'] as String? ?? '',
      salName: json['SAL_NAME'] as String? ?? '',
      dep: json['DEP'] as String? ?? '',
      depName: json['DEP_NAME'] as String? ?? '',
      jbType: json['JB_TYPE'] as String? ?? 'WORKING_DAY',
      jbDate: json['JB_DATE'] != null
          ? DateTime.tryParse(json['JB_DATE'] as String)
          : null,
      startTime: json['START_TIME'] != null
          ? DateTime.tryParse(json['START_TIME'] as String)
          : null,
      endTime: json['END_TIME'] != null
          ? DateTime.tryParse(json['END_TIME'] as String)
          : null,
      jbHours: (json['JB_HOURS'] as num?)?.toDouble() ?? 0.0,
      jbDays: (json['JB_DAYS'] as num?)?.toDouble() ?? 0.0,
      attPeriod: json['ATT_PERIOD'] as String? ?? '',
      reason: json['REASON'] as String? ?? '',
      compensationType: json['COMPENSATION_TYPE'] as String? ?? 'OVERTIME_PAY',
      compensationCount:
          (json['COMPENSATION_COUNT'] as num?)?.toDouble() ?? 0.0,
      adr: json['ADR'] as String? ?? '',
      rem: json['REM'] as String? ?? '',
      dayOfWeek: json['DAY_OF_WEEK'] is int
          ? json['DAY_OF_WEEK'] as int
          : int.tryParse(
                  (json['DAY_OF_WEEK'] ?? json['dayOfWeek'])?.toString() ??
                      '') ??
              0,
    );
  }

  Map<String, dynamic> toJson() => {
    'JB_NO': jbNo,
    'ITM': itm,
    'SAL_NO': salNo,
    'SAL_NAME': salName,
    'DEP': dep,
    'DEP_NAME': depName,
    'JB_TYPE': jbType,
    'JB_DATE': jbDate?.toIso8601String(),
    'START_TIME': startTime?.toIso8601String(),
    'END_TIME': endTime?.toIso8601String(),
    'JB_HOURS': jbHours,
    'JB_DAYS': jbDays,
    'ATT_PERIOD': attPeriod,
    'REASON': reason,
    'COMPENSATION_TYPE': compensationType,
    'COMPENSATION_COUNT': compensationCount,
    'ADR': adr,
    'REM': rem,
    'DAY_OF_WEEK': dayOfWeek.toString(),
  };

  OvertimeApplyDetailModel copyWith({
    int? itm,
    String? jbNo,
    String? salNo,
    String? salName,
    String? dep,
    String? depName,
    String? jbType,
    DateTime? jbDate,
    DateTime? startTime,
    DateTime? endTime,
    double? jbHours,
    double? jbDays,
    String? attPeriod,
    String? reason,
    String? compensationType,
    double? compensationCount,
    String? adr,
    String? rem,
    int? dayOfWeek,
  }) {
    return OvertimeApplyDetailModel(
      itm: itm ?? this.itm,
      jbNo: jbNo ?? this.jbNo,
      salNo: salNo ?? this.salNo,
      salName: salName ?? this.salName,
      dep: dep ?? this.dep,
      depName: depName ?? this.depName,
      jbType: jbType ?? this.jbType,
      jbDate: jbDate ?? this.jbDate,
      startTime: startTime ?? this.startTime,
      endTime: endTime ?? this.endTime,
      jbHours: jbHours ?? this.jbHours,
      jbDays: jbDays ?? this.jbDays,
      attPeriod: attPeriod ?? this.attPeriod,
      reason: reason ?? this.reason,
      compensationType: compensationType ?? this.compensationType,
      compensationCount: compensationCount ?? this.compensationCount,
      adr: adr ?? this.adr,
      rem: rem ?? this.rem,
      dayOfWeek: dayOfWeek ?? this.dayOfWeek,
    );
  }
}
