import 'package:intl/intl.dart';
import 'package:krow/core/data/models/shift/business_skill_model.dart';
import 'package:krow/core/data/models/shift/department_model.dart';
import 'package:krow/core/data/models/shift/event_shift_position_model.dart';
import 'package:krow/core/data/models/staff/pivot.dart';
import 'package:krow/core/entity/event_entity.dart';
import 'package:krow/core/entity/role_schedule_entity.dart';
import 'package:krow/core/entity/shift_entity.dart';
import 'package:krow/core/entity/staff_contact_entity.dart';

class PositionEntity {
  final String id;
  final DepartmentModel? department;
  final int? count;
  final int? breakDuration;
  final DateTime startTime;
  final DateTime endTime;
  final double price;
  final List<StaffContact> staffContacts;
  final EventShiftPositionModel? dto;
  final BusinessSkillModel? businessSkill;
  final List<RoleScheduleEntity>? schedule;
  ShiftEntity? parentShift;

  PositionEntity(
      {required this.id,
      this.department,
      this.count,
      this.breakDuration = 15,
      required this.startTime,
      required this.endTime,
      this.price = 0,
      this.staffContacts = const [],
      this.schedule,
      this.businessSkill,
      this.dto});

  PositionEntity copyWith({
    String? id,
    String? name,
    DepartmentModel? department,
    int? count,
    DateTime? startTime,
    DateTime? endTime,
    int? price,
    int? breakDuration,
    List<StaffContact>? staffContacts,
    BusinessSkillModel? businessSkill,
    EventShiftPositionModel? dto,
    List<RoleScheduleEntity>? schedule,
  }) {
    if (schedule != null) {
      schedule.sort((a, b) => a.dayIndex.compareTo(b.dayIndex));
    }

    var timeResult = _calcTimeSlot(startTime, endTime);

    var start = timeResult.start;
    var end = timeResult.end;
    businessSkill = businessSkill ?? this.businessSkill;

    var newEntity = PositionEntity(
      id: id ?? this.id,
      department: department ?? this.department,
      count: count ?? this.count,
      breakDuration: breakDuration ?? this.breakDuration,
      startTime: start,
      endTime: end,
      price: _getTotalCost(
          businessSkill?.price ?? 0, start, end, count ?? this.count ?? 0),
      staffContacts: staffContacts ?? this.staffContacts,
      businessSkill: businessSkill,
      dto: dto ?? this.dto,
      schedule: schedule ?? this.schedule,
    );

    newEntity.parentShift = parentShift;
    int index =
        parentShift?.positions.indexWhere((item) => item.id == this.id) ?? -1;
    if (index != -1) {
      parentShift?.positions[index] = newEntity;
    }

    var event = parentShift?.parentEvent;
    event?.totalCost.value = EventEntity.getTotalCost(event);

    return newEntity;
  }

  ({DateTime start, DateTime end}) _calcTimeSlot(
      DateTime? startTime, DateTime? endTime) {
    if (startTime != null) {
      startTime = startTime.copyWith(
          day: this.endTime.day,
          month: this.endTime.month,
          year: this.endTime.year);
    } else if (endTime != null) {
      endTime = endTime.copyWith(
          day: this.startTime.day,
          month: this.startTime.month,
          year: this.startTime.year);
    }

    const Duration minDuration = Duration(hours: 5);
    DateTime? updatedStartTime = startTime ?? this.startTime;
    DateTime? updatedEndTime = endTime ?? this.endTime;

    if (startTime != null && this.startTime != startTime) {
      Duration diff = updatedEndTime.difference(startTime);
      if (diff < minDuration) {
        updatedEndTime = startTime.add(minDuration);
      }
    } else if (endTime != null && this.endTime != endTime) {
      Duration diff = endTime.difference(updatedStartTime);
      if (diff < minDuration) {
        updatedStartTime = endTime.subtract(minDuration);
      }
    }

    if (updatedStartTime.day != updatedEndTime.day) {
      final DateTime midnight = DateTime(
        updatedStartTime.year,
        updatedStartTime.month,
        updatedStartTime.day,
        0,
        0,
      );
      updatedStartTime = midnight.subtract(const Duration(hours: 5));
      updatedEndTime = midnight;
    }

    ({DateTime start, DateTime end}) timeResult =
        (start: updatedStartTime, end: updatedEndTime);
    return timeResult;
  }

  static PositionEntity fromDto(EventShiftPositionModel model, DateTime date) {
    final DateFormat timeFormat = DateFormat('HH:mm');
    final DateTime start = timeFormat.parse(model.startTime);
    final DateTime end = timeFormat.parse(model.endTime);
    return PositionEntity(
      id: model.id,
      breakDuration: model.breakTime,
      department: model.department,
      dto: model,
      startTime: DateTime(date.year, date.month, date.day, start.hour,
          start.minute - start.minute % 10),
      endTime: DateTime(date.year, date.month, date.day, end.hour,
          end.minute - end.minute % 10),
      count: model.count,
      businessSkill: model.businessSkill,
      price: _getTotalCost(
          model.businessSkill.price ?? 0, start, end, model.count),
      staffContacts: model.staff?.map((e) {
            return StaffContact(
              id: e.pivot?.id ?? '',
              photoUrl: e.avatar ?? '',
              firstName: e.firstName ?? '',
              lastName: e.lastName ?? '',
              phoneNumber: e.phone ?? '',
              email: e.email ?? '',
              rate: model.businessSkill.price ?? 0,
              status: e.pivot?.status ?? PivotStatus.assigned,
              startAt: e.pivot?.startAt ?? '',
              endAt: e.pivot?.endAt ?? '',
              //todo
              isFavorite: false,
              isBlackListed:  false,
              skillName: e.pivot?.position?.businessSkill.skill?.name ?? '',
            )..rating.value = e.pivot?.rating?.rating ?? 0;
          }).toList() ??
          [],
    );
  }

  static empty() {
    return PositionEntity(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      startTime: DateTime.now().copyWith(hour: 9, minute: 0),
      endTime: DateTime.now().copyWith(hour: 14, minute: 0),
    );
  }

  static double _getTotalCost(
      double ratePerHour, DateTime startTime, DateTime endTime, int count) {
    final double hours = endTime.difference(startTime).inMinutes.abs() / 60.0;
    final double price = hours * ratePerHour;
    return price * count;
  }

  @override
  // TODO: implement hashCode
  int get hashCode =>
      id.hashCode ^
      department.hashCode ^
      count.hashCode ^
      breakDuration.hashCode ^
      startTime.hashCode ^
      endTime.hashCode ^
      price.hashCode ^
      staffContacts.hashCode ^
      dto.hashCode ^
      businessSkill.hashCode ^
      schedule.hashCode;

  @override
  bool operator ==(Object other) {
    return other is PositionEntity &&
        id == other.id &&
        department == other.department &&
        count == other.count &&
        breakDuration == other.breakDuration &&
        startTime == other.startTime &&
        endTime == other.endTime &&
        price == other.price &&
        staffContacts == other.staffContacts &&
        dto == other.dto &&
        businessSkill == other.businessSkill &&
        schedule == other.schedule;
  }
}
