import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

const DAY_IN_MS = 24 * 60 * 60 * 1000;

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function formatDate(date: Date | string): string {
  return new Intl.DateTimeFormat("en-US", {
    month: "short",
    day: "numeric",
    year: "numeric",
  }).format(new Date(date));
}

export function formatTime(date: Date | string): string {
  return new Intl.DateTimeFormat("en-US", {
    hour: "numeric",
    minute: "2-digit",
    hour12: true,
  }).format(new Date(date));
}

export function startOfLocalDay(date: Date | string): Date {
  const result = new Date(date);
  result.setHours(0, 0, 0, 0);
  return result;
}

export function isSameLocalDay(a: Date | string, b: Date | string): boolean {
  const left = new Date(a);
  const right = new Date(b);
  return (
    left.getFullYear() === right.getFullYear() &&
    left.getMonth() === right.getMonth() &&
    left.getDate() === right.getDate()
  );
}

export function isAllDayTaskDeadline(deadline: Date | string): boolean {
  const date = new Date(deadline);
  if (Number.isNaN(date.getTime())) return false;
  return (
    date.getHours() === 0 &&
    date.getMinutes() === 0 &&
    date.getSeconds() === 0 &&
    date.getMilliseconds() === 0
  );
}

export function isTaskDeadlineDueToday(
  deadline: Date | string | null | undefined,
  now: Date = new Date(),
): boolean {
  if (!deadline) return false;
  const date = new Date(deadline);
  if (Number.isNaN(date.getTime())) return false;
  return isSameLocalDay(date, now);
}

export function isTaskDeadlineOverdue(
  deadline: Date | string | null | undefined,
  now: Date = new Date(),
): boolean {
  if (!deadline) return false;

  const date = new Date(deadline);
  if (Number.isNaN(date.getTime())) return false;

  if (isAllDayTaskDeadline(date)) {
    const nextDay = startOfLocalDay(date);
    nextDay.setDate(nextDay.getDate() + 1);
    return now.getTime() >= nextDay.getTime();
  }

  return now.getTime() > date.getTime();
}

export function formatRelativeDate(date: Date | string): string {
  const now = new Date();
  const target = new Date(date);
  const targetDay = startOfLocalDay(target);
  const nowDay = startOfLocalDay(now);
  const diffDays = Math.round(
    (Date.UTC(targetDay.getFullYear(), targetDay.getMonth(), targetDay.getDate()) -
      Date.UTC(nowDay.getFullYear(), nowDay.getMonth(), nowDay.getDate())) /
      DAY_IN_MS,
  );

  if (diffDays === 0) return "Today";
  if (diffDays === 1) return "Tomorrow";
  if (diffDays === -1) return "Yesterday";
  if (diffDays > 0 && diffDays <= 7) return `In ${diffDays} days`;
  if (diffDays < 0 && diffDays >= -7) return `${Math.abs(diffDays)} days ago`;
  return formatDate(date);
}
