import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/services/api_service.dart';
import 'package:on_field_work/helpers/widgets/my_snackbar.dart';
import 'package:on_field_work/model/employees/employee_model.dart';
import 'package:on_field_work/model/finance/payment_request_details_model.dart';
import 'package:on_field_work/model/expense/payment_types_model.dart';
import 'package:on_field_work/helpers/widgets/time_stamp_image_helper.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:mime/mime.dart';
import 'package:on_field_work/controller/finance/payment_request_controller.dart';

class PaymentRequestDetailController extends GetxController {
  final Rx<PaymentRequestData?> paymentRequest = Rx<PaymentRequestData?>(null);
  final RxBool isLoading = false.obs;
  final RxString errorMessage = ''.obs;
  final RxList<PaymentModeModel> paymentModes = <PaymentModeModel>[].obs;

  // Employee selection
  final Rx<EmployeeModel?> selectedReimbursedBy = Rx<EmployeeModel?>(null);
  final RxList<EmployeeModel> allEmployees = <EmployeeModel>[].obs;
  final RxList<EmployeeModel> employeeSearchResults = <EmployeeModel>[].obs;
  final TextEditingController employeeSearchController =
      TextEditingController();
  PaymentRequestController get paymentRequestController =>
      Get.find<PaymentRequestController>();
  final RxBool isSearchingEmployees = false.obs;

  // Attachments
  final RxList<File> attachments = <File>[].obs;
  final RxList<Map<String, dynamic>> existingAttachments =
      <Map<String, dynamic>>[].obs;
  final isProcessingAttachment = false.obs;

  // Payment mode
  final selectedPaymentMode = Rxn<PaymentModeModel>();

  // Text controllers for form
  final TextEditingController locationController = TextEditingController();
  final TextEditingController gstNumberController = TextEditingController();

  // Form submission state
  final RxBool isSubmitting = false.obs;

  late String _requestId;
  bool _isInitialized = false;
  RxBool paymentSheetOpened = false.obs;
  final ImagePicker _picker = ImagePicker();

  /// Initialize controller
  void init(String requestId) {
    if (_isInitialized) return;
    _isInitialized = true;

    _requestId = requestId;

    // Fetch payment request details + employees concurrently
    Future.wait([
      fetchPaymentRequestDetail(),
      fetchAllEmployees(),
      fetchPaymentModes(),
    ]);
  }

  /// Generic API wrapper for error handling
  Future<T?> _apiCallWrapper<T>(
      Future<T?> Function() apiCall, String operationName) async {
    isLoading.value = true;
    errorMessage.value = '';
    try {
      final result = await apiCall();
      return result;
    } catch (e) {
      errorMessage.value = 'Error during $operationName: $e';
      showAppSnackbar(
          title: 'Error',
          message: errorMessage.value,
          type: SnackbarType.error);
      return null;
    } finally {
      isLoading.value = false;
    }
  }

  /// Fetch payment request details
  Future<void> fetchPaymentRequestDetail() async {
    isLoading.value = true;
    try {
      final response =
          await ApiService.getExpensePaymentRequestDetailApi(_requestId);
      if (response != null) {
        paymentRequest.value = response.data;
      } else {
        errorMessage.value = "Failed to fetch payment request details";
        showAppSnackbar(
          title: 'Error',
          message: errorMessage.value,
          type: SnackbarType.error,
        );
      }
    } catch (e) {
      errorMessage.value = "Error fetching payment request details: $e";
      showAppSnackbar(
        title: 'Error',
        message: errorMessage.value,
        type: SnackbarType.error,
      );
    } finally {
      isLoading.value = false;
    }
  }

  /// Pick files from gallery or file picker
  Future<void> pickAttachments() async {
    try {
      final result = await FilePicker.platform.pickFiles(
        type: FileType.custom,
        allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'],
        allowMultiple: true,
      );
      if (result != null) {
        attachments.addAll(
          result.paths.whereType<String>().map(File.new),
        );
      }
    } catch (e) {
      _errorSnackbar("Attachment error: $e");
    }
  }

  void removeAttachment(File file) => attachments.remove(file);

  Future<void> pickFromCamera() async {
    try {
      final pickedFile = await _picker.pickImage(source: ImageSource.camera);
      if (pickedFile != null) {
        isProcessingAttachment.value = true;
        File imageFile = File(pickedFile.path);

        File timestampedFile = await TimestampImageHelper.addTimestamp(
          imageFile: imageFile,
        );

        attachments.add(timestampedFile);
        attachments.refresh();
      }
    } catch (e) {
      _errorSnackbar("Camera error: $e");
    } finally {
      isProcessingAttachment.value = false;
    }
  }

  // --- Location ---
  final RxBool isFetchingLocation = false.obs;

  Future<void> fetchCurrentLocation() async {
    isFetchingLocation.value = true;
    try {
      if (!await _ensureLocationPermission()) return;

      final position = await Geolocator.getCurrentPosition();
      final placemarks =
          await placemarkFromCoordinates(position.latitude, position.longitude);

      locationController.text = placemarks.isNotEmpty
          ? [
              placemarks.first.name,
              placemarks.first.street,
              placemarks.first.locality,
              placemarks.first.administrativeArea,
              placemarks.first.country,
            ].where((e) => e?.isNotEmpty == true).join(", ")
          : "${position.latitude}, ${position.longitude}";
    } catch (e) {
      _errorSnackbar("Location error: $e");
    } finally {
      isFetchingLocation.value = false;
    }
  }

  Future<bool> _ensureLocationPermission() async {
    var permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied ||
        permission == LocationPermission.deniedForever) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied ||
          permission == LocationPermission.deniedForever) {
        _errorSnackbar("Location permission denied.");
        return false;
      }
    }
    if (!await Geolocator.isLocationServiceEnabled()) {
      _errorSnackbar("Location service disabled.");
      return false;
    }
    return true;
  }

  /// Fetch all employees
  Future<void> fetchAllEmployees() async {
    final response = await _apiCallWrapper(
        () => ApiService.getAllEmployees(), "fetch all employees");

    if (response != null && response.isNotEmpty) {
      try {
        allEmployees.assignAll(response.map((e) => EmployeeModel.fromJson(e)));
      } catch (e) {
        errorMessage.value = 'Failed to parse employee data: $e';
        showAppSnackbar(
            title: 'Error',
            message: errorMessage.value,
            type: SnackbarType.error);
      }
    } else {
      allEmployees.clear();
    }
  }

  /// Fetch payment modes
  Future<void> fetchPaymentModes() async {
    isLoading.value = true;
    try {
      final paymentModesData = await ApiService.getMasterPaymentModes();
      if (paymentModesData is List) {
        paymentModes.value =
            paymentModesData.map((e) => PaymentModeModel.fromJson(e)).toList();
      } else {
        paymentModes.clear();
        showAppSnackbar(
            title: 'Error',
            message: 'Failed to fetch payment modes',
            type: SnackbarType.error);
      }
    } catch (e) {
      paymentModes.clear();
      showAppSnackbar(
          title: 'Error',
          message: 'Error fetching payment modes: $e',
          type: SnackbarType.error);
    } finally {
      isLoading.value = false;
    }
  }

  /// Search employees
  Future<void> searchEmployees(String query) async {
    if (query.trim().isEmpty) {
      employeeSearchResults.clear();
      return;
    }

    isSearchingEmployees.value = true;
    try {
      final data =
          await ApiService.searchEmployeesBasic(searchString: query.trim());
      employeeSearchResults.assignAll(
        (data ?? []).map((e) => EmployeeModel.fromJson(e)),
      );
    } catch (e) {
      employeeSearchResults.clear();
    } finally {
      isSearchingEmployees.value = false;
    }
  }

  /// Update payment request status
  Future<bool> updatePaymentRequestStatus({
    required String statusId,
    required String comment,
    String? paidTransactionId,
    String? paidById,
    DateTime? paidAt,
    double? baseAmount,
    double? taxAmount,
    String? tdsPercentage,
  }) async {
    isLoading.value = true;

    try {
      final success = await ApiService.updateExpensePaymentRequestStatusApi(
        paymentRequestId: _requestId,
        statusId: statusId,
        comment: comment,
        paidTransactionId: paidTransactionId,
        paidById: paidById,
        paidAt: paidAt,
        baseAmount: baseAmount,
        taxAmount: taxAmount,
        tdsPercentage: tdsPercentage,
      );

      if (success) {
        // Controller refreshes the data but does not show snackbars.
        await fetchPaymentRequestDetail();
        paymentRequestController.fetchPaymentRequests();
      }

      return success;
    } catch (e) {
      // Controller returns false on error; UI will show the snackbar.
      return false;
    } finally {
      isLoading.value = false;
    }
  }

  // --- Snackbar Helper ---
  void _errorSnackbar(String msg, [String title = "Error"]) {
    showAppSnackbar(title: title, message: msg, type: SnackbarType.error);
  }

  // --- Payment Mode Selection ---
  void selectPaymentMode(PaymentModeModel mode) {
    selectedPaymentMode.value = mode;
  }

  // --- Submit Expense ---
  Future<bool> submitExpense(
      {required String statusId, String? comment}) async {
    if (selectedPaymentMode.value == null) return false;

    isSubmitting.value = true;
    try {
      // prepare attachments
      final success = await ApiService.createExpenseForPRApi(
        paymentModeId: selectedPaymentMode.value!.id,
        location: locationController.text,
        gstNumber: gstNumberController.text,
        paymentRequestId: _requestId,
        billAttachments: attachments.map((file) {
          final bytes = file.readAsBytesSync();
          final mimeType =
              lookupMimeType(file.path) ?? 'application/octet-stream';
          return {
            "fileName": file.path.split('/').last,
            "base64Data": base64Encode(bytes),
            "contentType": mimeType,
            "description": "",
            "fileSize": bytes.length,
            "isActive": true,
          };
        }).toList(),
        statusId: statusId,
        comment: comment ?? '',
      );

      if (success) {
        // Refresh the payment request details so the UI updates
        await fetchPaymentRequestDetail();
      }

      return success;
    } finally {
      isSubmitting.value = false;
    }
  }
}
