import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/widgets/my_spacing.dart';
import 'package:on_field_work/helpers/widgets/my_text.dart';
import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart';
import 'package:on_field_work/controller/service_project/service_project_allocation_controller.dart';
import 'package:on_field_work/model/employees/employee_model.dart';
import 'package:on_field_work/model/employees/multiple_select_bottomsheet.dart';
import 'package:on_field_work/model/service_project/job_allocation_model.dart';
import 'package:on_field_work/helpers/services/api_service.dart';
import 'package:on_field_work/helpers/utils/mixins/ui_mixin.dart';
import 'package:on_field_work/helpers/widgets/my_snackbar.dart';

class RoleEmployeeAllocation {
  final TeamRole role;
  final List<EmployeeModel> employees;

  RoleEmployeeAllocation({required this.role, required this.employees});
}

class SimpleProjectAllocationBottomSheet extends StatefulWidget {
  final String projectId;
  final List<RoleEmployeeAllocation>? existingAllocations;

  const SimpleProjectAllocationBottomSheet({
    super.key,
    required this.projectId,
    this.existingAllocations,
  });

  @override
  State<SimpleProjectAllocationBottomSheet> createState() =>
      _SimpleProjectAllocationBottomSheetState();
}

class _SimpleProjectAllocationBottomSheetState
    extends State<SimpleProjectAllocationBottomSheet> with UIMixin {
  late ServiceProjectAllocationController controller;

  final RxList<EmployeeModel> _selectedEmployees = <EmployeeModel>[].obs;
  final RxList<RoleEmployeeAllocation> _addedAllocations =
      <RoleEmployeeAllocation>[].obs;

  final TextEditingController _employeeTextCtrl = TextEditingController();
  final _roleDropdownKey = GlobalKey();
  final RxBool _hasChanges = false.obs;

  bool _checkIfChanged() {
    final existingMap = {
      for (var alloc in widget.existingAllocations ?? [])
        alloc.role.id: alloc.employees.map((e) => e.id).toSet()
    };

    final currentMap = {
      for (var alloc in _addedAllocations)
        alloc.role.id: alloc.employees.map((e) => e.id).toSet()
    };

    // Compare existing and current allocations
    if (existingMap.length != currentMap.length) return true;

    for (var roleId in existingMap.keys) {
      if (!currentMap.containsKey(roleId)) return true;
      if (existingMap[roleId]!.difference(currentMap[roleId]!).isNotEmpty ||
          currentMap[roleId]!.difference(existingMap[roleId]!).isNotEmpty) {
        return true;
      }
    }

    return false;
  }

  @override
  void initState() {
    super.initState();
    controller = Get.put(ServiceProjectAllocationController());
    controller.projectId.value = widget.projectId;
    controller.fetchRoles();

    // Prepopulate existing allocations
    if (widget.existingAllocations != null) {
      _addedAllocations.assignAll(widget.existingAllocations!);
    }
  }

  @override
  void dispose() {
    _employeeTextCtrl.dispose();
    Get.delete<ServiceProjectAllocationController>();
    super.dispose();
  }

  InputDecoration _inputDecoration(String hint) => InputDecoration(
        hintText: hint,
        filled: true,
        fillColor: Colors.grey.shade50,
        border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide(color: Colors.grey.shade400)),
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
      );

  Widget _roleDropdown() {
    return GestureDetector(
      key: _roleDropdownKey,
      onTap: _showRoleMenu,
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
        decoration: BoxDecoration(
          color: Colors.grey.shade50,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: Colors.grey.shade400),
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.08),
              offset: const Offset(0, 1),
              blurRadius: 2,
            ),
          ],
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Obx(() => MyText.bodyMedium(
                controller.selectedRole.value?.name ?? "Select Role")),
            const Icon(Icons.arrow_drop_down_rounded, size: 30),
          ],
        ),
      ),
    );
  }

  Future<void> _showRoleMenu() async {
    if (_roleDropdownKey.currentContext == null) return;

    final RenderBox button =
        _roleDropdownKey.currentContext!.findRenderObject() as RenderBox;
    final RenderBox overlay =
        Overlay.of(context).context.findRenderObject() as RenderBox;
    final position = button.localToGlobal(Offset.zero, ancestor: overlay);

    final selectedRoleId = await showMenu<String>(
      context: context,
      position: RelativeRect.fromLTRB(
        position.dx,
        position.dy + button.size.height,
        overlay.size.width - position.dx - button.size.width,
        0,
      ),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      items: controller.roles
          .map(
            (role) => PopupMenuItem<String>(
              value: role.id,
              child: MyText.bodyMedium(role.name),
            ),
          )
          .toList(),
    );

    if (selectedRoleId != null) {
      final role = controller.roles.firstWhere((r) => r.id == selectedRoleId);
      controller.selectedRole.value = role;
      controller.fetchEmployeesByRole(role.id);

      _selectedEmployees.clear();
      _employeeTextCtrl.clear();
    }
  }

  Widget _employeeSelector() {
    return GestureDetector(
      onTap: () async {
        final selected = await showModalBottomSheet<List<EmployeeModel>>(
          context: context,
          isScrollControlled: true,
          builder: (_) => EmployeeSelectionBottomSheet(
            multipleSelection: true,
            initiallySelected: _selectedEmployees,
            title: "Select Employees",
          ),
        );
        if (selected != null) {
          _selectedEmployees.assignAll(selected);
          _employeeTextCtrl.text = _selectedEmployees
              .map((e) => "${e.firstName} ${e.lastName}")
              .join(", ");
        }
      },
      child: AbsorbPointer(
        child: TextFormField(
          controller: _employeeTextCtrl,
          decoration: _inputDecoration("Select Employees").copyWith(
            suffixIcon: const Icon(Icons.search),
          ),
          validator: (_) =>
              _selectedEmployees.isEmpty ? "Please select employees" : null,
        ),
      ),
    );
  }

  void _handleAdd() {
    final selectedRole = controller.selectedRole.value;
    if (selectedRole == null || _selectedEmployees.isEmpty) {
      showAppSnackbar(
        title: "Error",
        message: "Please select role and employees",
        type: SnackbarType.error,
      );
      return;
    }

    final updatedAllocations =
        List<RoleEmployeeAllocation>.from(_addedAllocations);

    final existingIndex =
        updatedAllocations.indexWhere((a) => a.role.id == selectedRole.id);

    if (existingIndex >= 0) {
      final existingAlloc = updatedAllocations[existingIndex];
      final mergedEmployees = [
        ...existingAlloc.employees,
        ..._selectedEmployees.where(
          (emp) => !existingAlloc.employees.any((e) => e.id == emp.id),
        ),
      ];
      updatedAllocations[existingIndex] = RoleEmployeeAllocation(
          role: existingAlloc.role, employees: mergedEmployees);
    } else {
      updatedAllocations.add(
        RoleEmployeeAllocation(
          role: selectedRole,
          employees: List.from(_selectedEmployees),
        ),
      );
    }

    _addedAllocations.assignAll(updatedAllocations);
    _hasChanges.value = _checkIfChanged();
    controller.selectedRole.value = null;
    _selectedEmployees.clear();
    _employeeTextCtrl.clear();
  }

  void _handleSubmit() async {
    if (_addedAllocations.isEmpty) {
      showAppSnackbar(
        title: "Error",
        message: "Please add at least one allocation",
        type: SnackbarType.error,
      );
      return;
    }

    final payload = <Map<String, dynamic>>[];

    payload.addAll(_getRemovedEmployeesPayload());
    payload.addAll(_getAddedEmployeesPayload());

    final success =
        await ApiService.manageServiceProjectAllocation(payload: payload);

    if (success) {
      Get.back(result: true);
    } else {
      showAppSnackbar(
        title: "Error",
        message: "Failed to save allocation",
        type: SnackbarType.error,
      );
    }
  }

  List<Map<String, dynamic>> _getRemovedEmployeesPayload() {
    final removedPayload = <Map<String, dynamic>>[];

    final existingMap = {
      for (var alloc in widget.existingAllocations ?? [])
        alloc.role.id: alloc.employees.map((e) => e.id).toList()
    };

    final currentMap = {
      for (var alloc in _addedAllocations)
        alloc.role.id: alloc.employees.map((e) => e.id).toList()
    };

    existingMap.forEach((roleId, existingEmpIds) {
      final currentEmpIds = currentMap[roleId] ?? [];
      final removedEmpIds =
          existingEmpIds.where((id) => !currentEmpIds.contains(id)).toList();

      for (var empId in removedEmpIds) {
        removedPayload.add({
          "projectId": widget.projectId,
          "employeeId": empId.toString(),
          "teamRoleId": roleId,
          "isActive": false,
        });
      }
    });

    return removedPayload;
  }

  List<Map<String, dynamic>> _getAddedEmployeesPayload() {
    final addedPayload = <Map<String, dynamic>>[];

    final existingMap = {
      for (var alloc in widget.existingAllocations ?? [])
        alloc.role.id: alloc.employees.map((e) => e.id).toList()
    };

    for (var alloc in _addedAllocations) {
      final currentEmpIds = alloc.employees.map((e) => e.id).toList();
      final existingEmpIds = existingMap[alloc.role.id] ?? [];

      final newEmpIds =
          currentEmpIds.where((id) => !existingEmpIds.contains(id)).toList();

      for (var empId in newEmpIds) {
        addedPayload.add({
          "projectId": widget.projectId,
          "employeeId": empId.toString(),
          "teamRoleId": alloc.role.id,
          "isActive": true,
        });
      }
    }

    return addedPayload;
  }

  Widget _addedAllocationList() {
    return Obx(() {
      if (_addedAllocations.isEmpty) return const SizedBox.shrink();

      return Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          MyText.labelMedium("Added Allocations"),
          const Divider(height: 22, thickness: 1.2),
          ..._addedAllocations.map((alloc) => Padding(
                padding: const EdgeInsets.symmetric(vertical: 9),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    MyText.bodyMedium(
                      "Role: ${alloc.role.name}",
                      fontWeight: 700,
                    ),
                    const SizedBox(height: 7),
                    Wrap(
                      spacing: 7,
                      runSpacing: 5,
                      children: alloc.employees
                          .map((e) => Chip(
                                label: MyText.bodyMedium(
                                    "${e.firstName} ${e.lastName}"),
                                onDeleted: () {
                                  final updatedEmployees = alloc.employees
                                      .where((emp) => emp.id != e.id)
                                      .toList();

                                  if (updatedEmployees.isEmpty) {
                                    _addedAllocations.removeWhere(
                                        (a) => a.role.id == alloc.role.id);
                                  } else {
                                    final updatedAlloc = RoleEmployeeAllocation(
                                        role: alloc.role,
                                        employees: updatedEmployees);
                                    final index = _addedAllocations.indexWhere(
                                        (a) => a.role.id == alloc.role.id);
                                    _addedAllocations[index] = updatedAlloc;
                                  }

                                  _addedAllocations.refresh();
                                  _hasChanges.value = _checkIfChanged();
                                },
                                backgroundColor: Colors.grey.shade200,
                              ))
                          .toList(),
                    ),
                  ],
                ),
              )),
        ],
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Obx(() => BaseBottomSheet(
          title: "Allocate Employees",
          onCancel: () => Get.back(),
          onSubmit: _hasChanges.value
              ? _handleSubmit
              : () {
                  showAppSnackbar(
                    title: "No changes",
                    message: "You haven't made any changes to submit.",
                    type: SnackbarType.info,
                  );
                },
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                MyText.labelMedium("Select Role"),
                MySpacing.height(8),
                _roleDropdown(),
                MySpacing.height(12),
                MyText.labelMedium("Select Employees"),
                MySpacing.height(8),
                _employeeSelector(),
                MySpacing.height(8),
                Obx(() => Wrap(
                      spacing: 7,
                      runSpacing: 5,
                      children: _selectedEmployees
                          .map((emp) => Chip(
                                label: MyText.bodyMedium(
                                    "${emp.firstName} ${emp.lastName}"),
                                onDeleted: () {
                                  _selectedEmployees.remove(emp);
                                  _employeeTextCtrl.text = _selectedEmployees
                                      .map(
                                          (e) => "${e.firstName} ${e.lastName}")
                                      .join(", ");
                                },
                              ))
                          .toList(),
                    )),
                MySpacing.height(12),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton.icon(
                    icon: const Icon(Icons.add_circle_outline),
                    label: MyText.bodyMedium(
                      "Add Allocation",
                      fontWeight: 600,
                      color: Colors.white,
                    ),
                    onPressed: _handleAdd,
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 15),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                      backgroundColor: contentTheme.primary,
                      textStyle: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                ),
                MySpacing.height(12),
                _addedAllocationList(),
                MySpacing.height(12),
              ],
            ),
          ),
        ));
  }
}
