import 'dart:convert';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/services/api_service.dart';
import 'package:on_field_work/helpers/services/app_logger.dart';
import 'package:on_field_work/model/expense/expense_list_model.dart';
import 'package:on_field_work/model/expense/payment_types_model.dart';
import 'package:on_field_work/model/expense/expense_type_model.dart';
import 'package:on_field_work/model/expense/expense_status_model.dart';
import 'package:on_field_work/model/employees/employee_model.dart';
import 'package:on_field_work/helpers/widgets/my_snackbar.dart';
import 'package:flutter/material.dart';

class ExpenseController extends GetxController {
  final RxList<ExpenseModel> expenses = <ExpenseModel>[].obs;
  final RxBool isLoading = false.obs;
  final RxString errorMessage = ''.obs;

  // Master data
  final RxList<ExpenseTypeModel> expenseTypes = <ExpenseTypeModel>[].obs;
  final RxList<PaymentModeModel> paymentModes = <PaymentModeModel>[].obs;
  final RxList<ExpenseStatusModel> expenseStatuses = <ExpenseStatusModel>[].obs;
  final RxList<String> globalProjects = <String>[].obs;
  final RxMap<String, String> projectsMap = <String, String>{}.obs;
  RxList<EmployeeModel> allEmployees = <EmployeeModel>[].obs;

  // Persistent Filter States
  final RxString selectedProject = ''.obs;
  final RxString selectedStatus = ''.obs;
  final Rx<DateTime?> startDate = Rx<DateTime?>(null);
  final Rx<DateTime?> endDate = Rx<DateTime?>(null);
  final RxList<EmployeeModel> selectedPaidByEmployees = <EmployeeModel>[].obs;
  final RxList<EmployeeModel> selectedCreatedByEmployees =
      <EmployeeModel>[].obs;
  final RxString selectedDateType = 'Transaction Date'.obs;

  final employeeSearchController = TextEditingController();
  final isSearchingEmployees = false.obs;
  final employeeSearchResults = <EmployeeModel>[].obs;

  final List<String> dateTypes = [
    'Transaction Date',
    'Created At',
  ];

  int _pageSize = 20;
  int _pageNumber = 1;

  @override
  void onInit() {
    super.onInit();
    loadInitialMasterData();
    fetchAllEmployees();
    employeeSearchController.addListener(() {
      searchEmployees(employeeSearchController.text);
    });
  }

  bool get isFilterApplied {
    return selectedProject.value.isNotEmpty ||
        selectedStatus.value.isNotEmpty ||
        startDate.value != null ||
        endDate.value != null ||
        selectedPaidByEmployees.isNotEmpty ||
        selectedCreatedByEmployees.isNotEmpty;
  }

  /// Load master data
  Future<void> loadInitialMasterData() async {
    await fetchGlobalProjects();
    await fetchMasterData();
  }

  Future<void> deleteExpense(String expenseId) async {
    try {
      logSafe("Attempting to delete expense: $expenseId");
      final success = await ApiService.deleteExpense(expenseId);
      if (success) {
        expenses.removeWhere((e) => e.id == expenseId);
        logSafe("Expense deleted successfully.");
        showAppSnackbar(
          title: "Deleted",
          message: "Expense has been deleted successfully.",
          type: SnackbarType.success,
        );
      } else {
        logSafe("Failed to delete expense: $expenseId", level: LogLevel.error);
        showAppSnackbar(
          title: "Failed",
          message: "Failed to delete expense.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Exception in deleteExpense: $e", level: LogLevel.error);
      logSafe("StackTrace: $stack", level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Something went wrong while deleting.",
        type: SnackbarType.error,
      );
    }
  }

  Future<void> searchEmployees(String searchQuery) async {
    if (searchQuery.trim().isEmpty) {
      employeeSearchResults.clear();
      return;
    }

    isSearchingEmployees.value = true;
    try {
      final results = await ApiService.searchEmployeesBasic(
        searchString: searchQuery.trim(),
      );

      if (results != null) {
        employeeSearchResults.assignAll(
          results.map((e) => EmployeeModel.fromJson(e)),
        );
      } else {
        employeeSearchResults.clear();
      }
    } catch (e) {
      logSafe("Error searching employees: $e", level: LogLevel.error);
      employeeSearchResults.clear();
    } finally {
      isSearchingEmployees.value = false;
    }
  }

  /// Fetch expenses using filters
  Future<void> fetchExpenses({
    List<String>? projectIds,
    List<String>? statusIds,
    List<String>? createdByIds,
    List<String>? paidByIds,
    DateTime? startDate,
    DateTime? endDate,
    int pageSize = 20,
    int pageNumber = 1,
  }) async {
    isLoading.value = true;
    errorMessage.value = '';
    expenses.clear();
    _pageSize = pageSize;
    _pageNumber = pageNumber;

    final Map<String, dynamic> filterMap = {
      "projectIds": projectIds ??
          (selectedProject.value.isEmpty
              ? []
              : [projectsMap[selectedProject.value] ?? '']),
      "statusIds": statusIds ??
          (selectedStatus.value.isEmpty ? [] : [selectedStatus.value]),
      "createdByIds":
          createdByIds ?? selectedCreatedByEmployees.map((e) => e.id).toList(),
      "paidByIds":
          paidByIds ?? selectedPaidByEmployees.map((e) => e.id).toList(),
      "startDate": (startDate ?? this.startDate.value)?.toIso8601String(),
      "endDate": (endDate ?? this.endDate.value)?.toIso8601String(),
      "isTransactionDate": selectedDateType.value == 'Transaction Date',
    };

    try {
      logSafe("Fetching expenses with filter: ${jsonEncode(filterMap)}");

      final result = await ApiService.getExpenseListApi(
        filter: jsonEncode(filterMap),
        pageSize: _pageSize,
        pageNumber: _pageNumber,
      );

      if (result != null) {
        try {
          final expenseResponse = ExpenseResponse.fromJson(result);

          // If the backend returns no data, treat it as empty list
          if (expenseResponse.data.data.isEmpty) {
            expenses.clear();
            errorMessage.value = ''; // no error
            logSafe("Expense list is empty.");
          } else {
            expenses.assignAll(expenseResponse.data.data);
            logSafe("Expenses loaded: ${expenses.length}");
            logSafe(
                "Pagination Info: Page ${expenseResponse.data.currentPage} of ${expenseResponse.data.totalPages} | Total: ${expenseResponse.data.totalEntites}");
          }
        } catch (e) {
          errorMessage.value = 'Failed to parse expenses: $e';
          logSafe("Parse error in fetchExpenses: $e", level: LogLevel.error);
        }
      } else {
        // Only treat as error if this means a network or server failure
        errorMessage.value = 'Unable to connect to the server.';
        logSafe("fetchExpenses failed: null response", level: LogLevel.error);
      }
    } catch (e, stack) {
      errorMessage.value = 'An unexpected error occurred.';
      logSafe("Exception in fetchExpenses: $e", level: LogLevel.error);
      logSafe("StackTrace: $stack", level: LogLevel.debug);
    } finally {
      isLoading.value = false;
    }
  }

  /// Clear all filters
  void clearFilters() {
    selectedProject.value = '';
    selectedStatus.value = '';
    startDate.value = null;
    endDate.value = null;
    selectedPaidByEmployees.clear();
    selectedCreatedByEmployees.clear();
  }

  /// Fetch master data: Expense Categorys, payment modes, and expense status
  Future<void> fetchMasterData() async {
    try {
      final expenseTypesData = await ApiService.getMasterExpenseTypes();
      if (expenseTypesData is List) {
        expenseTypes.value =
            expenseTypesData.map((e) => ExpenseTypeModel.fromJson(e)).toList();
      }

      final paymentModesData = await ApiService.getMasterPaymentModes();
      if (paymentModesData is List) {
        paymentModes.value =
            paymentModesData.map((e) => PaymentModeModel.fromJson(e)).toList();
      }

      final expenseStatusData = await ApiService.getMasterExpenseStatus();
      if (expenseStatusData is List) {
        expenseStatuses.value = expenseStatusData
            .map((e) => ExpenseStatusModel.fromJson(e))
            .toList();
      }
    } catch (e) {
      showAppSnackbar(
        title: "Error",
        message: "Failed to fetch master data: $e",
        type: SnackbarType.error,
      );
    }
  }

  /// Fetch global projects
  Future<void> fetchGlobalProjects() async {
    try {
      final response = await ApiService.getGlobalProjects();
      if (response != null) {
        final names = <String>[];
        for (var item in response) {
          final name = item['name']?.toString().trim();
          final id = item['id']?.toString().trim();
          if (name != null && id != null && name.isNotEmpty) {
            projectsMap[name] = id;
            names.add(name);
          }
        }
        globalProjects.assignAll(names);
        logSafe("Fetched ${names.length} global projects");
      }
    } catch (e) {
      logSafe("Failed to fetch global projects: $e", level: LogLevel.error);
    }
  }

  /// Fetch all employees
  Future<void> fetchAllEmployees() async {
    isLoading.value = true;
    try {
      final response = await ApiService.getAllEmployees();
      if (response != null && response.isNotEmpty) {
        allEmployees
            .assignAll(response.map((json) => EmployeeModel.fromJson(json)));
        logSafe(
          "All Employees fetched for Manage Bucket: ${allEmployees.length}",
          level: LogLevel.info,
        );
      } else {
        allEmployees.clear();
        logSafe("No employees found for Manage Bucket.",
            level: LogLevel.warning);
      }
    } catch (e) {
      allEmployees.clear();
      logSafe("Error fetching employees in Manage Bucket",
          level: LogLevel.error, error: e);
    }
    isLoading.value = false;
    update();
  }

  Future<void> loadMoreExpenses() async {
    if (isLoading.value) return;

    _pageNumber += 1;
    isLoading.value = true;

    final Map<String, dynamic> filterMap = {
      "projectIds": selectedProject.value.isEmpty
          ? []
          : [projectsMap[selectedProject.value] ?? ''],
      "statusIds": selectedStatus.value.isEmpty ? [] : [selectedStatus.value],
      "createdByIds": selectedCreatedByEmployees.map((e) => e.id).toList(),
      "paidByIds": selectedPaidByEmployees.map((e) => e.id).toList(),
      "startDate": startDate.value?.toIso8601String(),
      "endDate": endDate.value?.toIso8601String(),
      "isTransactionDate": selectedDateType.value == 'Transaction Date',
    };

    try {
      final result = await ApiService.getExpenseListApi(
        filter: jsonEncode(filterMap),
        pageSize: _pageSize,
        pageNumber: _pageNumber,
      );

      if (result != null) {
        final expenseResponse = ExpenseResponse.fromJson(result);
        expenses.addAll(expenseResponse.data.data);
      }
    } catch (e) {
      logSafe("Error in loadMoreExpenses: $e", level: LogLevel.error);
    } finally {
      isLoading.value = false;
    }
  }

  /// Update expense status
  Future<bool> updateExpenseStatus(String expenseId, String statusId) async {
    isLoading.value = true;
    errorMessage.value = '';
    try {
      logSafe("Updating status for expense: $expenseId -> $statusId");
      final success = await ApiService.updateExpenseStatusApi(
        expenseId: expenseId,
        statusId: statusId,
      );
      if (success) {
        logSafe("Expense status updated successfully.");
        await fetchExpenses();
        return true;
      } else {
        errorMessage.value = "Failed to update expense status.";
        return false;
      }
    } catch (e, stack) {
      errorMessage.value = 'An unexpected error occurred.';
      logSafe("Exception in updateExpenseStatus: $e", level: LogLevel.error);
      logSafe("StackTrace: $stack", level: LogLevel.debug);
      return false;
    } finally {
      isLoading.value = false;
    }
  }
}
