import 'package:get/get.dart';
import 'package:on_field_work/helpers/services/app_logger.dart';
import 'package:on_field_work/helpers/services/api_service.dart';
import 'package:on_field_work/controller/project_controller.dart';
import 'package:on_field_work/model/dashboard/project_progress_model.dart';
import 'package:on_field_work/model/dashboard/pending_expenses_model.dart';
import 'package:on_field_work/model/dashboard/expense_type_report_model.dart';
import 'package:on_field_work/model/dashboard/monthly_expence_model.dart';
import 'package:on_field_work/model/expense/expense_type_model.dart';
import 'package:on_field_work/model/employees/employee_model.dart';
import 'package:on_field_work/model/dashboard/collection_overview_model.dart';
import 'package:on_field_work/model/dashboard/purchase_invoice_model.dart';

class DashboardController extends GetxController {
  // Dependencies
  final ProjectController projectController = Get.put(ProjectController());

  // =========================
  // 1. STATE VARIABLES
  // =========================

  // Attendance
  final roleWiseData = <Map<String, dynamic>>[].obs;
  final attendanceSelectedRange = '15D'.obs;
  final attendanceIsChartView = true.obs;
  final isAttendanceLoading = false.obs;

  // Project Progress
  final projectChartData = <ChartTaskData>[].obs;
  final projectSelectedRange = '15D'.obs;
  final projectIsChartView = true.obs;
  final isProjectLoading = false.obs;

  // Overview Counts
  final totalProjects = 0.obs;
  final ongoingProjects = 0.obs;
  final isProjectsLoading = false.obs;

  final totalTasks = 0.obs;
  final completedTasks = 0.obs;
  final isTasksLoading = false.obs;

  final totalEmployees = 0.obs;
  final inToday = 0.obs;
  final isTeamsLoading = false.obs;

  // Expenses & Reports
  final isPendingExpensesLoading = false.obs;
  final pendingExpensesData = Rx<PendingExpensesData?>(null);

  final isExpenseTypeReportLoading = false.obs;
  final expenseTypeReportData = Rx<ExpenseTypeReportData?>(null);
  final expenseReportStartDate =
      DateTime.now().subtract(const Duration(days: 15)).obs;
  final expenseReportEndDate = DateTime.now().obs;

  final isMonthlyExpenseLoading = false.obs;
  final monthlyExpenseList = <MonthlyExpenseData>[].obs;
  final selectedMonthlyExpenseDuration =
      MonthlyExpenseDuration.twelveMonths.obs;
  final selectedMonthsCount = 12.obs;

  final expenseTypes = <ExpenseTypeModel>[].obs;
  final selectedExpenseType = Rx<ExpenseTypeModel?>(null);

  // Teams/Employees
  final isLoadingEmployees = true.obs;
  final employees = <EmployeeModel>[].obs;
  final uploadingStates = <String, RxBool>{}.obs;

  // Collection
  final isCollectionOverviewLoading = true.obs;
  final collectionOverviewData = Rx<CollectionOverviewData?>(null);
// =========================
// Purchase Invoice Overview
// =========================
  final isPurchaseInvoiceLoading = true.obs;
  final purchaseInvoiceOverviewData = Rx<PurchaseInvoiceOverviewData?>(null);
  // Constants
  final List<String> ranges = ['7D', '15D', '30D'];
  static const _rangeDaysMap = {
    '7D': 7,
    '15D': 15,
    '30D': 30,
    '3M': 90,
    '6M': 180
  };

  // =========================
  // 2. COMPUTED PROPERTIES
  // =========================

  int getAttendanceDays() => _rangeDaysMap[attendanceSelectedRange.value] ?? 7;
  int getProjectDays() => _rangeDaysMap[projectSelectedRange.value] ?? 7;

  // DSO Calculation Constants
  static const double _w0_30 = 15.0;
  static const double _w30_60 = 45.0;
  static const double _w60_90 = 75.0;
  static const double _w90_plus = 105.0;

  double get calculatedDSO {
    final data = collectionOverviewData.value;
    if (data == null || data.totalDueAmount == 0) return 0.0;

    final double weightedDue = (data.bucket0To30Amount * _w0_30) +
        (data.bucket30To60Amount * _w30_60) +
        (data.bucket60To90Amount * _w60_90) +
        (data.bucket90PlusAmount * _w90_plus);

    return weightedDue / data.totalDueAmount;
  }

  // =========================
  // 3. LIFECYCLE
  // =========================

  @override
  void onInit() {
    super.onInit();
    logSafe('DashboardController initialized', level: LogLevel.info);

    // Project Selection Listener
    ever<String>(projectController.selectedProjectId, (id) {
      if (id.isNotEmpty) {
        fetchAllDashboardData();
        fetchTodaysAttendance(id);
      }
    });

    // Expense Report Date Listener
    everAll([expenseReportStartDate, expenseReportEndDate], (_) {
      if (projectController.selectedProjectId.value.isNotEmpty) {
        fetchExpenseTypeReport(
          startDate: expenseReportStartDate.value,
          endDate: expenseReportEndDate.value,
        );
      }
    });

    // Chart Range Listeners
    ever(attendanceSelectedRange, (_) => fetchRoleWiseAttendance());
    ever(projectSelectedRange, (_) => fetchProjectProgress());
  }

  // =========================
  // 4. USER ACTIONS
  // =========================

  void updateAttendanceRange(String range) =>
      attendanceSelectedRange.value = range;
  void updateProjectRange(String range) => projectSelectedRange.value = range;
  void toggleAttendanceChartView(bool isChart) =>
      attendanceIsChartView.value = isChart;
  void toggleProjectChartView(bool isChart) =>
      projectIsChartView.value = isChart;

  void updateSelectedExpenseType(ExpenseTypeModel? type) {
    selectedExpenseType.value = type;
    fetchMonthlyExpenses(categoryId: type?.id);
  }

  void updateMonthlyExpenseDuration(MonthlyExpenseDuration duration) {
    selectedMonthlyExpenseDuration.value = duration;

    // Efficient Map lookup instead of Switch
    const durationMap = {
      MonthlyExpenseDuration.oneMonth: 1,
      MonthlyExpenseDuration.threeMonths: 3,
      MonthlyExpenseDuration.sixMonths: 6,
      MonthlyExpenseDuration.twelveMonths: 12,
      MonthlyExpenseDuration.all: 0,
    };

    selectedMonthsCount.value = durationMap[duration] ?? 12;
    fetchMonthlyExpenses();
  }

  Future<void> refreshDashboard() => fetchAllDashboardData();
  Future<void> refreshAttendance() => fetchRoleWiseAttendance();
  Future<void> refreshProjects() => fetchProjectProgress();
  Future<void> refreshTasks() async {
    final id = projectController.selectedProjectId.value;
    if (id.isNotEmpty) await fetchDashboardTasks(projectId: id);
  }

  // =========================
  // 5. DATA FETCHING (API)
  // =========================

  /// Wrapper to reduce try-finally boilerplate for loading states
  Future<void> _executeApiCall(
      RxBool loader, Future<void> Function() apiLogic) async {
    loader.value = true;
    try {
      await apiLogic();
    } finally {
      loader.value = false;
    }
  }

  Future<void> fetchAllDashboardData() async {
    final String projectId = projectController.selectedProjectId.value;
    if (projectId.isEmpty) return;

    await Future.wait([
      fetchRoleWiseAttendance(),
      fetchProjectProgress(),
      fetchDashboardTasks(projectId: projectId),
      fetchDashboardTeams(projectId: projectId),
      fetchPendingExpenses(),
      fetchExpenseTypeReport(
        startDate: expenseReportStartDate.value,
        endDate: expenseReportEndDate.value,
      ),
      fetchMonthlyExpenses(),
      fetchMasterData(),
      fetchCollectionOverview(),
      fetchPurchaseInvoiceOverview(),
    ]);
  }

  Future<void> fetchCollectionOverview() async {
    final projectId = projectController.selectedProjectId.value;
    if (projectId.isEmpty) return;

    await _executeApiCall(isCollectionOverviewLoading, () async {
      final response =
          await ApiService.getCollectionOverview(projectId: projectId);
      collectionOverviewData.value =
          (response?.success == true) ? response!.data : null;
    });
  }

  Future<void> fetchTodaysAttendance(String projectId) async {
    await _executeApiCall(isLoadingEmployees, () async {
      final response = await ApiService.getAttendanceForDashboard(projectId);
      if (response != null) {
        employees.value = response;
        for (var emp in employees) {
          uploadingStates.putIfAbsent(emp.id, () => false.obs);
        }
      }
    });
  }

  Future<void> fetchMasterData() async {
    try {
      final data = await ApiService.getMasterExpenseTypes();
      if (data is List) {
        expenseTypes.value =
            data.map((e) => ExpenseTypeModel.fromJson(e)).toList();
      }
    } catch (_) {}
  }

  Future<void> fetchMonthlyExpenses({String? categoryId}) async {
    await _executeApiCall(isMonthlyExpenseLoading, () async {
      final response = await ApiService.getDashboardMonthlyExpensesApi(
        categoryId: categoryId,
        months: selectedMonthsCount.value,
      );
      monthlyExpenseList.value =
          (response?.success == true) ? response!.data : [];
    });
  }

  Future<void> fetchPurchaseInvoiceOverview() async {
    final projectId = projectController.selectedProjectId.value;
    if (projectId.isEmpty) return;

    await _executeApiCall(isPurchaseInvoiceLoading, () async {
      final response = await ApiService.getPurchaseInvoiceOverview(
        projectId: projectId,
      );
      purchaseInvoiceOverviewData.value =
          (response?.success == true) ? response!.data : null;
    });
  }

  Future<void> fetchPendingExpenses() async {
    final id = projectController.selectedProjectId.value;
    if (id.isEmpty) return;

    await _executeApiCall(isPendingExpensesLoading, () async {
      final response = await ApiService.getPendingExpensesApi(projectId: id);
      pendingExpensesData.value =
          (response?.success == true) ? response!.data : null;
    });
  }

  Future<void> fetchRoleWiseAttendance() async {
    final id = projectController.selectedProjectId.value;
    if (id.isEmpty) return;

    await _executeApiCall(isAttendanceLoading, () async {
      final response = await ApiService.getDashboardAttendanceOverview(
          id, getAttendanceDays());
      roleWiseData.value =
          response?.map((e) => Map<String, dynamic>.from(e)).toList() ?? [];
    });
  }

  Future<void> fetchExpenseTypeReport(
      {required DateTime startDate, required DateTime endDate}) async {
    final id = projectController.selectedProjectId.value;
    if (id.isEmpty) return;

    await _executeApiCall(isExpenseTypeReportLoading, () async {
      final response = await ApiService.getExpenseTypeReportApi(
        projectId: id,
        startDate: startDate,
        endDate: endDate,
      );
      expenseTypeReportData.value =
          (response?.success == true) ? response!.data : null;
    });
  }

  Future<void> fetchProjectProgress() async {
    final id = projectController.selectedProjectId.value;
    if (id.isEmpty) return;

    await _executeApiCall(isProjectLoading, () async {
      final response = await ApiService.getProjectProgress(
          projectId: id, days: getProjectDays());
      if (response?.success == true) {
        projectChartData.value = response!.data
            .map((d) => ChartTaskData.fromProjectData(d))
            .toList();
      } else {
        projectChartData.clear();
      }
    });
  }

  Future<void> fetchDashboardTasks({required String projectId}) async {
    await _executeApiCall(isTasksLoading, () async {
      final response = await ApiService.getDashboardTasks(projectId: projectId);
      if (response?.success == true) {
        totalTasks.value = response!.data?.totalTasks ?? 0;
        completedTasks.value = response.data?.completedTasks ?? 0;
      } else {
        totalTasks.value = 0;
        completedTasks.value = 0;
      }
    });
  }

  Future<void> fetchDashboardTeams({required String projectId}) async {
    await _executeApiCall(isTeamsLoading, () async {
      final response = await ApiService.getDashboardTeams(projectId: projectId);
      if (response?.success == true) {
        totalEmployees.value = response!.data?.totalEmployees ?? 0;
        inToday.value = response.data?.inToday ?? 0;
      } else {
        totalEmployees.value = 0;
        inToday.value = 0;
      }
    });
  }
}

enum MonthlyExpenseDuration {
  oneMonth,
  threeMonths,
  sixMonths,
  twelveMonths,
  all,
}
