import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/req_models/submit_refund_request_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/is_refundable_invoice_response_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/refund_request_list_response_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/models/resp_models/refundable_invoices_response_model.dart';
import 'package:hmg_patient_app_new/features/refund_request/refund_request_repo.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';

enum RefundTabType { invoices, hmgWallet }

extension RefundTabTypeExtension on RefundTabType {
  String toDisplayString() {
    switch (this) {
      case RefundTabType.hmgWallet:
        return LocaleKeys.hmgWallet.tr();
      case RefundTabType.invoices:
        return LocaleKeys.bankTransfer.tr();
    }
  }
}

/// Lightweight model representing a refundable procedure line item.
class RefundableProcedure {
  final int procedureId;
  final String procedureName;
  final String? procedureNameN;
  final double patientPaidAmount;
  bool isSelected;
  String? refundReason;

  /// Raw values from the API — used when building the SubmitRefund payload
  final int? apiLineItemNo;
  final String? apiProcedureId;

  RefundableProcedure({
    required this.procedureId,
    required this.procedureName,
    this.procedureNameN,
    required this.patientPaidAmount,
    this.isSelected = false,
    this.refundReason,
    this.apiLineItemNo,
    this.apiProcedureId,
  });
}

/// Lightweight model for an invoice dropdown item.
class RefundInvoiceItem {
  final int invoiceNo;
  final int appointmentNo;
  final int projectId;
  final String displayLabel;

  const RefundInvoiceItem({
    required this.invoiceNo,
    required this.appointmentNo,
    required this.projectId,
    required this.displayLabel,
  });
}

class RefundRequestViewModel extends ChangeNotifier {
  final AppState appState;
  final RefundRequestRepo refundRequestRepo;
  final ErrorHandlerService errorHandlerService;

  RefundRequestViewModel({
    required this.appState,
    required this.refundRequestRepo,
    required this.errorHandlerService,
  });

  // ── Tab ──────────────────────────────────────────────────────────────────
  RefundTabType selectedTab = RefundTabType.invoices;

  void setTab(RefundTabType tab) {
    selectedTab = tab;
    notifyListeners();
  }

  // ── Step (0 = select invoice+hospital, 1 = select procedures) ────────────
  int currentStep = 0;

  void goToStep(int step) {
    currentStep = step;
    notifyListeners();
  }

  void nextStep() {
    if (currentStep < 1) {
      currentStep++;
      notifyListeners();
    }
  }

  void previousStep() {
    if (currentStep > 0) {
      currentStep--;
      notifyListeners();
    }
  }

  void resetState() {
    currentStep = 0;
    procedures.clear();
    needITGApproval = false;
    selectedPaymentMethod = null;
    selectedRefundReason = null;
    isProceduresLoading = false;
    isInvoiceDetailsLoading = false;
    invoiceDetails = null;
    _ibanError = null;
    _descriptionError = null;
    // Use Future.microtask to avoid calling notifyListeners while tree is locked (e.g. during dispose)
    Future.microtask(() {
      if (hasListeners) notifyListeners();
    });
  }

  // ── Hospital selection ────────────────────────────────────────────────────
  HospitalsModel? selectedHospital;

  void setSelectedHospital(HospitalsModel? hospital) {
    selectedHospital = hospital;
    // Reset invoice and procedures whenever hospital changes
    selectedInvoice = null;
    procedures.clear();
    refundableInvoicesList.clear();
    notifyListeners();

    // Fetch refundable invoices for the selected hospital's project
    if (hospital != null && hospital.iD != null) {
      final projectID = hospital.iD is int ? hospital.iD as int : int.tryParse(hospital.iD.toString());
      if (projectID != null) {
        getRefundableInvoices(projectID: projectID);
      }
    }
  }


  void clearHospital() {
    selectedHospital = null;
    selectedInvoice = null;
    procedures.clear();
    notifyListeners();
  }

  // ── Invoice selection ─────────────────────────────────────────────────────
  RefundInvoiceItem? selectedInvoice;
  List<RefundableInvoiceItem> refundableInvoicesList = [];
  bool isRefundableInvoicesLoading = false;

  Future<void> getRefundableInvoices({
    required int projectID,
    Function(dynamic)? onSuccess,
    Function(String)? onError,
  }) async {
    isRefundableInvoicesLoading = true;
    refundableInvoicesList.clear();
    notifyListeners();

    final result = await refundRequestRepo.getRefundableInvoices(projectID: projectID);

    result.fold(
      (failure) {
        isRefundableInvoicesLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isRefundableInvoicesLoading = false;
        if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
          refundableInvoicesList = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) onSuccess(apiResponse);
        } else {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to load invoices");
          }
        }
      },
    );
  }

  void setSelectedInvoice(RefundInvoiceItem invoice) {
    selectedInvoice = invoice;
    procedures.clear();
    notifyListeners();
  }

  void clearInvoice() {
    selectedInvoice = null;
    procedures.clear();
    notifyListeners();
  }

  // ── Procedures ────────────────────────────────────────────────────────────
  List<RefundableProcedure> procedures = [];
  bool isProceduresLoading = false;
  bool needITGApproval = false;

  void setProcedures(List<RefundableProcedure> list, {bool needsITGApproval = false}) {
    procedures = list;
    needITGApproval = needsITGApproval;
    isProceduresLoading = false;
    notifyListeners();
  }

  void toggleProcedureSelection(RefundableProcedure procedure) {
    procedure.isSelected = !procedure.isSelected;
    notifyListeners();
  }

  void setRefundReasonForProcedure(RefundableProcedure procedure, String reason) {
    procedure.refundReason = reason;
    notifyListeners();
  }

  bool get isAllSelected => procedures.isNotEmpty && procedures.every((p) => p.isSelected);

  void toggleSelectAll() {
    final selectAll = !isAllSelected;
    for (final p in procedures) {
      p.isSelected = selectAll;
    }
    notifyListeners();
  }

  List<RefundableProcedure> get selectedProcedures => procedures.where((p) => p.isSelected).toList();

  bool get allSelectedProceduresHaveReasons =>
      selectedProcedures.isNotEmpty && selectedProcedures.every((p) => p.refundReason != null && p.refundReason!.isNotEmpty);

  // ── Calculation ───────────────────────────────────────────────────────────
  double get totalRefundableAmount {
    double total = 0.0;
    for (final p in selectedProcedures) {
      total += p.patientPaidAmount;
    }
    return total;
  }

  // ── Bank Details & Description ────────────────────────────────────────────
  String bankDetails = '';
  String description = '';

  // ── Bank Transfer Error Tracking ──────────────────────────────────────────
  String? _ibanError;
  String? _descriptionError;

  String? get ibanError => _ibanError;

  String? get descriptionError => _descriptionError;

  void clearIbanError() {
    _ibanError = null;
    notifyListeners();
  }

  void clearDescriptionError() {
    _descriptionError = null;
    notifyListeners();
  }

  bool validateBankTransferForm({required String iban, required String description}) {
    bool isValid = true;
    _ibanError = null;
    _descriptionError = null;

    if (iban.trim().isEmpty) {
      _ibanError = LocaleKeys.ibanIsRequired.tr();
      isValid = false;
    } else {
      if (!ValidationUtils.validateIban(iban.trim())) {
        _ibanError = LocaleKeys.invalidIbanFormat.tr();
        isValid = false;
      }
    }

    if (description.trim().isEmpty) {
      _descriptionError = LocaleKeys.descriptionIsRequired.tr();
      isValid = false;
    }

    notifyListeners();
    return isValid;
  }

  // ── Payment Method ────────────────────────────────────────────────────────
  RefundTabType? selectedPaymentMethod;

  void setPaymentMethod(RefundTabType method) {
    selectedPaymentMethod = method;
    notifyListeners();
  }

  // ── Refund Reason ─────────────────────────────────────────────────────────
  String? selectedRefundReason;

  void setRefundReason(String? reason) {
    selectedRefundReason = reason;
    notifyListeners();
  }

  /// Used by text controllers in the UI layer to trigger a rebuild.
  void triggerRebuild() => notifyListeners();

  void setBankDetails(String value) {
    bankDetails = value;
    notifyListeners();
  }

  void setDescription(String value) {
    description = value;
    notifyListeners();
  }

  // ── Validation ────────────────────────────────────────────────────────────
  bool get isStep1Valid => selectedHospital != null && selectedInvoice != null;

  bool get isStep2Valid => selectedProcedures.isNotEmpty && selectedPaymentMethod != null;

  // ── Invoice Details ───────────────────────────────────────────────────────
  RefundableInvoicesDetailsObject? invoiceDetails;
  bool isInvoiceDetailsLoading = false;

  Future<void> getRefundableInvoicesDetails({
    required int invoiceNo,
    required int projectId,
    Function(RefundableInvoicesDetailsObject)? onSuccess,
    Function(String)? onError,
  }) async {
    isInvoiceDetailsLoading = true;
    isProceduresLoading = true;
    invoiceDetails = null;
    procedures.clear();
    notifyListeners();

    final result = await refundRequestRepo.getRefundableInvoicesDetails(invoiceNo: invoiceNo, projectId: projectId);

    result.fold(
      (failure) {
        isInvoiceDetailsLoading = false;
        isProceduresLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isInvoiceDetailsLoading = false;
        isProceduresLoading = false;
        if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
          invoiceDetails = apiResponse.data;
          notifyListeners();
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? LocaleKeys.failed.tr());
          }
        }
      },
    );
  }

  // ── Refundable Advances ───────────────────────────────────────────────────
  RefundableAdvancesObject? refundableAdvances;
  bool isRefundableAdvancesLoading = false;

  Future<void> getRefundableAdvances({
    Function(RefundableAdvancesObject)? onSuccess,
    Function(String)? onError,
  }) async {
    isRefundableAdvancesLoading = true;
    refundableAdvances = null;
    notifyListeners();

    final result = await refundRequestRepo.getRefundableAdvances();

    result.fold(
      (failure) {
        isRefundableAdvancesLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isRefundableAdvancesLoading = false;
        if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
          refundableAdvances = apiResponse.data;
          notifyListeners();
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to load refundable advances");
          }
        }
      },
    );
  }

  // ── Refund Status ─────────────────────────────────────────────────────────
  RefundStatusModel? refundStatus;
  bool isRefundStatusLoading = false;

  Future<void> getRefundStatus({
    required String referenceNumber,
    Function(RefundStatusModel)? onSuccess,
    Function(String)? onError,
  }) async {
    isRefundStatusLoading = true;
    refundStatus = null;
    notifyListeners();

    final result = await refundRequestRepo.getRefundStatus(referenceNumber: referenceNumber);

    result.fold(
      (failure) {
        isRefundStatusLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isRefundStatusLoading = false;
        if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
          refundStatus = apiResponse.data;
          notifyListeners();
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to load refund status");
          }
        }
      },
    );
  }

  // ── Refund Invoices ───────────────────────────────────────────────────────
  List<RefundInvoiceModel> refundInvoicesList = [];
  bool isRefundInvoicesLoading = false;

  Future<void> getRefundInvoices({
    required int refundType,
    String invoiceNumberQuery = "",
    Function(List<RefundInvoiceModel>)? onSuccess,
    Function(String)? onError,
  }) async {
    isRefundInvoicesLoading = true;
    refundInvoicesList.clear();
    notifyListeners();

    final resolvedPatientId = appState.getAuthenticatedUser()?.patientId?.toString() ?? "";
    final result = await refundRequestRepo.getRefundInvoices(
      refundType: refundType,
      patientId: resolvedPatientId,
      invoiceNumberQuery: invoiceNumberQuery,
    );

    result.fold(
      (failure) {
        isRefundInvoicesLoading = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isRefundInvoicesLoading = false;
        if (apiResponse.messageStatus == 1 && apiResponse.data != null) {
          refundInvoicesList = apiResponse.data!;
          notifyListeners();
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          notifyListeners();
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to load refund invoices");
          }
        }
      },
    );
  }

  // ── Submit Refund ─────────────────────────────────────────────────────────
  bool isSubmitting = false;

  Future<void> submitInvoiceRefundRequest({
    required SubmitRefundRequestModel request,
    Function(SubmitRefundResponseModel)? onSuccess,
    Function(String)? onError,
  }) async {
    isSubmitting = true;
    notifyListeners();

    final result = await refundRequestRepo.submitRefund(request: request);

    result.fold(
      (failure) {
        isSubmitting = false;
        notifyListeners();
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        isSubmitting = false;
        notifyListeners();
        if (apiResponse.data != null) {
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to submit refund");
          }
        }
      },
    );
  }

  // ── Check Is Refundable Invoice ───────────────────────────────────────────

  Future<void> checkIsRefundableInvoice({
    required int invoiceNo,
    required int projectId,
    Function(IsRefundableInvoiceObject)? onSuccess,
    Function(String)? onError,
  }) async {
    final result = await refundRequestRepo.checkIsRefundableInvoice(invoiceNo: invoiceNo, projectId: projectId);

    result.fold(
      (failure) {
        if (onError != null) {
          onError(failure.message);
        } else {
          errorHandlerService.handleError(failure: failure);
        }
      },
      (apiResponse) {
        if (apiResponse.data != null) {
          if (onSuccess != null) onSuccess(apiResponse.data!);
        } else {
          if (onError != null) {
            onError(apiResponse.errorMessage ?? "Failed to check refundable status");
          }
        }
      },
    );
  }
}
