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

export type EquipmentStatus = "active" | "service" | "retired";
export type BookingOperationTaskType = "preparation" | "delivery" | "pickup" | "verification";
export type OperationsEquipmentKind = "laptop" | "tablet" | "printer" | "router" | "flipchart";

export interface BookingOperationTask {
  date: string | null;
  time: string | null;
}

export interface BookingOperationsPlan {
  preparation: BookingOperationTask;
  delivery: BookingOperationTask;
  pickup: BookingOperationTask;
  verification: BookingOperationTask;
}

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

export type StatisticsRangePreset = "month" | "year" | "custom";

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;
  serviceItemCount: number;
  retiredItemCount: number;
  createdAt: string;
  updatedAt: string;
}

export interface EquipmentItem {
  id: string;
  categoryId: string;
  categoryName: string;
  name: string;
  shortName: string | null;
  useShortNameOnTimeline: boolean;
  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;
  relationGroupKey: string | null;
  customerName: string;
  orderNumber: string | null;
  projectNumber: string | null;
  projectName: string | null;
  notes: string | null;
  startDate: string;
  endDate: string;
  startTime: string | null;
  endTime: string | null;
  operationsPlan: BookingOperationsPlan | 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 interface RevenueByProductStat {
  categoryId: string;
  categoryName: string;
  productKey: string;
  productName: string;
  productShortName: string | null;
  bookingCount: number;
  revenue: number;
  lifetimeRevenue: number;
}

export interface RevenueByCategoryStat {
  categoryId: string;
  categoryName: string;
  bookingCount: number;
  revenue: number;
  lifetimeRevenue: number;
}

export interface RevenueByCustomerStat {
  customerName: string;
  bookingCount: number;
  revenue: number;
}

export interface UserClickStat {
  userId: string;
  userName: string;
  clickDate: string;
  clickCount: number;
}

export interface StatsResponse {
  from: string;
  to: string;
  totalRevenue: number;
  revenueByProduct: RevenueByProductStat[];
  revenueByCategory: RevenueByCategoryStat[];
  revenueByCustomer: RevenueByCustomerStat[];
  userClicks: UserClickStat[];
}

export interface OperationsTaskEntry {
  id: string;
  scopeKey: string;
  type: BookingOperationTaskType;
  date: string;
  time: string | null;
  customerName: string;
  orderNumber: string | null;
  projectNumber: string | null;
  projectName: string | null;
  bookingIds: string[];
  itemNames: string[];
  itemCount: number;
  taskItems: Array<{
    bookingId: string;
    equipmentItemId: string;
    name: string;
    kind: OperationsEquipmentKind | null;
    serialNumber: string | null;
    assetTag: string | null;
  }>;
  equipmentKinds: OperationsEquipmentKind[];
  checkedEquipmentKinds: OperationsEquipmentKind[];
  confirmedEquipmentItemIds: string[];
  isFinished: boolean;
  finishedAt: string | null;
}

export interface OperationsResponse {
  from: string;
  to: string;
  tasks: OperationsTaskEntry[];
}

export interface OperationsTaskProgressPayload {
  scopeKey: string;
  type: BookingOperationTaskType;
  date: string;
  time: string | null;
  checkedEquipmentKinds: OperationsEquipmentKind[];
  confirmedEquipmentItemIds: string[];
  markFinished: boolean;
}

export interface ChatUserSummary {
  id: string;
  name: string;
  role: UserRole;
}

export interface ChatMessage {
  id: string;
  senderId: string;
  senderName: string;
  recipientId: string;
  recipientName: string;
  body: string;
  createdAt: string;
  readAt: string | null;
}

export interface ChatThread {
  user: ChatUserSummary;
  lastMessage: ChatMessage | null;
  unreadCount: number;
}

export interface ChatConversation {
  user: ChatUserSummary;
  messages: ChatMessage[];
}

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

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 BOOKING_OPERATION_TASK_TYPES: Array<{ value: BookingOperationTaskType; label: string }> = [
  { value: "preparation", label: "Przygotowanie" },
  { value: "delivery", label: "Dostawa" },
  { value: "pickup", label: "Odbiór" },
  { value: "verification", label: "Weryfikacja" }
];

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);
};

const dayDelta = (from: string, to: string) => {
  const fromStamp = new Date(`${from}T12:00:00.000Z`).getTime();
  const toStamp = new Date(`${to}T12:00:00.000Z`).getTime();
  return Math.round((toStamp - fromStamp) / 86_400_000);
};

const createBookingOperationTask = (date: string | null, time: string | null): BookingOperationTask => ({
  date,
  time: normalizeBookingTime(time)
});

export const getDefaultBookingOperationsPlan = (input: {
  startDate: string;
  endDate: string;
  startTime?: string | null;
  endTime?: string | null;
}): BookingOperationsPlan => ({
  preparation: createBookingOperationTask(input.startDate, input.startTime ?? null),
  delivery: createBookingOperationTask(input.startDate, input.startTime ?? null),
  pickup: createBookingOperationTask(input.endDate, input.endTime ?? null),
  verification: createBookingOperationTask(input.endDate, input.endTime ?? null)
});

export const normalizeBookingOperationsPlan = (
  input: Partial<Record<BookingOperationTaskType, Partial<BookingOperationTask> | null>> | null | undefined,
  fallback: {
    startDate: string;
    endDate: string;
    startTime?: string | null;
    endTime?: string | null;
  }
): BookingOperationsPlan => {
  const defaults = getDefaultBookingOperationsPlan(fallback);

  return {
    preparation: createBookingOperationTask(input?.preparation?.date ?? defaults.preparation.date, input?.preparation?.time ?? defaults.preparation.time),
    delivery: createBookingOperationTask(input?.delivery?.date ?? defaults.delivery.date, input?.delivery?.time ?? defaults.delivery.time),
    pickup: createBookingOperationTask(input?.pickup?.date ?? defaults.pickup.date, input?.pickup?.time ?? defaults.pickup.time),
    verification: createBookingOperationTask(
      input?.verification?.date ?? defaults.verification.date,
      input?.verification?.time ?? defaults.verification.time
    )
  };
};

export const shiftBookingOperationsPlan = (
  operationsPlan: Partial<Record<BookingOperationTaskType, Partial<BookingOperationTask> | null>> | null | undefined,
  source: {
    startDate: string;
    endDate: string;
    startTime?: string | null;
    endTime?: string | null;
  },
  target: {
    startDate: string;
    endDate: string;
    startTime?: string | null;
    endTime?: string | null;
  }
): BookingOperationsPlan | null => {
  if (!operationsPlan) {
    return null;
  }

  const normalized = normalizeBookingOperationsPlan(operationsPlan, source);
  const startShift = dayDelta(source.startDate, target.startDate);
  const endShift = dayDelta(source.endDate, target.endDate);

  return {
    preparation: {
      date: normalized.preparation.date ? addDays(normalized.preparation.date, startShift) : null,
      time: normalized.preparation.time
    },
    delivery: {
      date: normalized.delivery.date ? addDays(normalized.delivery.date, startShift) : null,
      time: normalized.delivery.time
    },
    pickup: {
      date: normalized.pickup.date ? addDays(normalized.pickup.date, endShift) : null,
      time: normalized.pickup.time
    },
    verification: {
      date: normalized.verification.date ? addDays(normalized.verification.date, endShift) : null,
      time: normalized.verification.time
    }
  };
};

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 normalizeEquipmentModelName = (value: string) =>
  value
    .trim()
    .replace(/\s+(?:[A-Z](?:\d{1,3})?|#\d{1,3})$/i, "")
    .trim();

export const buildUnitLabel = (baseName: string, index: number, totalUnits: number) => {
  const normalizedBaseName = baseName.trim();

  if (totalUnits <= 1) {
    return normalizedBaseName;
  }

  return `${normalizedBaseName} #${String(index + 1).padStart(2, "0")}`;
};

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;
};
