// ignore_for_file: use_build_context_synchronously

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../../core/i18n/app_localizations.dart';
import '../../../core/navigation/host_app_channel.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_colors_extension.dart';
import '../../../shared/widgets/searchable_picker_sheet.dart';
import '../../expense_apply/expense_apply_api.dart';
import '../overtime_apply_api.dart';

/// 加班明细输入数据。
class OvertimeDetailData {
  final String? jbNo;
  final int? itm;
  final String salNo;
  final String salName;
  final String dep;
  final String depName;
  final String jbType;
  final String jbDate;
  final String startTime;
  final String endTime;
  final double jbHours;
  final double jbDays;
  final String attPeriod;
  final String reason;
  final String compensationType;
  final double compensationCount;
  final String adr;
  final String rem;

  const OvertimeDetailData({
    this.jbNo,
    this.itm,
    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 = '',
    this.compensationCount = 0.0,
    this.adr = '',
    this.rem = '',
  });
}

/// 添加/编辑加班明细弹窗。
class OvertimeApplyDetailDialog extends StatefulWidget {
  final OvertimeApplyApi api;
  final AppLocalizations l10n;
  final OvertimeDetailData? initialData;

  const OvertimeApplyDetailDialog({
    super.key,
    required this.api,
    required this.l10n,
    this.initialData,
  });

  /// 显示弹窗，返回 [OvertimeDetailData] 或 `null`（取消时）。
  static Future<OvertimeDetailData?> show(
    BuildContext context, {
    required OvertimeApplyApi api,
    required AppLocalizations l10n,
    OvertimeDetailData? initialData,
  }) {
    FocusScope.of(context).unfocus();
    return Navigator.push<OvertimeDetailData>(
      context,
      TDSlidePopupRoute<OvertimeDetailData>(
        slideTransitionFrom: SlideTransitionFrom.bottom,
        isDismissible: true,
        builder: (_) => OvertimeApplyDetailDialog(
          api: api,
          l10n: l10n,
          initialData: initialData,
        ),
      ),
    );
  }

  @override
  State<OvertimeApplyDetailDialog> createState() =>
      _OvertimeApplyDetailDialogState();
}

class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
  // 员工
  EmployeeItem? _selEmployee;
  // 部门
  String _dep = '';
  String _depName = '';
  // 加班类型
  String _jbType = 'WORKING_DAY';
  // 加班日期
  String _jbDate = '';
  // 开始/结束时间
  String _startTime = '';
  String _endTime = '';
  // 加班时长、天数
  final _hoursCtrl = TextEditingController();
  final _hoursFocus = FocusNode();
  final _daysCtrl = TextEditingController();
  final _daysFocus = FocusNode();
  // 考勤周期
  String _attPeriod = '';
  // 事由
  final _reasonCtrl = TextEditingController();
  final _reasonFocus = FocusNode();
  // 补偿类型
  String _compensationType = '';
  // 折算补偿次数
  final _compCountCtrl = TextEditingController();
  final _compCountFocus = FocusNode();
  // 地点
  final _adrCtrl = TextEditingController();
  final _adrFocus = FocusNode();
  // 备注
  final _remarkCtrl = TextEditingController();
  final _remarkFocus = FocusNode();
  // 滚动
  final _scrollCtrl = ScrollController();

  // 加班类型选项
  static const _jbTypeOptions = [
    'WORKING_DAY',
    'REST_DAY',
    'PUBLIC_HOLIDAY',
    'SPECIAL_HOLIDAY',
    'OTHER',
  ];

  // 补偿类型选项
  static const _compensationTypeOptions = [
    'OVERTIME_PAY',
    'COMPENSATORY_LEAVE',
    'NO_COMPENSATION',
    'OTHER',
  ];

  AppLocalizations get _l10n => widget.l10n;

  bool get _isEdit => widget.initialData != null;

  String _todayStr() {
    final now = DateTime.now();
    return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
  }

  @override
  void initState() {
    super.initState();
    final d = widget.initialData;
    _selEmployee = (d != null && d.salNo.isNotEmpty)
        ? EmployeeItem(salNo: d.salNo, name: d.salName)
        : null;
    _dep = d?.dep ?? '';
    _depName = d?.depName ?? '';
    _jbType = d?.jbType ?? 'WORKING_DAY';
    _jbDate = d?.jbDate ?? '';
    if (!_isEdit && _jbDate.isEmpty) {
      final now = DateTime.now();
      _jbDate =
          '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
    }
    _startTime = d?.startTime ?? '';
    _endTime = d?.endTime ?? '';
    // 新增模式：开始/结束时间给默认值
    if (!_isEdit) {
      final dateStr = _jbDate.isNotEmpty ? _jbDate : _todayStr();
      final weekday =
          DateTime.tryParse(dateStr)?.weekday ?? DateTime.now().weekday;
      if (_startTime.isEmpty) {
        final h = (weekday <= DateTime.friday) ? 18 : 9;
        _startTime = '$dateStr ${h.toString().padLeft(2, '0')}:00';
      }
      if (_endTime.isEmpty) {
        final h = (weekday <= DateTime.friday) ? 19 : 18;
        _endTime = '$dateStr ${h.toString().padLeft(2, '0')}:00';
      }
      _attPeriod = dateStr.substring(0, 7);
      _autoCalcHours();
    }
    if (d != null) {
      _hoursCtrl.text = d.jbHours > 0 ? d.jbHours.toString() : '';
      _daysCtrl.text = d.jbDays > 0 ? d.jbDays.toString() : '';
      _attPeriod = d.attPeriod;
    }
    if (d != null) {
      _reasonCtrl.text = d.reason;
    }
    _compensationType = d?.compensationType ?? '';
    if (d != null) {
      _compCountCtrl.text = d.compensationCount > 0
          ? d.compensationCount.toString()
          : '';
    }
    if (d != null) {
      _adrCtrl.text = d.adr;
      _remarkCtrl.text = d.rem;
    }
    _reasonFocus.addListener(() => _ensureVisible(_reasonFocus));
    _hoursFocus.addListener(() {
      _ensureVisible(_hoursFocus);
      if (!_hoursFocus.hasFocus) _roundToHalf(_hoursCtrl);
    });
    _daysFocus.addListener(() {
      _ensureVisible(_daysFocus);
      if (!_daysFocus.hasFocus) _roundToHalf(_daysCtrl);
    });
    _compCountFocus.addListener(() => _ensureVisible(_compCountFocus));
    _adrFocus.addListener(() => _ensureVisible(_adrFocus));
    _remarkFocus.addListener(() => _ensureVisible(_remarkFocus));
    // 非编辑模式自动带出当前用户
    if (!_isEdit) {
      WidgetsBinding.instance.addPostFrameCallback((_) => _doAutoFill());
    }
  }

  void _ensureVisible(FocusNode node) {
    if (!node.hasFocus) return;
    _doEnsureVisible(node, 0, -1);
  }

  void _doEnsureVisible(FocusNode node, int attempt, double lastInsets) {
    if (attempt >= 15) return;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted || !node.hasFocus || !_scrollCtrl.hasClients) return;
      final insets = MediaQuery.of(context).viewInsets.bottom;
      if (insets != lastInsets) {
        _doEnsureVisible(node, attempt + 1, insets);
        return;
      }
      final ctx = node.context;
      if (ctx == null) return;
      Future.delayed(const Duration(milliseconds: 500), () {
        if (!mounted || !node.hasFocus || !_scrollCtrl.hasClients) return;
        Scrollable.ensureVisible(
          ctx,
          alignment: 0.5,
          duration: const Duration(milliseconds: 300),
        );
      });
    });
  }

  Future<void> _doAutoFill() async {
    await HostAppChannel.ensureConfig();
    final usr = HostAppChannel.usr;
    if (usr.isEmpty) return;
    try {
      final employees = await widget.api.getEmployees(salNo: usr);
      if (employees.isNotEmpty && mounted) {
        setState(() {
          _selEmployee = employees.first;
        });
        // 自动带出部门
        final depCode = HostAppChannel.dep;
        if (depCode.isNotEmpty) {
          final depts = await widget.api.getDepartments();
          final matched = depts.where((d) => d.dep == depCode);
          if (matched.isNotEmpty && mounted) {
            setState(() {
              _dep = matched.first.dep;
              _depName = matched.first.name;
            });
          }
        }
      }
    } catch (_) {
      // 静默失败
    }
  }

  @override
  void dispose() {
    _hoursCtrl.dispose();
    _hoursFocus.dispose();
    _daysCtrl.dispose();
    _daysFocus.dispose();
    _reasonCtrl.dispose();
    _reasonFocus.dispose();
    _compCountCtrl.dispose();
    _compCountFocus.dispose();
    _adrCtrl.dispose();
    _adrFocus.dispose();
    _remarkCtrl.dispose();
    _remarkFocus.dispose();
    _scrollCtrl.dispose();
    super.dispose();
  }

  void _confirm() {
    if (_selEmployee == null) {
      TDToast.showText(
        '${_l10n.get('pleaseSelect')}${_l10n.get('employee')}',
        context: context,
      );
      return;
    }
    if (_jbDate.isEmpty) {
      TDToast.showText(
        '${_l10n.get('pleaseSelect')}${_l10n.get('date')}',
        context: context,
      );
      return;
    }
    if (_startTime.isEmpty) {
      TDToast.showText(
        '${_l10n.get('pleaseSelect')}${_l10n.get('startTime')}',
        context: context,
      );
      return;
    }
    if (_endTime.isEmpty) {
      TDToast.showText(
        '${_l10n.get('pleaseSelect')}${_l10n.get('endTime')}',
        context: context,
      );
      return;
    }
    if (_startTime.compareTo(_endTime) >= 0) {
      TDToast.showText(_l10n.get('endTimeMustLater'), context: context);
      return;
    }
    final hours = double.tryParse(_hoursCtrl.text) ?? 0;
    if (hours <= 0) {
      TDToast.showText(_l10n.get('overtimeHoursPositive'), context: context);
      return;
    }
    Navigator.pop(
      context,
      OvertimeDetailData(
        jbNo: widget.initialData?.jbNo,
        itm: widget.initialData?.itm,
        salNo: _selEmployee!.salNo,
        salName: _selEmployee!.name,
        dep: _dep,
        depName: _depName,
        jbType: _jbType,
        jbDate: _jbDate,
        startTime: _startTime,
        endTime: _endTime,
        jbHours: hours,
        jbDays: double.tryParse(_daysCtrl.text) ?? 0,
        attPeriod: _attPeriod,
        reason: _reasonCtrl.text.trim(),
        compensationType: _compensationType,
        compensationCount: double.tryParse(_compCountCtrl.text) ?? 0,
        adr: _adrCtrl.text,
        rem: _remarkCtrl.text,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;

    return AnimatedPadding(
      padding: EdgeInsets.only(
        bottom: MediaQuery.of(context).viewInsets.bottom,
      ),
      duration: const Duration(milliseconds: 200),
      child: SafeArea(
        child: ConstrainedBox(
          constraints: BoxConstraints(
            maxHeight: MediaQuery.of(context).size.height * 0.85,
          ),
          child: Container(
            decoration: BoxDecoration(
              color: colors.bgPage,
              borderRadius: const BorderRadius.vertical(
                top: Radius.circular(16),
              ),
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                _buildHeader(colors),
                Flexible(
                  child: GestureDetector(
                    onTap: () => FocusScope.of(context).unfocus(),
                    behavior: HitTestBehavior.translucent,
                    child: SingleChildScrollView(
                      controller: _scrollCtrl,
                      keyboardDismissBehavior:
                          ScrollViewKeyboardDismissBehavior.manual,
                      padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: [
                          // ── 必填 ──
                          _buildEmployeePicker(colors),
                          const SizedBox(height: 12),
                          _buildJbTypeSelector(colors),
                          const SizedBox(height: 12),
                          _buildJbDatePicker(colors),
                          const SizedBox(height: 12),
                          _buildStartTimePicker(colors),
                          const SizedBox(height: 12),
                          _buildEndTimePicker(colors),
                          const SizedBox(height: 12),
                          _buildHoursInput(colors),
                          // ── 非必填 ──
                          const SizedBox(height: 12),
                          _buildDeptPicker(colors),
                          const SizedBox(height: 12),
                          _buildDaysInput(colors),
                          const SizedBox(height: 12),
                          _buildAttPeriodInput(colors),
                          const SizedBox(height: 12),
                          _buildReasonInput(colors),
                          const SizedBox(height: 12),
                          _buildCompensationTypeSelector(colors),
                          const SizedBox(height: 12),
                          _buildCompCountInput(colors),
                          const SizedBox(height: 12),
                          _buildAdrInput(colors),
                          const SizedBox(height: 12),
                          _buildRemarkInput(colors),
                        ],
                      ),
                    ),
                  ),
                ),
                Container(
                  padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
                  decoration: BoxDecoration(
                    color: colors.bgCard,
                    border: Border(
                      top: BorderSide(color: colors.border, width: 0.5),
                    ),
                  ),
                  child: _buildActions(),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  // ── 标题栏 ──
  Widget _buildHeader(AppColorsExtension colors) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Center(
          child: Container(
            margin: const EdgeInsets.only(top: 8, bottom: 4),
            width: 36,
            height: 4,
            decoration: BoxDecoration(
              color: colors.border,
              borderRadius: BorderRadius.circular(2),
            ),
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(20, 8, 12, 16),
          child: Row(
            children: [
              const SizedBox(width: 24),
              Expanded(
                child: Center(
                  child: Text(
                    _isEdit
                        ? _l10n.get('editOvertimeDetail')
                        : _l10n.get('addOvertimeDetail'),
                    style: TextStyle(
                      fontSize: AppFontSizes.title,
                      fontWeight: FontWeight.w600,
                      color: colors.textPrimary,
                    ),
                  ),
                ),
              ),
              GestureDetector(
                onTap: () => Navigator.pop(context),
                child: Padding(
                  padding: const EdgeInsets.all(4),
                  child: Icon(
                    Icons.close,
                    size: 20,
                    color: colors.textSecondary,
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  // ── 通用 Picker 卡片 ──
  Widget _pickerCard({
    required String label,
    required bool required,
    required String currentLabel,
    required bool hasValue,
    required VoidCallback onTap,
    VoidCallback? onClear,
  }) {
    final tdTheme = TDTheme.of(context);
    return GestureDetector(
      onTap: () {
        FocusManager.instance.primaryFocus?.unfocus();
        onTap();
      },
      child: Container(
        padding: const EdgeInsets.only(
          left: 16,
          right: 10,
          top: 12,
          bottom: 12,
        ),
        decoration: BoxDecoration(
          color: tdTheme.bgColorContainer,
          borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
          border: Border.all(color: tdTheme.componentStrokeColor),
        ),
        child: Row(
          children: [
            TDText(
              label,
              maxLines: 1,
              overflow: TextOverflow.visible,
              font: tdTheme.fontBodyLarge,
              fontWeight: FontWeight.w400,
              style: const TextStyle(letterSpacing: 0),
            ),
            if (required)
              Padding(
                padding: const EdgeInsets.only(left: 4),
                child: TDText(
                  '*',
                  font: tdTheme.fontBodyLarge,
                  fontWeight: FontWeight.w400,
                  style: TextStyle(color: tdTheme.errorColor6),
                ),
              ),
            const SizedBox(width: 12),
            Expanded(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                mainAxisSize: MainAxisSize.max,
                children: [
                  Flexible(
                    child: TDText(
                      currentLabel,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      font: tdTheme.fontBodyLarge,
                      fontWeight: FontWeight.w400,
                      textColor: hasValue
                          ? tdTheme.textColorPrimary
                          : tdTheme.textColorPlaceholder,
                      textAlign: TextAlign.end,
                    ),
                  ),
                  const SizedBox(width: 4),
                  SizedBox(
                    width: 18,
                    height: 18,
                    child: onClear != null
                        ? GestureDetector(
                            onTap: onClear,
                            child: Icon(
                              Icons.close,
                              size: 18,
                              color: tdTheme.textColorPlaceholder,
                            ),
                          )
                        : Icon(
                            Icons.chevron_right,
                            size: 18,
                            color: tdTheme.textColorPlaceholder,
                          ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  // ── 0. 员工 ──
  Widget _buildEmployeePicker(AppColorsExtension colors) {
    return _pickerCard(
      label: _l10n.get('employee'),
      required: true,
      hasValue: _selEmployee != null,
      currentLabel: _selEmployee != null
          ? '${_selEmployee!.salNo}/${_selEmployee!.name}'
          : _l10n.get('pleaseSelect'),
      onTap: () async {
        final result = await showSearchablePicker<EmployeeItem>(
          context,
          title: '${_l10n.get('select')}${_l10n.get('employee')}',
          searchHint: _l10n.get('search'),
          loader: (keyword, page) =>
              widget.api.getEmployees(keyword: keyword, page: page, size: 20),
          labelBuilder: (e) =>
              e.name.isEmpty ? e.salNo : '${e.salNo} ${e.name}',
          onRefresh: () => widget.api.clearRefCache(),
        );
        if (result != null && mounted) {
          setState(() => _selEmployee = result);
        }
      },
      onClear: _selEmployee != null
          ? () => setState(() => _selEmployee = null)
          : null,
    );
  }

  // ── 1. 部门 ──
  Widget _buildDeptPicker(AppColorsExtension colors) {
    return _pickerCard(
      label: _l10n.get('dep'),
      required: false,
      hasValue: _dep.isNotEmpty,
      currentLabel: _dep.isNotEmpty
          ? (_depName.isNotEmpty ? '$_dep/$_depName' : _dep)
          : _l10n.get('pleaseSelect'),
      onTap: () async {
        final result = await showSearchablePicker<DepartmentItem>(
          context,
          title: '${_l10n.get('select')}${_l10n.get('dep')}',
          searchHint: _l10n.get('search'),
          loader: (keyword, page) =>
              widget.api.getDepartments(keyword: keyword, page: page, size: 20),
          labelBuilder: (d) => d.name.isEmpty ? d.dep : '${d.dep} ${d.name}',
          onRefresh: () => widget.api.clearRefCache(),
        );
        if (result != null && mounted) {
          setState(() {
            _dep = result.dep;
            _depName = result.name;
          });
        }
      },
      onClear: _dep.isNotEmpty
          ? () => setState(() {
              _dep = '';
              _depName = '';
            })
          : null,
    );
  }

  // ── 2. 加班类型 ──
  Widget _buildJbTypeSelector(AppColorsExtension colors) {
    final selLabel = _jbTypeLabel(_jbType);
    return _pickerCard(
      label: _l10n.get('jbType'),
      required: true,
      hasValue: true,
      currentLabel: selLabel,
      onTap: () => _showJbTypePicker(),
    );
  }

  void _showJbTypePicker() {
    FocusManager.instance.primaryFocus?.unfocus();
    final labels = _jbTypeOptions.map((t) => _jbTypeLabel(t)).toList();
    TDPicker.showMultiPicker(
      context,
      title: _l10n.get('selectJbType'),
      data: [labels],
      onConfirm: (selected) {
        if (selected.isNotEmpty) {
          final idx = selected.first is int ? selected.first as int : 0;
          if (idx >= 0 && idx < _jbTypeOptions.length) {
            setState(() => _jbType = _jbTypeOptions[idx]);
          }
        }
        Navigator.of(context).pop();
      },
    );
  }

  String _jbTypeLabel(String type) {
    switch (type) {
      case 'WORKING_DAY':
        return _l10n.get('workingDay');
      case 'REST_DAY':
        return _l10n.get('restDay');
      case 'PUBLIC_HOLIDAY':
        return _l10n.get('publicHoliday');
      case 'SPECIAL_HOLIDAY':
        return _l10n.get('specialHoliday');
      case 'OTHER':
        return _l10n.get('other');
      default:
        return type;
    }
  }

  String? _dayOfWeekLabel(String dateTimeStr) {
    final dt = DateTime.tryParse(dateTimeStr);
    if (dt == null) return null;
    switch (dt.weekday) {
      case 1:
        return _l10n.get('monday');
      case 2:
        return _l10n.get('tuesday');
      case 3:
        return _l10n.get('wednesday');
      case 4:
        return _l10n.get('thursday');
      case 5:
        return _l10n.get('friday');
      case 6:
        return _l10n.get('saturday');
      case 7:
        return _l10n.get('sunday');
      default:
        return null;
    }
  }

  // ── 4. 申请日期 ──
  Widget _buildJbDatePicker(AppColorsExtension colors) {
    return _datePickerCard(
      label: _l10n.get('applyDate'),
      value: _jbDate,
      hint: _l10n.get('pleaseSelect'),
      colors: colors,
      required: true,
      onPick: (d) => setState(() => _jbDate = d),
      onClear: _jbDate.isNotEmpty ? () => setState(() => _jbDate = '') : null,
    );
  }

  // ── 4. 开始时间 ──
  Widget _buildStartTimePicker(AppColorsExtension colors) {
    return _datePickerCard(
      label: _l10n.get('startTime'),
      value: _startTime,
      hint: _l10n.get('pleaseSelect'),
      colors: colors,
      required: true,
      showDayOfWeek: true,
      onPick: (d) {
        setState(() {
          _startTime = d;
          if (d.length >= 7) _attPeriod = d.substring(0, 7);
          _autoCalcHours();
        });
      },
      onClear: _startTime.isNotEmpty
          ? () {
              setState(() {
                _startTime = '';
                _autoCalcHours();
              });
            }
          : null,
    );
  }

  // ── 5. 结束时间 ──
  Widget _buildEndTimePicker(AppColorsExtension colors) {
    return _datePickerCard(
      label: _l10n.get('endTime'),
      value: _endTime,
      hint: _l10n.get('pleaseSelect'),
      colors: colors,
      required: true,
      showDayOfWeek: true,
      onPick: (d) {
        setState(() {
          _endTime = d;
          _autoCalcHours();
        });
      },
      onClear: _endTime.isNotEmpty
          ? () {
              setState(() {
                _endTime = '';
                _autoCalcHours();
              });
            }
          : null,
    );
  }

  void _autoCalcHours() {
    if (_startTime.isEmpty || _endTime.isEmpty) return;
    final st = DateTime.tryParse(_startTime);
    final et = DateTime.tryParse(_endTime);
    if (st == null || et == null || et.isBefore(st)) return;

    // 按自然日逐天累加，每天最多 8 小时
    double totalHours = 0;
    DateTime dayStart = DateTime(st.year, st.month, st.day);
    while (dayStart.isBefore(et)) {
      final dayEnd = dayStart.add(const Duration(days: 1));
      final segStart = st.isAfter(dayStart) ? st : dayStart;
      final segEnd = et.isBefore(dayEnd) ? et : dayEnd;
      final segHours = segEnd.difference(segStart).inMinutes / 60.0;
      totalHours += segHours > 8.0 ? 8.0 : segHours;
      dayStart = dayEnd;
    }

    if (totalHours > 0) {
      _hoursCtrl.text = ((totalHours * 2).ceil() / 2.0).toStringAsFixed(1);
    }
    _autoCalcDays();
  }

  void _autoCalcDays() {
    final hours = double.tryParse(_hoursCtrl.text) ?? 0;
    if (hours <= 0) return;
    // 8小时工作制：天数 = 小时 ÷ 8
    final diffDays = hours / 8.0;
    _daysCtrl.text = ((diffDays * 2).ceil() / 2.0).toStringAsFixed(1);
  }

  /// 手动输入时失焦自动按 0.5 向上取整
  void _roundToHalf(TextEditingController controller) {
    final text = controller.text.trim();
    if (text.isEmpty) return;
    final value = double.tryParse(text);
    if (value == null || value <= 0) return;
    final rounded = (value * 2).ceil() / 2.0;
    final formatted = rounded.toStringAsFixed(1);
    if (controller.text != formatted) {
      controller.text = formatted;
    }
  }

  // ── 6. 加班时长 ──
  Widget _buildHoursInput(AppColorsExtension colors) {
    return _inputCard(
      label: _l10n.get('overtimeHours'),
      required: true,
      controller: _hoursCtrl,
      hintText: '>0',
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [
        FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?[05]?$')),
      ],
      focusNode: _hoursFocus,
    );
  }

  // ── 7. 加班天数 ──
  Widget _buildDaysInput(AppColorsExtension colors) {
    return _inputCard(
      label: _l10n.get('overtimeDays'),
      required: false,
      controller: _daysCtrl,
      hintText: '0',
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [
        FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?[05]?$')),
      ],
      focusNode: _daysFocus,
    );
  }

  // ── 8. 考勤周期 ──
  Widget _buildAttPeriodInput(AppColorsExtension colors) {
    return _datePickerCard(
      label: _l10n.get('attPeriod'),
      value: _attPeriod,
      hint: _l10n.get('autoFromDate'),
      colors: colors,
      onPick: (d) => setState(() => _attPeriod = d),
      onClear: _attPeriod.isNotEmpty
          ? () => setState(() => _attPeriod = '')
          : null,
    );
  }

  // ── 10. 事由 ──
  Widget _buildReasonInput(AppColorsExtension colors) {
    final tdTheme = TDTheme.of(context);
    return TDTextarea(
      controller: _reasonCtrl,
      focusNode: _reasonFocus,
      label: _l10n.get('overtimeDetailReason'),
      hintText: _l10n.get('enterReason'),
      maxLines: 3,
      minLines: 1,
      maxLength: 1000,
      indicator: true,
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      onChanged: (_) => setState(() {}),
    );
  }

  // ── 10. 补偿类型 ──
  Widget _buildCompensationTypeSelector(AppColorsExtension colors) {
    final hasValue = _compensationType.isNotEmpty;
    final selLabel = hasValue
        ? _compensationTypeLabel(_compensationType)
        : _l10n.get('pleaseSelect');
    return _pickerCard(
      label: _l10n.get('compensationType'),
      required: false,
      hasValue: hasValue,
      currentLabel: selLabel,
      onTap: () => _showCompensationTypePicker(),
    );
  }

  void _showCompensationTypePicker() {
    FocusManager.instance.primaryFocus?.unfocus();
    final labels = _compensationTypeOptions
        .map((t) => _compensationTypeLabel(t))
        .toList();
    TDPicker.showMultiPicker(
      context,
      title: _l10n.get('selectCompensationType'),
      data: [labels],
      onConfirm: (selected) {
        if (selected.isNotEmpty) {
          final idx = selected.first is int ? selected.first as int : 0;
          if (idx >= 0 && idx < _compensationTypeOptions.length) {
            final newType = _compensationTypeOptions[idx];
            setState(() {
              _compensationType = newType;
              // 无补偿 → 清空；其他 → 默认取 JB_DAYS
              if (newType == 'NO_COMPENSATION') {
                _compCountCtrl.clear();
              } else if (_compCountCtrl.text.isEmpty &&
                  _daysCtrl.text.isNotEmpty) {
                _compCountCtrl.text = _daysCtrl.text;
              }
            });
          }
        }
        Navigator.of(context).pop();
      },
    );
  }

  String _compensationTypeLabel(String type) {
    switch (type) {
      case 'OVERTIME_PAY':
        return _l10n.get('overtimePay');
      case 'COMPENSATORY_LEAVE':
        return _l10n.get('compensatoryLeave');
      case 'NO_COMPENSATION':
        return _l10n.get('noCompensation');
      case 'OTHER':
        return _l10n.get('other');
      default:
        return type;
    }
  }

  // ── 11. 折算补偿次数 ──
  Widget _buildCompCountInput(AppColorsExtension colors) {
    // 无补偿时显示空
    if (_compensationType == 'NO_COMPENSATION') {
      final tdTheme = TDTheme.of(context);
      return GestureDetector(
        onTap: () {},
        child: Container(
          padding: const EdgeInsets.only(
            left: 16,
            right: 10,
            top: 12,
            bottom: 12,
          ),
          decoration: BoxDecoration(
            color: tdTheme.bgColorContainer,
            borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
            border: Border.all(color: tdTheme.componentStrokeColor),
          ),
          child: Row(
            children: [
              TDText(
                _l10n.get('compensationCount'),
                maxLines: 1,
                overflow: TextOverflow.visible,
                font: tdTheme.fontBodyLarge,
                fontWeight: FontWeight.w400,
                style: const TextStyle(letterSpacing: 0),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: TDText(
                  '',
                  maxLines: 1,
                  textAlign: TextAlign.end,
                  font: tdTheme.fontBodyLarge,
                  fontWeight: FontWeight.w400,
                  textColor: tdTheme.textColorPlaceholder,
                ),
              ),
            ],
          ),
        ),
      );
    }
    return _inputCard(
      label: _l10n.get('compensationCount'),
      required: false,
      controller: _compCountCtrl,
      hintText: '0',
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [
        FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,1}$')),
      ],
      focusNode: _compCountFocus,
    );
  }

  // ── 12. 地点 ──
  Widget _buildAdrInput(AppColorsExtension colors) {
    return _inputCard(
      label: _l10n.get('adr'),
      required: false,
      controller: _adrCtrl,
      hintText: _l10n.get('enterAdr'),
      focusNode: _adrFocus,
    );
  }

  // ── 13. 备注 ──
  Widget _buildRemarkInput(AppColorsExtension colors) {
    final tdTheme = TDTheme.of(context);
    return TDTextarea(
      controller: _remarkCtrl,
      focusNode: _remarkFocus,
      label: _l10n.get('remark'),
      hintText: _l10n.get('enterRemark'),
      maxLines: 3,
      minLines: 1,
      maxLength: 1000,
      indicator: true,
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      onChanged: (_) => setState(() {}),
    );
  }

  // ── 操作按钮 ──
  Widget _buildActions() {
    return Row(
      children: [
        Expanded(
          child: TDButton(
            text: _l10n.get('cancel'),
            size: TDButtonSize.large,
            type: TDButtonType.outline,
            shape: TDButtonShape.rectangle,
            theme: TDButtonTheme.defaultTheme,
            onTap: () => Navigator.pop(context),
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: TDButton(
            text: _isEdit ? _l10n.get('confirmEdit') : _l10n.get('add'),
            size: TDButtonSize.large,
            type: TDButtonType.fill,
            shape: TDButtonShape.rectangle,
            theme: TDButtonTheme.primary,
            onTap: _confirm,
          ),
        ),
      ],
    );
  }

  // ── 日期选择器卡片 ──
  Widget _datePickerCard({
    required String label,
    required String value,
    required String hint,
    required AppColorsExtension colors,
    required ValueChanged<String> onPick,
    VoidCallback? onClear,
    bool required = false,
    bool showDayOfWeek = false,
  }) {
    final tdTheme = TDTheme.of(context);
    final hasValue = value.isNotEmpty;
    final dayOfWeek = showDayOfWeek && hasValue ? _dayOfWeekLabel(value) : null;
    return GestureDetector(
      onTap: () => _pickDateTime(label, onPick, currentValue: value),
      child: Container(
        padding: const EdgeInsets.only(
          left: 16,
          right: 10,
          top: 12,
          bottom: 12,
        ),
        decoration: BoxDecoration(
          color: tdTheme.bgColorContainer,
          borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
          border: Border.all(color: tdTheme.componentStrokeColor),
        ),
        child: Row(
          children: [
            TDText(
              label,
              maxLines: 1,
              overflow: TextOverflow.visible,
              font: tdTheme.fontBodyLarge,
              fontWeight: FontWeight.w400,
              style: const TextStyle(letterSpacing: 0),
            ),
            if (required)
              Padding(
                padding: const EdgeInsets.only(left: 4),
                child: TDText(
                  '*',
                  font: tdTheme.fontBodyLarge,
                  fontWeight: FontWeight.w400,
                  style: TextStyle(color: tdTheme.errorColor6),
                ),
              ),
            const SizedBox(width: 12),
            Expanded(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                mainAxisSize: MainAxisSize.max,
                children: [
                  Flexible(
                    child: TDText(
                      value.isEmpty ? hint : value,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      font: tdTheme.fontBodyLarge,
                      fontWeight: FontWeight.w400,
                      textColor: value.isEmpty
                          ? tdTheme.textColorPlaceholder
                          : tdTheme.textColorPrimary,
                      textAlign: TextAlign.end,
                    ),
                  ),
                  if (dayOfWeek != null) ...[
                    const SizedBox(width: 6),
                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 6,
                        vertical: 2,
                      ),
                      decoration: BoxDecoration(
                        color: tdTheme.brandColor1,
                        borderRadius: BorderRadius.circular(4),
                      ),
                      child: TDText(
                        dayOfWeek,
                        font: TDTheme.of(context).fontBodySmall,
                        fontWeight: FontWeight.w500,
                        textColor: tdTheme.brandColor7,
                      ),
                    ),
                  ],
                  const SizedBox(width: 4),
                  SizedBox(
                    width: 18,
                    height: 18,
                    child: hasValue
                        ? GestureDetector(
                            onTap: onClear,
                            child: Icon(
                              Icons.close,
                              size: 18,
                              color: tdTheme.textColorPlaceholder,
                            ),
                          )
                        : Icon(
                            Icons.chevron_right,
                            size: 18,
                            color: tdTheme.textColorPlaceholder,
                          ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _pickDateTime(
    String label,
    ValueChanged<String> onPick, {
    String currentValue = '',
  }) {
    final l10n = _l10n;
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final now = DateTime.now();
    final isDateOnly =
        label == _l10n.get('applyDate') || label == _l10n.get('attPeriod');
    final isAttPeriod = label == _l10n.get('attPeriod');
    final isStartTime = label == _l10n.get('startTime');
    final isEndTime = label == _l10n.get('endTime');

    // 根据当前值定位初始日期，无值时默认当月/当天
    int initYear = now.year;
    int initMonth = now.month;
    int initDay = now.day;
    int initHour = now.hour;
    int initMinute = now.minute;

    if (currentValue.isNotEmpty) {
      if (isAttPeriod) {
        // yyyy-MM
        final parts = currentValue.split('-');
        if (parts.length == 2) {
          initYear = int.tryParse(parts[0]) ?? now.year;
          initMonth = int.tryParse(parts[1]) ?? now.month;
          initDay = 1;
        }
      } else {
        // yyyy-MM-dd 或 yyyy-MM-dd HH:mm
        final dt = DateTime.tryParse(currentValue);
        if (dt != null) {
          initYear = dt.year;
          initMonth = dt.month;
          initDay = dt.day;
          initHour = dt.hour;
          initMinute = dt.minute;
        }
      }
    } else if (isStartTime) {
      // 开始时间无当前值：工作日默认 18:00，周末默认 09:00
      final date = _jbDate.isNotEmpty ? DateTime.tryParse(_jbDate) : null;
      final weekday = date?.weekday ?? now.weekday;
      initHour = (weekday <= DateTime.friday) ? 18 : 9;
      initMinute = 0;
    } else if (isEndTime) {
      // 结束时间无当前值：工作日默认 19:00，周末默认 18:00
      final date = _jbDate.isNotEmpty ? DateTime.tryParse(_jbDate) : null;
      final weekday = date?.weekday ?? now.weekday;
      initHour = (weekday <= DateTime.friday) ? 19 : 18;
      initMinute = 0;
    }

    final useTime = !isDateOnly;
    FocusManager.instance.primaryFocus?.unfocus();
    TDPicker.showDatePicker(
      context,
      title: l10n.get('selectDate'),
      backgroundColor: colors.bgCard,
      useYear: true,
      useMonth: true,
      useDay: !isAttPeriod,
      useHour: useTime,
      useMinute: useTime,
      useSecond: false,
      useWeekDay: false,
      dateStart: useTime ? const [2020, 1, 1, 0, 0] : const [2020, 1, 1],
      dateEnd: useTime
          ? [now.year + 1, 12, 31, 23, 59]
          : [now.year + 1, 12, 31],
      initialDate: useTime
          ? [initYear, initMonth, initDay, initHour, initMinute]
          : [initYear, initMonth, initDay],
      onConfirm: (selected) {
        final year = selected['year'];
        final month = selected['month'].toString().padLeft(2, '0');
        final day = selected['day'].toString().padLeft(2, '0');
        final hour = selected['hour']?.toString().padLeft(2, '0') ?? '00';
        final minute = selected['minute']?.toString().padLeft(2, '0') ?? '00';
        if (isAttPeriod) {
          onPick('$year-$month');
        } else if (isDateOnly) {
          onPick('$year-$month-$day');
        } else {
          onPick('$year-$month-$day $hour:$minute');
        }
        Navigator.of(context).pop();
      },
    );
  }

  // ── 通用输入卡片 ──
  Widget _inputCard({
    required String label,
    required bool required,
    required TextEditingController controller,
    required String hintText,
    TextInputType? keyboardType,
    List<TextInputFormatter>? inputFormatters,
    FocusNode? focusNode,
  }) {
    final tdTheme = TDTheme.of(context);
    final hasValue = controller.text.isNotEmpty;
    return Container(
      padding: const EdgeInsets.only(left: 16, right: 10, top: 12, bottom: 12),
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      child: Row(
        children: [
          TDText(
            label,
            maxLines: 1,
            overflow: TextOverflow.visible,
            font: tdTheme.fontBodyLarge,
            fontWeight: FontWeight.w400,
            style: const TextStyle(letterSpacing: 0),
          ),
          if (required)
            Padding(
              padding: const EdgeInsets.only(left: 4),
              child: TDText(
                '*',
                font: tdTheme.fontBodyLarge,
                fontWeight: FontWeight.w400,
                style: TextStyle(color: tdTheme.errorColor6),
              ),
            ),
          const SizedBox(width: 12),
          Expanded(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              mainAxisSize: MainAxisSize.max,
              children: [
                Flexible(
                  child: TextField(
                    controller: controller,
                    focusNode: focusNode,
                    textAlign: TextAlign.end,
                    keyboardType: keyboardType,
                    inputFormatters: inputFormatters,
                    style: TextStyle(
                      fontSize: 16,
                      color: tdTheme.textColorPrimary,
                    ),
                    decoration: InputDecoration(
                      hintText: hintText,
                      hintStyle: TextStyle(
                        fontSize: 16,
                        color: tdTheme.textColorPlaceholder,
                      ),
                      border: InputBorder.none,
                      isDense: true,
                      contentPadding: EdgeInsets.zero,
                    ),
                    onChanged: (_) => setState(() {}),
                  ),
                ),
                const SizedBox(width: 4),
                SizedBox(
                  width: 18,
                  height: 18,
                  child: hasValue
                      ? GestureDetector(
                          onTap: () {
                            controller.clear();
                            setState(() {});
                          },
                          child: Icon(
                            Icons.close,
                            size: 18,
                            color: tdTheme.textColorPlaceholder,
                          ),
                        )
                      : null,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
