export type UserRole = "admin" | "operator" | "viewer";

export type EquipmentStatus = "active" | "service" | "retired";

export interface UserPreferences {
  equipmentCategoryOrder: string[];
  equipmentItemOrder: string[];
}

export interface AuthUser {
  id: string;
  name: string;
  email: string;
  role: UserRole;
  isActive: boolean;
}

export interface AuthSession {
  token: string;
  user: AuthUser;
}

export interface EquipmentCategory {
  id: string;
  name: string;
  itemCount: number;
  activeItemCount: number;
  createdAt: string;
  updatedAt: string;
}

export interface EquipmentItem {
  id: string;
  categoryId: string;
  categoryName: string;
  name: string;
  serialNumber: string | null;
  assetTag: string | null;
  suggestedDayRate: number | null;
  status: EquipmentStatus;
  notes: string | null;
  isVisibleOnTimeline: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface RentalBooking {
  id: string;
  equipmentItemId: string;
  customerName: string;
  orderNumber: string | null;
  projectNumber: string | null;
  projectName: string | null;
  notes: string | null;
  startDate: string;
  endDate: string;
  startTime: string | null;
  endTime: string | null;
  dayRate: number | null;
  totalPrice: number | null;
  createdBy: string | null;
  createdByName: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface TimelineItem extends EquipmentItem {
  bookings: RentalBooking[];
}

export interface TimelineGroup {
  categoryId: string;
  categoryName: string;
  totalItems: number;
  visibleItems: number;
  items: TimelineItem[];
}

export interface TimelineFilters {
  from: string;
  to: string;
  categoryIds: string[];
  status: EquipmentStatus | "all";
  search: string;
  customerName: string;
  includeInactive: boolean;
}

export interface TimelineResponse {
  range: {
    from: string;
    to: string;
    dayCount: number;
  };
  filters: TimelineFilters;
  groups: TimelineGroup[];
}

export const USER_ROLE_OPTIONS: Array<{ value: UserRole; label: string }> = [
  { value: "admin", label: "Admin" },
  { value: "operator", label: "Operator" },
  { value: "viewer", label: "Viewer" }
];

export const EQUIPMENT_STATUS_OPTIONS: Array<{ value: EquipmentStatus; label: string }> = [
  { value: "active", label: "Aktywne" },
  { value: "service", label: "W serwisie" },
  { value: "retired", label: "Wyłączone" }
];

export const addDays = (date: string, days: number) => {
  const base = new Date(`${date}T12:00:00.000Z`);
  base.setUTCDate(base.getUTCDate() + days);
  return base.toISOString().slice(0, 10);
};

export const BOOKING_TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
export const DEFAULT_BOOKING_START_TIME = "00:00";
export const DEFAULT_BOOKING_END_TIME = "23:59";

export const normalizeBookingTime = (value?: string | null) => {
  if (!value || typeof value !== "string") {
    return null;
  }

  const trimmed = value.trim().slice(0, 5);
  return BOOKING_TIME_PATTERN.test(trimmed) ? trimmed : null;
};

const toUtcStamp = (date: string, time: string) => new Date(`${date}T${time}:00.000Z`).getTime();

export const getBookingStartStamp = (booking: Pick<RentalBooking, "startDate" | "startTime">) =>
  toUtcStamp(booking.startDate, normalizeBookingTime(booking.startTime) ?? DEFAULT_BOOKING_START_TIME);

export const getBookingEndStamp = (booking: Pick<RentalBooking, "endDate" | "endTime">) =>
  toUtcStamp(booking.endDate, normalizeBookingTime(booking.endTime) ?? DEFAULT_BOOKING_END_TIME);

export const bookingIntervalsOverlap = (
  left: Pick<RentalBooking, "startDate" | "endDate" | "startTime" | "endTime">,
  right: Pick<RentalBooking, "startDate" | "endDate" | "startTime" | "endTime">
) => getBookingStartStamp(left) < getBookingEndStamp(right) && getBookingEndStamp(left) > getBookingStartStamp(right);

export const bookingConflictOnDay = (
  left: Pick<RentalBooking, "startDate" | "endDate" | "startTime" | "endTime">,
  right: Pick<RentalBooking, "startDate" | "endDate" | "startTime" | "endTime">,
  day: string
) => {
  if (!bookingIntervalsOverlap(left, right)) {
    return false;
  }

  const dayStart = toUtcStamp(day, "00:00");
  const dayEnd = toUtcStamp(addDays(day, 1), "00:00");
  const overlapStart = Math.max(getBookingStartStamp(left), getBookingStartStamp(right), dayStart);
  const overlapEnd = Math.min(getBookingEndStamp(left), getBookingEndStamp(right), dayEnd);

  return overlapStart < overlapEnd;
};

export const enumerateDays = (from: string, to: string) => {
  const dates: string[] = [];
  let current = from;

  while (current <= to) {
    dates.push(current);
    current = addDays(current, 1);
  }

  return dates;
};

export const dayDiffInclusive = (from: string, to: string) => {
  const start = new Date(`${from}T12:00:00.000Z`).getTime();
  const end = new Date(`${to}T12:00:00.000Z`).getTime();
  return Math.floor((end - start) / 86_400_000) + 1;
};

export const roundCurrency = (value: number) => Math.round(value * 100) / 100;

export const calculateBookingTotalPrice = (startDate: string, endDate: string, dayRate?: number | null) => {
  if (typeof dayRate !== "number" || !Number.isFinite(dayRate)) {
    return null;
  }

  return roundCurrency(dayDiffInclusive(startDate, endDate) * dayRate);
};

export const bookingHasManualTotalPrice = (
  booking: Pick<RentalBooking, "startDate" | "endDate" | "dayRate" | "totalPrice">
) => {
  if (typeof booking.totalPrice !== "number" || !Number.isFinite(booking.totalPrice)) {
    return false;
  }

  const computedTotal = calculateBookingTotalPrice(booking.startDate, booking.endDate, booking.dayRate);

  if (computedTotal === null) {
    return true;
  }

  return Math.abs(booking.totalPrice - computedTotal) > 0.009;
};
