import 'package:dropdown_search/dropdown_search.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';

import '../../../../core/config/env.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/toastNotification.dart';
import '../../../../data/utils/validators.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../widgets/policy_search_results_list.dart';

/// Four-step Add/Edit Claim flow; submits via [claim/createClaim] or [claim/updateClaim].
class AddClaimStepperDialog extends StatefulWidget {
  const AddClaimStepperDialog({
    super.key,
    required this.userId,
    required this.managerId,
    required this.role,
    required this.onSuccess,
    this.claimToEdit,
  });

  final dynamic userId;
  final dynamic managerId;
  final dynamic role;
  final VoidCallback onSuccess;
  /// When set, dialog runs in edit mode and calls [claim/updateClaim].
  final Map<String, dynamic>? claimToEdit;

  @override
  State<AddClaimStepperDialog> createState() => _AddClaimStepperDialogState();
}

class _AddClaimStepperDialogState extends State<AddClaimStepperDialog> {
  static const Color _teal = Color(0xFF14A492);
  static const Color _border = Color(0xFFE2E8F0);
  static const Color _muted = Color(0xFF64748B);
  static const double _fieldHeight = 40;

  late ApiService _api;
  String? _token;
  bool _loading = false;
  bool _searchLoading = false;
  int _step = 0;

  final _vehicleSearch = TextEditingController();
  final _externalVehicleNo = TextEditingController();
  final _policySearch = TextEditingController();
  final _policyNum = TextEditingController();
  final _claimsNumber = TextEditingController();
  final _insuredName = TextEditingController();

  final _accidentDt = TextEditingController();
  final _accidentPlace = TextEditingController();
  final _claimDesc = TextEditingController();
  final _spotSurveyorName = TextEditingController();
  final _workshopSurveyorName = TextEditingController();
  final _spotSurveyorContact = TextEditingController();
  final _workshopSurveyorContact = TextEditingController();
  final _remark = TextEditingController();
  final _partner = TextEditingController();
  final _broker = TextEditingController();
  final _insurer = TextEditingController();

  String? _policyFrom;
  String? _selectedClaimTypeId;
  String? _regNo;
  String? _claimId;
  String? _selectedClaimStatusId;
  /// True when claim was loaded from API already SETTLED (not user dropdown change).
  bool _apiClaimStatusWasSettled = false;
  Map<String, dynamic>? _lastApiClaimItem;
  String? _mobile;
  String? _mail;
  String? _insurerId;
  String? _brokerId;
  String? _partnerId;

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

  static const int _maxFileSlots = 6;
  final List<PlatformFile> _pickedFiles = [];
  final List<Map<String, dynamic>> _existingFiles = [];
  final List<String> _removedExistingFileIds = [];

  List<Map<String, dynamic>> _claimTypes = [];
  List<Map<String, dynamic>> _claimStatuses = [];
  List<Map<String, dynamic>> _insurerList = [];
  List<Map<String, dynamic>> _brokerList = [];
  List<Map<String, dynamic>> _partnerList = [];

  List<Map<String, dynamic>> _vehicleHits = [];
  List<Map<String, dynamic>> _policyHits = [];

  final _policyFromOptions = const ['Internal', 'External'];

  bool get _isInternal => _policyFrom == 'Internal';
  bool get _isExternal => _policyFrom == 'External';

  @override
  void initState() {
    super.initState();
    _api = ApiService();
    _policyFrom = _policyFromOptions.first;
    if (_isEdit) {
      _populateFromClaim(widget.claimToEdit!);
    }
    _initTokenAndMasters();
  }

  Future<void> _initTokenAndMasters() async {
    await _initializeToken();
    if (_isEdit && mounted) setState(() => _loading = true);
    final futures = <Future>[
      _loadClaimTypes(),
      _loadClaimStatuses(),
    ];
    if (_isEdit) futures.add(_fetchAndApplyClaim());
    if (_isEdit && _isExternal) futures.add(_loadExternalMasters());
    await Future.wait(futures);
    if (!mounted) return;
    if (_isEdit && _lastApiClaimItem != null) {
      _apiClaimStatusWasSettled =
          _claimItemStatusIsSettled(_lastApiClaimItem!);
    }
    setState(() {
      if (_isEdit) _loading = false;
      if (!_isEdit &&
          (_selectedClaimStatusId == null || _selectedClaimStatusId!.isEmpty) &&
          _claimStatuses.isNotEmpty) {
        _selectedClaimStatusId = _claimStatuses.first['id']?.toString();
      }
    });
  }

  Future<void> _fetchAndApplyClaim() async {
    final id = _claimId ?? widget.claimToEdit?['id']?.toString();
    if (id == null || id.isEmpty) return;
    try {
      final r = await _api.fetchClaimById(id);
      if (!mounted) return;
      final ok = r['status'] == 'success' ||
          r['status'] == 200 ||
          r['status'] == '200';
      if (!ok) return;
      final data = r['data'];
      if (data is Map) {
        _existingFiles.clear();
        _insurerId = null;
        _brokerId = null;
        _partnerId = null;
        _insurer.clear();
        _broker.clear();
        _partner.clear();
        _populateFromClaim(Map<String, dynamic>.from(data));
      }
    } catch (e) {
      debugPrint('fetchClaimById error: $e');
    }
  }

  String _pickClaimField(Map<String, dynamic> row, List<String> keys) {
    for (final k in keys) {
      final v = row[k];
      if (v != null && v.toString().trim().isNotEmpty) {
        return v.toString().trim();
      }
    }
    return '';
  }

  void _populateFromClaim(Map<String, dynamic> item) {
    _lastApiClaimItem = Map<String, dynamic>.from(item);
    _claimId = item['id']?.toString();
    _policyNum.text = _pickClaimField(item, ['policy_number', 'policy_no']);
    _claimsNumber.text = _pickClaimField(item, ['claim_number', 'claims_number']);
    final policyFrom = _pickClaimField(item, ['policy_from']);
    if (policyFrom.isNotEmpty &&
        _policyFromOptions.contains(policyFrom)) {
      _policyFrom = policyFrom;
    }
    _insuredName.text = _pickClaimField(item, [
      'insured_name',
      'customer_name',
      'proposer_name',
    ]);
    _regNo = PolicySearchUtils.rcNoFromRow(item);
    if (_isInternal && _regNo != null && _regNo!.isNotEmpty) {
      _vehicleSearch.text = _regNo!;
    }
    if (_isExternal) {
      final reg = _pickClaimField(item, ['reg_no']);
      if (reg.isNotEmpty) _externalVehicleNo.text = reg;
    }
    _selectedClaimTypeId = _pickClaimField(item, ['claim_type_id', 'claim_type']);
    if ((_selectedClaimTypeId ?? '').isEmpty) _selectedClaimTypeId = null;
    _claimDesc.text = _pickClaimField(item, ['claim_description']);
    _remark.text = _pickClaimField(item, ['remark', 'remark']);
    _accidentPlace.text = _pickClaimField(item, [
      'place_of_incident',
      'accident_place',
    ]);
    final incidentRaw = _pickClaimField(item, [
      'date_of_incident',
      'accident_date',
      'accident_datetime',
    ]);
    if (incidentRaw.isNotEmpty) {
      _accidentDt.text = _formatDateOfIncidentForDisplay(incidentRaw);
    }
    _spotSurveyorName.text =
        _pickClaimField(item, ['spot_surveyor_name']);
    _spotSurveyorContact.text = _contactDigitsOnly(
      _pickClaimField(item, ['spot_surveyor_contact']),
    );
    _workshopSurveyorName.text =
        _pickClaimField(item, ['workshop_surveyor_name']);
    _workshopSurveyorContact.text = _contactDigitsOnly(
      _pickClaimField(item, ['workshop_surveyor_contact']),
    );
    _selectedClaimStatusId = _pickClaimField(item, ['claim_status_id']);
    if ((_selectedClaimStatusId ?? '').isEmpty) _selectedClaimStatusId = null;
    _apiClaimStatusWasSettled = _claimItemStatusIsSettled(item);
    _mobile = _pickClaimField(item, ['mobile', 'customer_mobile']);
    _mail = _pickClaimField(item, ['mail', 'email']);

    _partnerId = _pickClaimField(item, ['agent_id', 'partner_id']);
    if (_partnerId!.isEmpty) _partnerId = null;
    final agentName = _pickClaimField(item, [
      'agent_name',
      'partner_name',
      'partner',
    ]);
    _partner.text = agentName.isNotEmpty ? agentName : (_partnerId ?? '');

    _brokerId = _pickClaimField(item, ['broker_id']);
    if (_brokerId!.isEmpty) _brokerId = null;
    final brokerName = _pickClaimField(item, ['broker_name']);
    _broker.text = brokerName;

    _insurerId = _pickClaimField(item, ['insurer_id']);
    if (_insurerId!.isEmpty) _insurerId = null;
    final insurerName = _pickClaimField(item, [
      'insurer_short_name',
      'insurer_name',
    ]);
    _insurer.text = insurerName;

    final rawFiles = item['files'];
    if (rawFiles is List) {
      for (final f in rawFiles) {
        if (f is Map) {
          _existingFiles.add(Map<String, dynamic>.from(f));
        }
      }
    }
  }

  Future<void> _initializeToken() async {
    _token = await AuthService.getToken();
  }

  Future<void> _loadClaimTypes() async {
    try {
      final r = await _api.fetchMasterDropDown('Claim');
      if ((r['status'] == 'success' || r['status'] == 200) &&
          r['data'] is List) {
        setState(() {
          _claimTypes = List<Map<String, dynamic>>.from(r['data']);
        });
      }
    } catch (_) {}
  }

  Future<void> _loadExternalMasters() async {
    try {
      final results = await Future.wait([
        _api.fetchMasterDropDown('Insurers'),
        _api.fetchMasterDropDown('Broker'),
        if (widget.managerId != null)
          _api.fetchAgentNameDropDown(widget.managerId)
        else
          Future.value(<String, dynamic>{'status': 'success', 'data': []}),
      ]);
      if (!mounted) return;
      setState(() {
        if (_isSuccessResponse(results[0]) && results[0]['data'] is List) {
          _insurerList =
              List<Map<String, dynamic>>.from(results[0]['data'] as List);
        }
        if (_isSuccessResponse(results[1]) && results[1]['data'] is List) {
          _brokerList =
              List<Map<String, dynamic>>.from(results[1]['data'] as List);
        }
        if (_isSuccessResponse(results[2]) && results[2]['data'] is List) {
          _partnerList =
              List<Map<String, dynamic>>.from(results[2]['data'] as List);
        }
      });
    } catch (e) {
      debugPrint('Load external masters error: $e');
    }
  }

  Future<void> _loadClaimStatuses() async {
    try {
      final r = await _api.fetchClaimStatusList();
      if ((r['status'] == 'success' || r['status'] == 200) &&
          r['data'] is List) {
        if (mounted) {
          setState(() {
            _claimStatuses = List<Map<String, dynamic>>.from(r['data']);
          });
        }
      }
    } catch (_) {}
  }

  bool _isSuccessResponse(dynamic r) {
    if (r is! Map) return false;
    final s = r['status'];
    return s == 'success' || s == 200 || s == '200';
  }

  void _applyPolicySelection(Map<String, dynamic> row, {required bool fromVehicle}) {
    final policyNo = PolicySearchUtils.policyNumberFromRow(row);
    final rcNo = PolicySearchUtils.rcNoFromRow(row);
    final name = PolicySearchUtils.insuredNameFromRow(row);
    final brokerName = (row['broker_name'] ?? '').toString().trim();
    final brokerId = (row['broker_id'] ?? '').toString().trim();
    final agentId = (row['agent_id'] ?? row['partner_id'] ?? '').toString().trim();
    final agentName = (row['agent_name'] ??
            row['partner_name'] ??
            row['partner'] ??
            '')
        .toString()
        .trim();
    final insurerId = (row['insurer_id'] ?? '').toString().trim();
    final insurerName = (row['insurer_short_name'] ??
            row['insurer_name'] ??
            row['insurer'] ??
            '')
        .toString()
        .trim();

    setState(() {
      if (policyNo.isNotEmpty) _policyNum.text = policyNo;
      if (rcNo != null && rcNo.isNotEmpty) _regNo = rcNo;
      if (fromVehicle) {
        _vehicleSearch.text = PolicySearchUtils.vehicleInputValueOnSelect(row);
      } else if (policyNo.isNotEmpty) {
        _policySearch.text = policyNo;
      }
      if (name.isNotEmpty) _insuredName.text = name;
      final mob = (row['mobile'] ?? row['customer_mobile'] ?? '').toString().trim();
      final mail = (row['mail'] ?? row['email'] ?? '').toString().trim();
      if (mob.isNotEmpty) _mobile = mob;
      if (mail.isNotEmpty) _mail = mail;
      _partnerId = agentId.isEmpty ? null : agentId;
      _partner.text = agentName.isNotEmpty ? agentName : agentId;
      _brokerId = brokerId.isEmpty ? null : brokerId;
      _broker.text = brokerName;
      _insurerId = insurerId.isEmpty ? null : insurerId;
      _insurer.text = insurerName;
      _vehicleHits = [];
      _policyHits = [];
    });
  }

  Future<void> _runVehicleSearch([String? query]) async {
    final q = (query ?? _vehicleSearch.text).trim();
    if (q.isEmpty) {
      setState(() => _vehicleHits = []);
      return;
    }
    setState(() => _searchLoading = true);
    try {
      final r = await _api.fetchVehicleSearch(q);
      if (!mounted) return;
      if (_isSuccessResponse(r)) {
        setState(() {
          _vehicleHits = PolicySearchUtils.normalizeData(r['data']);
          _policyHits = [];
        });
      } else {
        setState(() => _vehicleHits = []);
      }
    } catch (e) {
      debugPrint('Vehicle search error: $e');
      if (mounted) setState(() => _vehicleHits = []);
    } finally {
      if (mounted) setState(() => _searchLoading = false);
    }
  }

  Future<void> _runPolicySearch([String? query]) async {
    final q = (query ?? _policySearch.text).trim();
    if (q.isEmpty) {
      setState(() => _policyHits = []);
      return;
    }
    setState(() => _searchLoading = true);
    try {
      final r = await _api.fetchPolicySearch(q);
      if (!mounted) return;
      if (_isSuccessResponse(r)) {
        setState(() {
          _policyHits = PolicySearchUtils.normalizeData(r['data']);
          _vehicleHits = [];
        });
      } else {
        setState(() => _policyHits = []);
      }
    } catch (e) {
      debugPrint('Policy search error: $e');
      if (mounted) setState(() => _policyHits = []);
    } finally {
      if (mounted) setState(() => _searchLoading = false);
    }
  }

  String _claimTypeLabel(Map<String, dynamic> m) {
    return (m['claim_type'] ?? m['name'] ?? '').toString();
  }

  Future<void> _pickFile() async {
    final r = await FilePicker.platform.pickFiles(withData: true);
    if (r != null && r.files.isNotEmpty) {
      setState(() {
        _pickedFiles.add(r.files.first);
      });
    }
  }

  void _removeExistingFile(int index) {
    if (index < 0 || index >= _existingFiles.length) return;
    final removed = _existingFiles.removeAt(index);
    final id = removed['id']?.toString();
    if (id != null && id.isNotEmpty) _removedExistingFileIds.add(id);
    setState(() {});
  }

  void _removePickedFile(int index) {
    if (index < 0 || index >= _pickedFiles.length) return;
    setState(() => _pickedFiles.removeAt(index));
  }

  String? _existingFileId(Map<String, dynamic> file) {
    final id = file['id'] ?? file['file_id'];
    if (id == null) return null;
    final s = id.toString().trim();
    return s.isEmpty ? null : s;
  }

  String _existingFileDownloadPath(Map<String, dynamic> file) {
    for (final key in ['download_url', 'preview_url', 'file_path', 'file_url']) {
      final raw = file[key];
      if (raw == null) continue;
      final s = raw.toString().trim();
      if (s.isEmpty) continue;
      final idx = s.indexOf('claim/downloadClaimFile');
      if (idx >= 0) {
        final path = s.substring(idx);
        return path.startsWith('/') ? path.substring(1) : path;
      }
      if (s.startsWith('http') && s.contains('/api/')) {
        final afterApi = s.split('/api/').last;
        if (afterApi.isNotEmpty) return afterApi;
      }
    }
    final id = _existingFileId(file);
    return id != null ? 'claim/downloadClaimFile?file_id=$id' : '';
  }

  Future<void> _downloadExistingFile(Map<String, dynamic> file) async {
    final path = _existingFileDownloadPath(file);
    final id = _existingFileId(file);
    if (path.isEmpty || id == null) {
      if (mounted) {
        ToastHelper.showErrorToast(context, 'Download not available for this file');
      }
      return;
    }
    await _api.downloadFile(
      apiUrl: path,
      apiId: id,
      localFile: null,
      fileName: _existingFileName(file),
    );
  }

  Future<void> _downloadPickedFile(PlatformFile file) async {
    if (file.bytes == null) {
      if (mounted) {
        ToastHelper.showErrorToast(context, 'File data not available');
      }
      return;
    }
    await _api.downloadFile(
      apiUrl: null,
      localFile: file,
      fileName: file.name,
    );
  }

  Future<void> _pickAccidentDate() async {
    final initial = _parseAccidentDate() ?? DateTime.now();
    final firstDate = DateTime(2000);
    final lastDate = DateTime.now().add(const Duration(days: 1));
    final pickedDate = await showDatePicker(
      context: context,
      initialDate: initial.isBefore(firstDate) || initial.isAfter(lastDate)
          ? DateTime.now()
          : initial,
      firstDate: firstDate,
      lastDate: lastDate,
      builder: (context, child) => Theme(
        data: Theme.of(context).copyWith(
          colorScheme: Theme.of(context).colorScheme.copyWith(primary: _teal),
        ),
        child: child!,
      ),
    );
    if (pickedDate == null || !mounted) return;
    setState(() {
      _accidentDt.text = DateFormat('dd-MM-yyyy').format(pickedDate);
    });
  }

  DateTime? _parseAccidentDate() {
    final t = _accidentDt.text.trim();
    if (t.isEmpty) return null;
    final patterns = <DateFormat>[
      DateFormat('dd-MM-yyyy'),
      DateFormat('dd-MM-yyyy hh:mm a'),
      DateFormat('dd-MM-yyyy HH:mm'),
      DateFormat('yyyy-MM-dd HH:mm:ss'),
      DateFormat('yyyy-MM-dd'),
    ];
    for (final p in patterns) {
      try {
        return p.parse(t);
      } catch (_) {}
    }
    return null;
  }

  String? _claimStatusLabel(Map<String, dynamic> m) {
    return (m['claim_status'] ??
            m['status'] ??
            m['name'] ??
            m['claim_status_value'] ??
            m['status_name'] ??
            '')
        .toString()
        .trim();
  }

  String? _statusLabelForId(String? id) {
    if (id == null || id.isEmpty) return null;
    for (final m in _claimStatuses) {
      if (m['id']?.toString() == id) return _claimStatusLabel(m);
    }
    return null;
  }

  bool _claimItemStatusIsSettled(Map<String, dynamic> item) {
    final label = _pickClaimField(item, [
      'claim_status_value',
      'claim_status',
      'status',
    ]).toUpperCase();
    if (label.contains('SETTLED')) return true;
    final statusId = _pickClaimField(item, ['claim_status_id']);
    if (statusId.isEmpty) return false;
    for (final m in _claimStatuses) {
      if (m['id']?.toString() != statusId) continue;
      final statusLabel = (_claimStatusLabel(m) ?? '').toUpperCase();
      if (statusLabel.contains('SETTLED')) return true;
    }
    return false;
  }

  /// Disable save only when API-loaded status was already SETTLED.
  bool get _isSaveClaimDisabled => _isEdit && _apiClaimStatusWasSettled;

  /// API format: `dd-MM-yyyy`.
  String? _formatDateOfIncidentForApi(String raw) {
    final t = raw.trim();
    if (t.isEmpty) return null;
    final patterns = <DateFormat>[
      DateFormat('dd-MM-yyyy'),
      DateFormat('d-M-yyyy'),
      DateFormat('dd-MM-yyyy hh:mm a'),
      DateFormat('dd-MM-yyyy HH:mm'),
      DateFormat('yyyy-MM-dd HH:mm:ss'),
      DateFormat('yyyy-MM-dd'),
    ];
    for (final p in patterns) {
      try {
        final dt = p.parse(t);
        return DateFormat('dd-MM-yyyy').format(dt);
      } catch (_) {}
    }
    return t;
  }

  String _formatDateOfIncidentForDisplay(String raw) {
    final api = _formatDateOfIncidentForApi(raw);
    return api ?? raw;
  }

  void _putField(Map<String, String> fields, String key, String? value) {
    final v = value?.trim();
    if (v != null && v.isNotEmpty) fields[key] = v;
  }

  String? _agentIdFromPartner() {
    final id = _partnerId?.trim();
    if (id != null && id.isNotEmpty) return id;
    final p = _partner.text.trim();
    if (p.isEmpty) return null;
    return int.tryParse(p) != null ? p : null;
  }

  String? _vehicleNoForApi() {
    if (_isExternal) {
      final v = _externalVehicleNo.text.trim();
      return v.isEmpty ? null : v;
    }
    final fromReg = _regNo?.trim();
    if (fromReg != null && fromReg.isNotEmpty) return fromReg;
    final fromSearch = _vehicleSearch.text.trim();
    return fromSearch.isEmpty ? null : fromSearch;
  }

  void _resetOriginFields() {
    _vehicleSearch.clear();
    _externalVehicleNo.clear();
    _policySearch.clear();
    _policyNum.clear();
    _claimsNumber.clear();
    _insuredName.clear();
    _insurer.clear();
    _broker.clear();
    _partner.clear();
    _vehicleHits = [];
    _policyHits = [];
    _regNo = null;
    _insurerId = null;
    _brokerId = null;
    _partnerId = null;
    _mobile = null;
    _mail = null;
  }

  Map<String, dynamic>? _findMasterItem(
    List<Map<String, dynamic>> list,
    String? id,
  ) {
    if (id == null || id.isEmpty) return null;
    for (final item in list) {
      if (item['id']?.toString() == id) return item;
    }
    return null;
  }

  String _masterItemLabel(Map<String, dynamic> item, {bool partner = false}) {
    if (partner) {
      final code = item['agent_code']?.toString() ?? '';
      final name = item['agent_name']?.toString() ??
          item['name']?.toString() ??
          '';
      return code.isNotEmpty ? '$code - $name' : name;
    }
    return item['short_name']?.toString() ??
        item['name']?.toString() ??
        item['value']?.toString() ??
        '';
  }

  void _onPolicyFromChanged(String? value) {
    if (value == null || value == _policyFrom) return;
    if (value == 'External') {
      _resetOriginFields();
      _loadExternalMasters();
      setState(() => _policyFrom = value);
    } else {
      setState(() {
        _policyFrom = value;
        _resetOriginFields();
      });
    }
  }

  Widget _buildInsurerOriginField() {
    if (_isInternal) {
      return _labeledField(
        'Insurer',
        TextFormField(
          controller: _insurer,
          readOnly: true,
          style: _fieldValueStyle,
          decoration: _fieldDeco().copyWith(
            hintText: 'Auto-fetched on search',
          ),
        ),
      );
    }
    return _labeledField(
      'Insurer',
      _uniformHeightField(
        DropdownSearch<Map<String, dynamic>>(
          selectedItem: _findMasterItem(_insurerList, _insurerId),
          items: (f, i) => _insurerList,
          itemAsString: (v) => _masterItemLabel(v),
          compareFn: (a, b) =>
              (a['id']?.toString() ?? '') == (b['id']?.toString() ?? ''),
          onChanged: (v) {
            setState(() {
              _insurerId = v?['id']?.toString();
              _insurer.text = v != null ? _masterItemLabel(v) : '';
            });
          },
          decoratorProps: DropDownDecoratorProps(
            decoration: _fieldDeco().copyWith(hintText: 'Select insurer'),
            baseStyle: _fieldValueStyle,
          ),
          popupProps: const PopupProps.menu(
            showSearchBox: true,
            fit: FlexFit.loose,
          ),
        ),
      ),
    );
  }

  Widget _buildBrokerOriginField() {
    if (_isInternal) {
      return _labeledField(
        'Broker',
        TextFormField(
          controller: _broker,
          readOnly: true,
          style: _fieldValueStyle,
          decoration: _fieldDeco().copyWith(
            hintText: 'Auto-fetched on search',
          ),
        ),
      );
    }
    return _labeledField(
      'Broker',
      _uniformHeightField(
        DropdownSearch<Map<String, dynamic>>(
          selectedItem: _findMasterItem(_brokerList, _brokerId),
          items: (f, i) => _brokerList,
          itemAsString: (v) => _masterItemLabel(v),
          compareFn: (a, b) =>
              (a['id']?.toString() ?? '') == (b['id']?.toString() ?? ''),
          onChanged: (v) {
            setState(() {
              _brokerId = v?['id']?.toString();
              _broker.text = v != null ? _masterItemLabel(v) : '';
            });
          },
          decoratorProps: DropDownDecoratorProps(
            decoration: _fieldDeco().copyWith(hintText: 'Select broker'),
            baseStyle: _fieldValueStyle,
          ),
          popupProps: const PopupProps.menu(
            showSearchBox: true,
            fit: FlexFit.loose,
          ),
        ),
      ),
    );
  }

  Widget _buildPartnerOriginField() {
    if (_isInternal) {
      return _labeledField(
        'Partner',
        TextFormField(
          controller: _partner,
          readOnly: true,
          style: _fieldValueStyle,
          decoration: _fieldDeco().copyWith(
            hintText: 'Auto-fetched on search',
          ),
        ),
      );
    }
    return _labeledField(
      'Partner',
      _uniformHeightField(
        DropdownSearch<Map<String, dynamic>>(
          selectedItem: _findMasterItem(_partnerList, _partnerId),
          items: (f, i) => _partnerList,
          itemAsString: (v) => _masterItemLabel(v, partner: true),
          compareFn: (a, b) =>
              (a['id']?.toString() ?? '') == (b['id']?.toString() ?? ''),
          onChanged: (v) {
            setState(() {
              _partnerId = v?['id']?.toString();
              _partner.text =
                  v != null ? _masterItemLabel(v, partner: true) : '';
            });
          },
          decoratorProps: DropDownDecoratorProps(
            decoration: _fieldDeco().copyWith(hintText: 'Select partner'),
            baseStyle: _fieldValueStyle,
          ),
          popupProps: const PopupProps.menu(
            showSearchBox: true,
            fit: FlexFit.loose,
          ),
        ),
      ),
    );
  }

  Map<String, String> _sharedClaimFields() {
    final fields = <String, String>{};
    _putField(fields, 'policy_from', _policyFrom);
    _putField(fields, 'reg_no', _vehicleNoForApi());
    _putField(fields, 'policy_number', _policyNum.text);
    _putField(fields, 'claim_number', _claimsNumber.text);
    _putField(fields, 'claim_type', _selectedClaimTypeId);
    _putField(fields, 'claim_description', _claimDesc.text);
    _putField(fields, 'remark', _remark.text);
    _putField(fields, 'claim_status_id', _selectedClaimStatusId);
    _putField(fields, 'insured_name', _insuredName.text);
    _putField(fields, 'mobile', _mobile);
    _putField(fields, 'mail', _mail);
    _putField(fields, 'manager_id', widget.managerId?.toString());
    _putField(fields, 'agent_id', _agentIdFromPartner());
    _putField(fields, 'insurer_id', _insurerId);
    _putField(fields, 'broker_id', _brokerId);
    _putField(
      fields,
      'date_of_incident',
      _formatDateOfIncidentForApi(_accidentDt.text),
    );
    _putField(fields, 'place_of_incident', _accidentPlace.text);
    _putField(fields, 'spot_surveyor_name', _spotSurveyorName.text);
    _putField(fields, 'spot_surveyor_contact', _spotSurveyorContact.text);
    _putField(fields, 'workshop_surveyor_name', _workshopSurveyorName.text);
    _putField(
      fields,
      'workshop_surveyor_contact',
      _workshopSurveyorContact.text,
    );
    if (_isEdit && _removedExistingFileIds.isNotEmpty) {
      _putField(
        fields,
        'removed_file_ids',
        _removedExistingFileIds.join(','),
      );
    }
    return fields;
  }

  Map<String, String> _buildCreateClaimFields() {
    final fields = _sharedClaimFields();
    _putField(fields, 'created_by', widget.userId?.toString());
    return fields;
  }

  Map<String, String> _buildUpdateClaimFields() {
    final fields = _sharedClaimFields();
    _putField(fields, 'id', _claimId);
    _putField(fields, 'updated_by', widget.userId?.toString());
    return fields;
  }

  int _attachClaimFiles(http.MultipartRequest request) {
    var attached = 0;
    var skippedNoBytes = 0;

    for (final file in _pickedFiles) {
      if (file.bytes == null) {
        skippedNoBytes++;
        debugPrint(
          'AddClaim upload: skipped "${file.name}" (no bytes loaded)',
        );
        continue;
      }
      request.files.add(
        http.MultipartFile.fromBytes(
          'claim_files[]',
          file.bytes!,
          filename: file.name,
        ),
      );
      attached++;
    }

    if (skippedNoBytes > 0) {
      debugPrint(
        'AddClaim upload: $skippedNoBytes picked file(s) not sent (no bytes)',
      );
    }

    return attached;
  }

  void _logClaimUploadRequest({
    required Uri uri,
    required String path,
    required Map<String, String> fields,
    required http.MultipartRequest request,
    required int filesAttached,
  }) {
    debugPrint('════════ AddClaim API request ════════');
    debugPrint('POST $path');
    debugPrint('URL: $uri');
    debugPrint('Headers:');
    for (final entry in request.headers.entries) {
      if (entry.key.toLowerCase() == 'authorization') {
        final token = entry.value;
        final preview = token.length > 24
            ? '${token.substring(0, 20)}…'
            : '***';
        debugPrint('  ${entry.key}: $preview (${token.length} chars)');
      } else {
        debugPrint('  ${entry.key}: ${entry.value}');
      }
    }
    debugPrint('Fields (${fields.length}):');
    final sortedKeys = fields.keys.toList()..sort();
    for (final key in sortedKeys) {
      debugPrint('  $key: ${fields[key]}');
    }
    debugPrint(
      'Files attached to multipart: $filesAttached '
      '(parts in request: ${request.files.length})',
    );
    for (var i = 0; i < request.files.length; i++) {
      final part = request.files[i];
      debugPrint(
        '  [$i] field="${part.field}" '
        'filename="${part.filename}" '
        'size=${part.length} bytes',
      );
    }
    if (_pickedFiles.isNotEmpty) {
      debugPrint('Picked files in UI (${_pickedFiles.length}):');
      for (final p in _pickedFiles) {
        final bytes = p.bytes?.length ?? 0;
        debugPrint(
          '  - name="${p.name}" size=$bytes bytes '
          'ext=${p.extension ?? "-"} sent=${p.bytes != null}',
        );
      }
    }
    if (_isEdit) {
      debugPrint(
        'Edit: claimId=$_claimId, '
        'existingFiles=${_existingFiles.length}, '
        'removedFileIds=${_removedExistingFileIds.join(",")}',
      );
    }
    debugPrint('══════════════════════════════════════');
  }

  Future<void> _submit() async {
    if (_isSaveClaimDisabled) return;
    if (_policyNum.text.trim().isEmpty) {
      ToastHelper.showErrorToast(context, 'Policy number is required');
      return;
    }
    if (_selectedClaimTypeId == null) {
      ToastHelper.showErrorToast(context, 'Claim type is required');
      return;
    }
    if (_accidentPlace.text.trim().isEmpty) {
      ToastHelper.showErrorToast(context, 'Place of accident is required');
      return;
    }
    if (!_isValidSurveyorContact(_spotSurveyorContact.text)) {
      ToastHelper.showErrorToast(
        context,
        'Spot surveyor contact must be exactly 10 digits',
      );
      return;
    }
    if (!_isValidSurveyorContact(_workshopSurveyorContact.text)) {
      ToastHelper.showErrorToast(
        context,
        'Workshop surveyor contact must be exactly 10 digits',
      );
      return;
    }
    if (_token == null) await _initializeToken();
    if (_token == null) {
      if (!mounted) return;
      ToastHelper.showErrorToast(context, 'Not logged in');
      return;
    }

    if (_isEdit && (_claimId == null || _claimId!.isEmpty)) {
      ToastHelper.showErrorToast(context, 'Claim id is missing');
      return;
    }

    final path = _isEdit ? 'claim/updateClaim' : 'claim/createClaim';
    final uri = Uri.parse('${Env.apiUrl}$path');
    final request = http.MultipartRequest('POST', uri);
    request.headers['Authorization'] = 'Bearer $_token';
    request.headers['app-signature'] = Env.App_Signature;

    final payload = _isEdit ? _buildUpdateClaimFields() : _buildCreateClaimFields();
    payload.forEach((key, value) {
      request.fields[key] = value;
    });
    final filesSent = _attachClaimFiles(request);
    _logClaimUploadRequest(
      uri: uri,
      path: path,
      fields: payload,
      request: request,
      filesAttached: filesSent,
    );

    setState(() => _loading = true);
    try {
      final streamed = await request.send();
      final response = await http.Response.fromStream(streamed);
      if (response.statusCode == 200 || response.statusCode == 201) {
        if (mounted) {
          ToastHelper.showSuccessToast(context, 'Saved successfully');
          Navigator.of(context).pop();
          widget.onSuccess();
        }
      } else if (response.statusCode == 403) {
        await _api.clearLocalStorageAndRedirect();
      } else {
        if (mounted) ToastHelper.showErrorToast(context, 'Failed to save');
      }
    } catch (e) {
      if (mounted) ToastHelper.showErrorToast(context, 'Error: $e');
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  bool _canGoNextFromStep(int s) {
    switch (s) {
      case 0:
        return _policyNum.text.trim().isNotEmpty;
      case 1:
        return _selectedClaimTypeId != null &&
            _accidentDt.text.trim().isNotEmpty &&
            _accidentPlace.text.trim().isNotEmpty &&
            _isValidSurveyorContact(_spotSurveyorContact.text) &&
            _isValidSurveyorContact(_workshopSurveyorContact.text);
      case 2:
        return true;
      case 3:
        return true;
      default:
        return false;
    }
  }

  void _goNext() {
    if (_step == 1) {
      if (!_isValidSurveyorContact(_spotSurveyorContact.text)) {
        ToastHelper.showErrorToast(
          context,
          'Spot surveyor contact must be exactly 10 digits',
        );
        return;
      }
      if (!_isValidSurveyorContact(_workshopSurveyorContact.text)) {
        ToastHelper.showErrorToast(
          context,
          'Workshop surveyor contact must be exactly 10 digits',
        );
        return;
      }
    }
    if (!_canGoNextFromStep(_step)) {
      ToastHelper.showErrorToast(context, 'Please complete required fields');
      return;
    }
    setState(() => _step = (_step + 1).clamp(0, 3));
  }

  void _goBack() {
    setState(() => _step = (_step - 1).clamp(0, 3));
  }

  @override
  void dispose() {
    _vehicleSearch.dispose();
    _externalVehicleNo.dispose();
    _policySearch.dispose();
    _policyNum.dispose();
    _claimsNumber.dispose();
    _insuredName.dispose();
    _accidentDt.dispose();
    _accidentPlace.dispose();
    _claimDesc.dispose();
    _spotSurveyorName.dispose();
    _workshopSurveyorName.dispose();
    _spotSurveyorContact.dispose();
    _workshopSurveyorContact.dispose();
    _remark.dispose();
    _partner.dispose();
    _broker.dispose();
    _insurer.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final maxW = ResponsiveLayout.isMobile(context)
        ? MediaQuery.sizeOf(context).width * 0.98
        : 720.0;

    return Dialog(
      backgroundColor: Colors.white,
      insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Theme(
        data: _formTheme(context),
        child: ConstrainedBox(
          constraints: BoxConstraints(maxWidth: maxW, maxHeight: 640),
          child: Stack(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(20, 16, 20, 12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  _header(),
                  const Divider(height: 24, color: _border),
                  _stepperRow(),
                  const Divider(height: 24, color: _border),
                  Expanded(child: SingleChildScrollView(child: _stepBody())),
                  const Divider(height: 20, color: _border),
                  _footerActions(),
                ],
              ),
            ),
            if (_loading)
              const Positioned.fill(
                child: ColoredBox(
                  color: Color(0x66FFFFFF),
                  child: Center(child: CircularProgressIndicator()),
                ),
              ),
          ],
        ),
      ),
      ),
    );
  }

  Widget _header() {
    return Row(
      children: [
        Text(
          _isEdit ? 'Edit Claim' : 'Add Claim',
          style: GoogleFonts.inter(
            fontSize: 18,
            fontWeight: FontWeight.w700,
            color: const Color(0xFF1E293B),
          ),
        ),
        const Spacer(),
        IconButton(
          onPressed: () => Navigator.pop(context),
          style: IconButton.styleFrom(
            backgroundColor: const Color(0xFFF1F5F9),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          icon: const Icon(Icons.close, size: 18),
        ),
      ],
    );
  }

  Widget _stepperRow() {
    const steps = [
      'Search Policy',
      'Claim Details',
      'Upload & Remarks',
      'Review & Save',
    ];
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Row(
        children: [
          for (var i = 0; i < steps.length; i++) ...[
            _stepChip(i, steps[i]),
            if (i < steps.length - 1)
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 4),
                child: Icon(
                  Icons.chevron_right,
                  size: 16,
                  color: i < _step ? _teal : _muted,
                ),
              ),
          ],
        ],
      ),
    );
  }

  Widget _stepChip(int index, String label) {
    final done = index < _step;
    final active = index == _step;
    final Color fg = done || active ? _teal : _muted;
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          width: 26,
          height: 26,
          alignment: Alignment.center,
          decoration: BoxDecoration(
            color: done || active ? _teal : const Color(0xFFE2E8F0),
            shape: BoxShape.circle,
          ),
          child: done
              ? const Icon(Icons.check, size: 16, color: Colors.white)
              : Text(
                  '${index + 1}',
                  style: GoogleFonts.inter(
                    fontSize: 12,
                    fontWeight: FontWeight.w600,
                    color: active ? Colors.white : _muted,
                  ),
                ),
        ),
        const SizedBox(width: 8),
        Text(
          label,
          style: GoogleFonts.inter(
            fontSize: 12,
            fontWeight: FontWeight.w600,
            color: fg,
          ),
        ),
      ],
    );
  }

  Widget _sectionTitle(String title) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Row(
        children: [
          Container(
            width: 4,
            height: 18,
            decoration: BoxDecoration(
              color: _teal,
              borderRadius: BorderRadius.circular(2),
            ),
          ),
          const SizedBox(width: 8),
          Text(
            title,
            style: GoogleFonts.inter(
              fontSize: 11,
              fontWeight: FontWeight.w700,
              letterSpacing: 0.6,
              color: _teal,
            ),
          ),
        ],
      ),
    );
  }

  Widget _stepBody() {
    switch (_step) {
      case 0:
        return _stepSearchPolicy();
      case 1:
        return _stepClaimDetails();
      case 2:
        return _stepUpload();
      case 3:
        return _stepStatus();
      default:
        return const SizedBox.shrink();
    }
  }

  Widget _stepSearchPolicy() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        if (_isInternal) ...[
          _sectionTitle('SEARCH POLICY'),
          LayoutBuilder(
            builder: (context, c) {
              final twoCol = c.maxWidth > 520;
              if (twoCol) {
                return Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: _searchFieldColumn(
                        label: 'Search By Vehicle Number',
                        controller: _vehicleSearch,
                        hits: _vehicleHits,
                        fromVehicle: true,
                        onSearch: _runVehicleSearch,
                      ),
                    ),
                    const SizedBox(width: 16),
                    Expanded(
                      child: _searchFieldColumn(
                        label: 'Search By Policy Number',
                        controller: _policySearch,
                        hits: _policyHits,
                        fromVehicle: false,
                        onSearch: _runPolicySearch,
                      ),
                    ),
                  ],
                );
              }
              return Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  _searchFieldColumn(
                    label: 'Search By Vehicle Number',
                    controller: _vehicleSearch,
                    hits: _vehicleHits,
                    fromVehicle: true,
                    onSearch: _runVehicleSearch,
                  ),
                  const SizedBox(height: 12),
                  _searchFieldColumn(
                    label: 'Search By Policy Number',
                    controller: _policySearch,
                    hits: _policyHits,
                    fromVehicle: false,
                    onSearch: _runPolicySearch,
                  ),
                ],
              );
            },
          ),
          const SizedBox(height: 20),
          const Divider(color: _border),
          const SizedBox(height: 16),
        ],
        _sectionTitle('CLAIM ORIGIN'),
        Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Policy From *', style: _labelStyle),
                  const SizedBox(height: 6),
                  _uniformHeightField(
                    DropdownButtonFormField<String>(
                    key: ValueKey('policy-from-$_policyFrom'),
                    initialValue: _policyFrom,
                    isExpanded: true,
                    iconSize: 20,
                    style: _fieldValueStyle,
                    decoration: _fieldDeco(),
                    items: _policyFromOptions
                        .map(
                          (e) => DropdownMenuItem(
                            value: e,
                            child: Text(e, style: _fieldValueStyle),
                          ),
                        )
                        .toList(),
                    onChanged: _onPolicyFromChanged,
                  ),
                  ),
                ],
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Insured Name', style: _labelStyle),
                  const SizedBox(height: 6),
                  _uniformHeightField(
                    TextFormField(
                    controller: _insuredName,
                    readOnly: _isInternal,
                    style: _fieldValueStyle,
                    decoration: _fieldDeco().copyWith(
                      hintText: _isInternal
                          ? 'Auto-fetched on search'
                          : 'Enter insured name',
                    ),
                  ),
                  ),
                ],
              ),
            ),
          ],
        ),  
        const SizedBox(height: 12),
        LayoutBuilder(
          builder: (context, c) {
            final twoCol = c.maxWidth > 520;
            final policyField = _labeledField(
              'Policy Number *',
              TextFormField(
                controller: _policyNum,
                readOnly: _isInternal,
                style: _fieldValueStyle,
                decoration: _fieldDeco().copyWith(
                  hintText: _isInternal
                      ? 'Auto-fetched on search'
                      : 'Enter policy number',
                ),
                validator: (v) => Validators.requiredField(v, 'Policy number'),
              ),
            );
            final claimsNumberField = _labeledField(
              'Claims Number',
              TextFormField(
                controller: _claimsNumber,
                style: _fieldValueStyle,
                decoration: _fieldDeco().copyWith(
                  hintText: 'Enter claims number',
                ),
              ),
            );
            final vehicleNoField = _isExternal
                ? _labeledField(
                    'Vehicle No',
                    TextFormField(
                      controller: _externalVehicleNo,
                      style: _fieldValueStyle,
                      decoration: _fieldDeco().copyWith(
                        hintText: 'Enter vehicle number',
                      ),
                      textCapitalization: TextCapitalization.characters,
                    ),
                  )
                : null;
            final insurerField = _buildInsurerOriginField();
            final brokerField = _buildBrokerOriginField();
            final partnerField = _buildPartnerOriginField();
            if (twoCol) {
              return Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Expanded(child: policyField),
                      const SizedBox(width: 16),
                      Expanded(child: claimsNumberField),
                    ],
                  ),
                  const SizedBox(height: 12),
                  if (vehicleNoField != null)
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: vehicleNoField),
                        const SizedBox(width: 16),
                        Expanded(child: insurerField),
                      ],
                    )
                  else
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: insurerField),
                        const SizedBox(width: 16),
                        Expanded(child: brokerField),
                      ],
                    ),
                  const SizedBox(height: 12),
                  if (vehicleNoField != null)
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: brokerField),
                        const SizedBox(width: 16),
                        Expanded(child: partnerField),
                      ],
                    )
                  else
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Expanded(child: partnerField),
                        const Spacer(),
                      ],
                    ),
                ],
              );
            }
            return Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                policyField,
                const SizedBox(height: 12),
                claimsNumberField,
                const SizedBox(height: 12),
                if (vehicleNoField != null)
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Expanded(child: vehicleNoField),
                      const SizedBox(width: 16),
                      Expanded(child: insurerField),
                    ],
                  )
                else
                  insurerField,
                const SizedBox(height: 12),
                brokerField,
                const SizedBox(height: 12),
                partnerField,
              ],
            );
          },
        ),
      ],
    );
  }

  Widget _searchFieldColumn({
    required String label,
    required TextEditingController controller,
    required List<Map<String, dynamic>> hits,
    required bool fromVehicle,
    required Future<void> Function([String? query]) onSearch,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(label, style: _labelStyle),
        const SizedBox(height: 6),
        _buildSearchBox(
          controller: controller,
          hint: label,
          onChanged: (val) async {
            if (val.trim().isNotEmpty) {
              await onSearch(val);
            } else if (mounted) {
              setState(() {
                if (fromVehicle) {
                  _vehicleHits = [];
                } else {
                  _policyHits = [];
                }
              });
            }
          },
          onSearchTap: () => onSearch(),
        ),
        if (hits.isNotEmpty)
          PolicySearchResultsList(
            rows: hits,
            fromVehicle: fromVehicle,
            onSelected: (row) => _applyPolicySelection(row, fromVehicle: fromVehicle),
          ),
      ],
    );
  }

  /// Search input with teal search button — matches [endorsomentUpdateValidation].
  Widget _buildSearchBox({
    required TextEditingController controller,
    required String hint,
    required ValueChanged<String> onChanged,
    required VoidCallback onSearchTap,
  }) {
    return Container(
      height: 40,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: _border),
      ),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: controller,
              onChanged: onChanged,
              onSubmitted: (_) => onSearchTap(),
              style: _fieldValueStyle,
              decoration: InputDecoration(
                hintText: hint,
                hintStyle: _fieldHintStyle,
                filled: false,
                isDense: true,
                border: InputBorder.none,
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
                disabledBorder: InputBorder.none,
                errorBorder: InputBorder.none,
                focusedErrorBorder: InputBorder.none,
                contentPadding: const EdgeInsets.symmetric(
                  horizontal: 12,
                  vertical: 12,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(right: 6),
            child: Material(
              color: _teal,
              borderRadius: BorderRadius.circular(8),
              child: InkWell(
                onTap: _searchLoading ? null : onSearchTap,
                borderRadius: BorderRadius.circular(8),
                child: SizedBox(
                  width: 32,
                  height: 32,
                  child: _searchLoading
                      ? const Padding(
                          padding: EdgeInsets.all(8),
                          child: CircularProgressIndicator(
                            strokeWidth: 2,
                            color: Colors.white,
                          ),
                        )
                      : const Icon(
                          Icons.search,
                          color: Colors.white,
                          size: 18,
                        ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _stepClaimDetails() {
    Map<String, dynamic>? selectedType;
    for (final m in _claimTypes) {
      if (m['id'].toString() == _selectedClaimTypeId) {
        selectedType = m;
        break;
      }
    }
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        _sectionTitle('CLAIM DETAILS'),
        LayoutBuilder(
          builder: (context, c) {
            final wide = c.maxWidth > 520;
            Widget gridRow(List<Widget> children) {
              if (!wide) {
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: children
                      .map((w) => Padding(
                            padding: const EdgeInsets.only(bottom: 12),
                            child: w,
                          ))
                      .toList(),
                );
              }
              return Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Expanded(child: children[0]),
                  const SizedBox(width: 16),
                  Expanded(child: children[1]),
                ],
              );
            }

            return Column(
              children: [
                gridRow([
                  _labeledField(
                    'Claim Type *',
                    DropdownSearch<Map<String, dynamic>>(
                      selectedItem: selectedType,
                      items: (f, i) => _claimTypes,
                      itemAsString: _claimTypeLabel,
                      compareFn: (a, b) =>
                          (a['id']?.toString() ?? '') ==
                          (b['id']?.toString() ?? ''),
                      onChanged: (v) {
                        setState(() {
                          _selectedClaimTypeId = v?['id']?.toString();
                        });
                      },
                      decoratorProps: DropDownDecoratorProps(
                        decoration: _fieldDeco().copyWith(
                          hintText: 'Select claim type',
                        ),
                        baseStyle: _fieldValueStyle,
                      ),
                      popupProps: const PopupProps.menu(
                        showSearchBox: true,
                        fit: FlexFit.loose,
                      ),
                    ),
                  ),
                  _labeledField(
                    'Date of Accident *',
                    TextFormField(
                      controller: _accidentDt,
                      readOnly: true,
                      style: _fieldValueStyle,
                      onTap: _pickAccidentDate,
                      decoration: _fieldDeco().copyWith(
                        hintText: 'Select date',
                        suffixIcon: IconButton(
                          tooltip: 'Pick date',
                          icon: const Icon(
                            Icons.event_outlined,
                            size: 18,
                            color: _muted,
                          ),
                          onPressed: _pickAccidentDate,
                        ),
                        suffixIconConstraints: const BoxConstraints(
                          minWidth: 36,
                          minHeight: _fieldHeight,
                        ),
                      ),
                    ),
                  ),
                ]),
                const SizedBox(height: 10),
                gridRow([
                  _labeledField(
                    'Place of Accident *',
                    TextFormField(
                      controller: _accidentPlace,
                      style: _fieldValueStyle,
                      decoration: _fieldDeco(),
                    ),
                  ),
                  _buildClaimStatusField(),
                ]),
              ],
            );
          },
        ),
        const SizedBox(height: 10),
        _labeledField(
          'Claim Description',
          TextFormField(
            controller: _claimDesc,
            style: _fieldValueStyle,
            minLines: 3,
            maxLines: 5,
            decoration: _fieldDeco().copyWith(
              constraints: const BoxConstraints(minHeight: 80),
            ),
          ),
          uniformHeight: false,
        ),
        const SizedBox(height: 20),
        _sectionTitle('SURVEYORS'),
        LayoutBuilder(
          builder: (context, c) {
            final wide = c.maxWidth > 520;
            Widget pair(Widget a, Widget b) {
              if (!wide) {
                return Column(
                  children: [
                    Padding(
                      padding: const EdgeInsets.only(bottom: 12),
                      child: a,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(bottom: 12),
                      child: b,
                    ),
                  ],
                );
              }
              return Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Expanded(child: a),
                  const SizedBox(width: 16),
                  Expanded(child: b),
                ],
              );
            }
            return Column(
              children: [
                pair(
                  _labeledField(
                    'Spot Surveyor Name',
                    TextFormField(
                      controller: _spotSurveyorName,
                      style: _fieldValueStyle,
                      decoration: _fieldDeco(),
                    ),
                  ),
                  _labeledField(
                    'Workshop Surveyor Name',
                    TextFormField(
                      controller: _workshopSurveyorName,
                      style: _fieldValueStyle,
                      decoration: _fieldDeco(),
                    ),
                  ),
                ),
                SizedBox(height: 10),
                pair(
                  _labeledField(
                    'Spot Surveyor Contact',
                    _surveyorContactField(_spotSurveyorContact),
                  ),
                  _labeledField(
                    'Workshop Surveyor Contact',
                    _surveyorContactField(_workshopSurveyorContact),
                  ),
                ),
                SizedBox(height: 10),
              ],
            );
          },
        ),
      ],
    );
  }

  Widget _stepUpload() {
    final cards = <Widget>[];
    for (var i = 0; i < _existingFiles.length && cards.length < _maxFileSlots; i++) {
      cards.add(_existingFileCard(i));
    }
    for (var i = 0; i < _pickedFiles.length && cards.length < _maxFileSlots; i++) {
      cards.add(_pickedFileCard(i));
    }
    while (cards.length < _maxFileSlots) {
      cards.add(_emptyUploadCard(cards.length + 1));
    }

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        _sectionTitle('DOCUMENTS & REMARKS'),
        GridView.count(
          crossAxisCount: 3,
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
          childAspectRatio: 1.25,
          children: cards,
        ),
        const SizedBox(height: 16),
        _labeledField(
          'Remark',
          TextFormField(
            controller: _remark,
            style: _fieldValueStyle,
            minLines: 3,
            maxLines: 5,
            decoration: _fieldDeco().copyWith(
              hintText: 'Add a note...',
              constraints: const BoxConstraints(minHeight: 80),
            ),
          ),
          uniformHeight: false,
        ),
      ],
    );
  }

  String _existingFileName(Map<String, dynamic> file) {
    final candidates = [
      'file_name',
      'fileName',
      'name',
      'original_name',
      'original_file_name',
    ];
    for (final k in candidates) {
      final v = file[k];
      if (v != null && v.toString().trim().isNotEmpty) {
        final raw = v.toString().trim();
        return raw.split('/').last;
      }
    }
    return 'Document';
  }

  Widget _existingFileCard(int index) {
    final file = _existingFiles[index];
    final name = _existingFileName(file);
    return _fileCard(
      name: name,
      onRemove: () => _removeExistingFile(index),
      onDownload: () => _downloadExistingFile(file),
      iconColor: _teal,
      labelColor: _teal,
    );
  }

  Widget _pickedFileCard(int index) {
    final file = _pickedFiles[index];
    return _fileCard(
      name: file.name,
      onRemove: () => _removePickedFile(index),
      onDownload: () => _downloadPickedFile(file),
      iconColor: _teal,
      labelColor: _teal,
    );
  }

  Widget _fileCard({
    required String name,
    required VoidCallback onRemove,
    VoidCallback? onDownload,
    required Color iconColor,
    required Color labelColor,
  }) {
    return Container(
      padding: const EdgeInsets.fromLTRB(8, 8, 4, 8),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: _teal),
        color: const Color(0xFFE8F7F5),
      ),
      child: Stack(
        children: [
          Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.insert_drive_file_outlined, color: iconColor),
                const SizedBox(height: 4),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 6),
                  child: Text(
                    name,
                    textAlign: TextAlign.center,
                    maxLines: 2,
                    overflow: TextOverflow.ellipsis,
                    style: GoogleFonts.inter(
                      fontSize: 11,
                      color: labelColor,
                      fontWeight: FontWeight.w500,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Positioned(
            top: 0,
            right: 0,
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                if (onDownload != null)
                  Material(
                    color: Colors.transparent,
                    child: InkWell(
                      onTap: onDownload,
                      borderRadius: BorderRadius.circular(20),
                      child: Container(
                        padding: const EdgeInsets.all(2),
                        child: const Icon(
                          Icons.download_rounded,
                          size: 18,
                          color: Color(0xFF1565C0),
                        ),
                      ),
                    ),
                  ),
                Material(
                  color: Colors.transparent,
                  child: InkWell(
                    onTap: onRemove,
                    borderRadius: BorderRadius.circular(20),
                    child: Container(
                      padding: const EdgeInsets.all(2),
                      child: const Icon(
                        Icons.cancel,
                        size: 18,
                        color: Color(0xFFC62828),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _emptyUploadCard(int slotNumber) {
    return Material(
      color: Colors.transparent,
      child: InkWell(
        onTap: _pickFile,
        borderRadius: BorderRadius.circular(8),
        child: Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            border: Border.all(color: const Color(0xFFCBD5E1)),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.upload_file_outlined, color: _muted),
              const SizedBox(height: 4),
              Text(
                'Upload $slotNumber',
                textAlign: TextAlign.center,
                style: GoogleFonts.inter(fontSize: 11, color: _muted),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _stepStatus() {
    String? typeLabel;
    if (_selectedClaimTypeId != null) {
      for (final e in _claimTypes) {
        if (e['id']?.toString() == _selectedClaimTypeId) {
          typeLabel = _claimTypeLabel(e);
          break;
        }
      }
    }
    final vn = _isExternal
        ? (_externalVehicleNo.text.trim().isNotEmpty
            ? _externalVehicleNo.text.trim()
            : '—')
        : (_vehicleSearch.text.trim().isNotEmpty
            ? _vehicleSearch.text.trim()
            : '—');
    final pn =
        _policyNum.text.trim().isNotEmpty ? _policyNum.text.trim() : '—';
    final statusLabel =
        _statusLabelForId(_selectedClaimStatusId) ?? '—';

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        _sectionTitle('REVIEW'),
        Container(
          width: double.infinity,
          padding: const EdgeInsets.all(14),
          decoration: BoxDecoration(
            color: const Color(0xFFE8F7F5),
            borderRadius: BorderRadius.circular(10),
          ),
          child: Text.rich(
            TextSpan(
              style: GoogleFonts.inter(
                fontSize: 12,
                color: const Color(0xFF334155),
              ),
              children: [
                TextSpan(
                  text: 'Review: ',
                  style: GoogleFonts.inter(
                    fontWeight: FontWeight.w700,
                    fontSize: 12,
                    color: const Color(0xFF334155),
                  ),
                ),
                TextSpan(
                  text:
                      'Vehicle $vn · Policy $pn · ${typeLabel ?? "—"} · Status ',
                ),
                TextSpan(
                  text: statusLabel,
                  style: GoogleFonts.inter(
                    fontWeight: FontWeight.w700,
                    fontSize: 12,
                    color: const Color(0xFF334155),
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  TextStyle get _labelStyle => GoogleFonts.inter(
        fontSize: 12,
        color: _muted,
        fontWeight: FontWeight.w500,
      );

  TextStyle get _fieldValueStyle => GoogleFonts.inter(
        fontSize: 12,
        fontWeight: FontWeight.w400,
        color: const Color(0xFF0F172A),
      );

  TextStyle get _fieldHintStyle => GoogleFonts.inter(
        fontSize: 11,
        fontWeight: FontWeight.w400,
        color: const Color(0xFF94A3B8),
      );

  ThemeData _formTheme(BuildContext context) {
    final base = GoogleFonts.interTextTheme(Theme.of(context).textTheme);
    return Theme.of(context).copyWith(
      textTheme: base.copyWith(
        bodyLarge: _fieldValueStyle,
        bodyMedium: _fieldValueStyle,
        titleMedium: _fieldValueStyle,
        labelLarge: _fieldValueStyle,
      ),
      inputDecorationTheme: InputDecorationTheme(
        isDense: true,
        filled: true,
        fillColor: Colors.white,
        hintStyle: _fieldHintStyle,
        constraints: const BoxConstraints(minHeight: _fieldHeight),
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 12, vertical: 11),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
          borderSide: const BorderSide(color: _border),
        ),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
          borderSide: const BorderSide(color: _border),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
          borderSide: const BorderSide(color: _teal, width: 1.5),
        ),
      ),
    );
  }

  Widget _uniformHeightField(Widget child) {
    return SizedBox(
      height: _fieldHeight,
      child: child,
    );
  }

  InputDecoration _fieldDeco() {
    return InputDecoration(
      isDense: true,
      filled: true,
      fillColor: Colors.white,
      hintStyle: _fieldHintStyle,
      constraints: const BoxConstraints(minHeight: _fieldHeight),
      contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 11),
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: const BorderSide(color: _border),
      ),
      enabledBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: const BorderSide(color: _border),
      ),
      focusedBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: const BorderSide(color: _teal, width: 1.5),
      ),
    );
  }

  static final RegExp _tenDigitContact = RegExp(r'^[0-9]{10}$');

  static String _contactDigitsOnly(String raw) {
    final digits = raw.replaceAll(RegExp(r'\D'), '');
    if (digits.length <= 10) return digits;
    return digits.substring(0, 10);
  }

  bool _isValidSurveyorContact(String? value) {
    final v = value?.trim() ?? '';
    if (v.isEmpty) return true;
    return _tenDigitContact.hasMatch(v);
  }

  Widget _buildClaimStatusField() {
    return _labeledField(
      'Claim Status',
      DropdownButtonFormField<String>(
        key: ValueKey('claim-status-$_selectedClaimStatusId'),
        initialValue: _selectedClaimStatusId,
        isExpanded: true,
        iconSize: 20,
        style: _fieldValueStyle,
        decoration: _fieldDeco(),
        items: _claimStatuses
            .map((m) {
              final id = m['id']?.toString() ?? '';
              final label = _claimStatusLabel(m) ?? id;
              return DropdownMenuItem(
                value: id.isEmpty ? null : id,
                child: Text(label, style: _fieldValueStyle),
              );
            })
            .where((e) => e.value != null)
            .toList(),
        onChanged: (v) => setState(() => _selectedClaimStatusId = v),
      ),
    );
  }

  Widget _surveyorContactField(TextEditingController controller) {
    return TextFormField(
      controller: controller,
      style: _fieldValueStyle,
      keyboardType: TextInputType.number,
      inputFormatters: [
        FilteringTextInputFormatter.digitsOnly,
        LengthLimitingTextInputFormatter(10),
      ],
      decoration: _fieldDeco().copyWith(
        hintText: '10-digit number',
      ),
      validator: (v) => Validators.nonReqphone(v, 'Contact'),
    );
  }

  Widget _labeledField(
    String label,
    Widget field, {
    bool uniformHeight = true,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: _labelStyle),
        const SizedBox(height: 6),
        uniformHeight ? _uniformHeightField(field) : field,
      ],
    );
  }

  Widget _footerActions() {
    return Row(
      children: [
        Text(
          '* Required fields',
          style: GoogleFonts.inter(fontSize: 11, color: _muted),
        ),
        const Spacer(),
        if (_step > 0)
          OutlinedButton(
            onPressed: _goBack,
            style: OutlinedButton.styleFrom(
              foregroundColor: Colors.black87,
              side: const BorderSide(color: _border),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
            ),
            child: const Text('Back'),
          ),
        const SizedBox(width: 8),
        OutlinedButton(
          onPressed: () => Navigator.pop(context),
          style: OutlinedButton.styleFrom(
            foregroundColor: Colors.black87,
            side: const BorderSide(color: _border),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          child: const Text('Cancel'),
        ),
        const SizedBox(width: 8),
        if (_step < 3)
          FilledButton(
            onPressed: _goNext,
            style: FilledButton.styleFrom(
              backgroundColor: _teal,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
            ),
            child: const Text('Continue'),
          )
        else
          FilledButton.icon(
            onPressed: _isSaveClaimDisabled ? null : _submit,
            icon: const Icon(Icons.save_outlined, size: 18),
            label: const Text('Save Claim'),
            style: FilledButton.styleFrom(
              backgroundColor: _teal,
              disabledBackgroundColor: const Color(0xFFCBD5E1),
              disabledForegroundColor: Colors.white,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
            ),
          ),
      ],
    );
  }
}
