import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:geolocator/geolocator.dart';
import 'package:intl/intl.dart';

import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/helpers/widgets/my_image_compressor.dart';

import 'package:marco/model/attendance/attendance_model.dart';
import 'package:marco/model/project_model.dart';
import 'package:marco/model/employees/employee_model.dart';
import 'package:marco/model/attendance/attendance_log_model.dart';
import 'package:marco/model/regularization_log_model.dart';
import 'package:marco/model/attendance/attendance_log_view_model.dart';

import 'package:marco/controller/project_controller.dart';

class AttendanceController extends GetxController {
  // Data models
  List<AttendanceModel> attendances = [];
  List<ProjectModel> projects = [];
  List<EmployeeModel> employees = [];
  List<AttendanceLogModel> attendanceLogs = [];
  List<RegularizationLogModel> regularizationLogs = [];
  List<AttendanceLogViewModel> attendenceLogsView = [];

  // States
  String selectedTab = 'Employee List';
  DateTime? startDateAttendance;
  DateTime? endDateAttendance;

  final isLoading = true.obs;
  final isLoadingProjects = true.obs;
  final isLoadingEmployees = true.obs;
  final isLoadingAttendanceLogs = true.obs;
  final isLoadingRegularizationLogs = true.obs;
  final isLoadingLogView = true.obs;
  final uploadingStates = <String, RxBool>{}.obs;

  @override
  void onInit() {
    super.onInit();
    _initializeDefaults();
  }

  void _initializeDefaults() {
    _setDefaultDateRange();
    fetchProjects();
  }

  void _setDefaultDateRange() {
    final today = DateTime.now();
    startDateAttendance = today.subtract(const Duration(days: 7));
    endDateAttendance = today.subtract(const Duration(days: 1));
    logSafe(
        "Default date range set: $startDateAttendance to $endDateAttendance");
  }

  // ------------------ Project & Employee ------------------
  /// Called when a notification says attendance has been updated
  Future<void> refreshDataFromNotification({String? projectId}) async {
    projectId ??= Get.find<ProjectController>().selectedProject?.id;
    if (projectId == null) {
      logSafe("No project selected for attendance refresh from notification",
          level: LogLevel.warning);
      return;
    }
    await fetchProjectData(projectId);
    logSafe(
        "Attendance data refreshed from notification for project $projectId");
  }

  Future<void> fetchProjects() async {
    isLoadingProjects.value = true;

    final response = await ApiService.getProjects();
    if (response != null && response.isNotEmpty) {
      projects = response.map((e) => ProjectModel.fromJson(e)).toList();
      logSafe("Projects fetched: ${projects.length}");
    } else {
      projects = [];
      logSafe("Failed to fetch projects or no projects available.",
          level: LogLevel.error);
    }

    isLoadingProjects.value = false;
    update(['attendance_dashboard_controller']);
  }

  Future<void> fetchEmployeesByProject(String? projectId) async {
    if (projectId == null) return;

    isLoadingEmployees.value = true;

    final response = await ApiService.getEmployeesByProject(projectId);
    if (response != null) {
      employees = response.map((e) => EmployeeModel.fromJson(e)).toList();
      for (var emp in employees) {
        uploadingStates[emp.id] = false.obs;
      }
      logSafe("Employees fetched: ${employees.length} for project $projectId");
    } else {
      logSafe("Failed to fetch employees for project $projectId",
          level: LogLevel.error);
    }
    isLoadingEmployees.value = false;
    update();
  }

  // ------------------ Attendance Capture ------------------

  Future<bool> captureAndUploadAttendance(
    String id,
    String employeeId,
    String projectId, {
    String comment = "Marked via mobile app",
    required int action,
    bool imageCapture = true,
    String? markTime, // still optional in controller
    String? date, // new optional param
  }) async {
    try {
      uploadingStates[employeeId]?.value = true;

      XFile? image;
      if (imageCapture) {
        image = await ImagePicker()
            .pickImage(source: ImageSource.camera, imageQuality: 80);
        if (image == null) {
          logSafe("Image capture cancelled.", level: LogLevel.warning);
          return false;
        }

        final compressedBytes =
            await compressImageToUnder100KB(File(image.path));
        if (compressedBytes == null) {
          logSafe("Image compression failed.", level: LogLevel.error);
          return false;
        }

        final compressedFile = await saveCompressedImageToFile(compressedBytes);
        image = XFile(compressedFile.path);
      }

      if (!await _handleLocationPermission()) return false;
      final position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);

      final imageName = imageCapture
          ? ApiService.generateImageName(employeeId, employees.length + 1)
          : "";

      // ---------------- DATE / TIME LOGIC ----------------
      final now = DateTime.now();

      // Default effectiveDate = now
      DateTime effectiveDate = now;

      if (action == 1) {
        // Checkout
        // Try to find today's open log for this employee
        final log = attendanceLogs.firstWhereOrNull(
          (log) => log.employeeId == employeeId && log.checkOut == null,
        );
        if (log?.checkIn != null) {
          effectiveDate = log!.checkIn!; // use check-in date
        }
      }

      final formattedMarkTime = markTime ?? DateFormat('hh:mm a').format(now);

      final formattedDate =
          date ?? DateFormat('yyyy-MM-dd').format(effectiveDate);

      // ---------------- API CALL ----------------
      final result = await ApiService.uploadAttendanceImage(
        id,
        employeeId,
        image,
        position.latitude,
        position.longitude,
        imageName: imageName,
        projectId: projectId,
        comment: comment,
        action: action,
        imageCapture: imageCapture,
        markTime: formattedMarkTime,
        date: formattedDate,
      );

      logSafe(
          "Attendance uploaded for $employeeId, action: $action, date: $formattedDate");
      return result;
    } catch (e, stacktrace) {
      logSafe("Error uploading attendance",
          level: LogLevel.error, error: e, stackTrace: stacktrace);
      return false;
    } finally {
      uploadingStates[employeeId]?.value = false;
    }
  }

  Future<bool> _handleLocationPermission() async {
    LocationPermission permission = await Geolocator.checkPermission();

    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        logSafe('Location permissions are denied', level: LogLevel.warning);
        return false;
      }
    }

    if (permission == LocationPermission.deniedForever) {
      logSafe('Location permissions are permanently denied',
          level: LogLevel.error);
      return false;
    }

    return true;
  }

  // ------------------ Attendance Logs ------------------

  Future<void> fetchAttendanceLogs(String? projectId,
      {DateTime? dateFrom, DateTime? dateTo}) async {
    if (projectId == null) return;

    isLoadingAttendanceLogs.value = true;

    final response = await ApiService.getAttendanceLogs(projectId,
        dateFrom: dateFrom, dateTo: dateTo);
    if (response != null) {
      attendanceLogs =
          response.map((e) => AttendanceLogModel.fromJson(e)).toList();
      logSafe("Attendance logs fetched: ${attendanceLogs.length}");
    } else {
      logSafe("Failed to fetch attendance logs for project $projectId",
          level: LogLevel.error);
    }

    isLoadingAttendanceLogs.value = false;
    update();
  }

  Map<String, List<AttendanceLogModel>> groupLogsByCheckInDate() {
    final groupedLogs = <String, List<AttendanceLogModel>>{};

    for (var logItem in attendanceLogs) {
      final checkInDate = logItem.checkIn != null
          ? DateFormat('dd MMM yyyy').format(logItem.checkIn!)
          : 'Unknown';
      groupedLogs.putIfAbsent(checkInDate, () => []).add(logItem);
    }

    final sortedEntries = groupedLogs.entries.toList()
      ..sort((a, b) {
        if (a.key == 'Unknown') return 1;
        if (b.key == 'Unknown') return -1;
        final dateA = DateFormat('dd MMM yyyy').parse(a.key);
        final dateB = DateFormat('dd MMM yyyy').parse(b.key);
        return dateB.compareTo(dateA);
      });

    return Map<String, List<AttendanceLogModel>>.fromEntries(sortedEntries);
  }

  // ------------------ Regularization Logs ------------------

  Future<void> fetchRegularizationLogs(String? projectId) async {
    if (projectId == null) return;

    isLoadingRegularizationLogs.value = true;

    final response = await ApiService.getRegularizationLogs(projectId);
    if (response != null) {
      regularizationLogs =
          response.map((e) => RegularizationLogModel.fromJson(e)).toList();
      logSafe("Regularization logs fetched: ${regularizationLogs.length}");
    } else {
      logSafe("Failed to fetch regularization logs for project $projectId",
          level: LogLevel.error);
    }

    isLoadingRegularizationLogs.value = false;
    update();
  }

  // ------------------ Attendance Log View ------------------

  Future<void> fetchLogsView(String? id) async {
    if (id == null) return;

    isLoadingLogView.value = true;

    final response = await ApiService.getAttendanceLogView(id);
    if (response != null) {
      attendenceLogsView =
          response.map((e) => AttendanceLogViewModel.fromJson(e)).toList();
      attendenceLogsView.sort((a, b) => (b.activityTime ?? DateTime(2000))
          .compareTo(a.activityTime ?? DateTime(2000)));
      logSafe("Attendance log view fetched for ID: $id");
    } else {
      logSafe("Failed to fetch attendance log view for ID $id",
          level: LogLevel.error);
    }

    isLoadingLogView.value = false;
    update();
  }

  // ------------------ Combined Load ------------------

  Future<void> loadAttendanceData(String projectId) async {
    isLoading.value = true;
    await fetchProjectData(projectId);
    isLoading.value = false;
  }

  Future<void> fetchProjectData(String? projectId) async {
    if (projectId == null) return;

    await Future.wait([
      fetchEmployeesByProject(projectId),
      fetchAttendanceLogs(projectId,
          dateFrom: startDateAttendance, dateTo: endDateAttendance),
      fetchRegularizationLogs(projectId),
    ]);

    logSafe("Project data fetched for project ID: $projectId");
  }

  // ------------------ UI Interaction ------------------

  Future<void> selectDateRangeForAttendance(
      BuildContext context, AttendanceController controller) async {
    final today = DateTime.now();

    final picked = await showDateRangePicker(
      context: context,
      firstDate: DateTime(2022),
      lastDate: today.subtract(const Duration(days: 1)),
      initialDateRange: DateTimeRange(
        start: startDateAttendance ?? today.subtract(const Duration(days: 7)),
        end: endDateAttendance ?? today.subtract(const Duration(days: 1)),
      ),
    );

    if (picked != null) {
      startDateAttendance = picked.start;
      endDateAttendance = picked.end;
      logSafe(
          "Date range selected: $startDateAttendance to $endDateAttendance");

      await controller.fetchAttendanceLogs(
        Get.find<ProjectController>().selectedProject?.id,
        dateFrom: picked.start,
        dateTo: picked.end,
      );
    }
  }
}
