/**
 * Date/Time Utility Functions
 *
 * TIMEZONE ARCHITECTURE:
 * - Database: All times stored in UTC
 * - API Communication: Always UTC (both directions)
 * - Frontend Display: Convert based on business_timezone setting
 *   - If business_timezone is set: Display in that timezone
 *   - If business_timezone is blank/null: Display in user's local timezone
 *
 * See CLAUDE.md for full architecture documentation.
 */

// ============================================================================
// SENDING TO API - Convert to UTC
// ============================================================================

/**
 * Convert a local Date to UTC ISO string for API requests.
 * Use this when sending datetime values to the API.
 *
 * @example
 * const payload = { start_time: toUTC(selectedDate) };
 * // Returns: "2024-12-08T19:00:00.000Z"
 */
export const toUTC = (date: Date): string => {
  return date.toISOString();
};

/**
 * Convert a date and time (in display timezone) to UTC ISO string.
 * Use when the user selects a time that should be interpreted in a specific timezone.
 *
 * @param date - The date portion
 * @param time - Time string "HH:MM"
 * @param timezone - IANA timezone the user is selecting in (business or local)
 *
 * @example
 * // User in Eastern selects 2pm, but display mode is "business" (Mountain)
 * // This means they selected 2pm Mountain time
 * toUTCFromTimezone(date, "14:00", "America/Denver")
 */
export const toUTCFromTimezone = (
  date: Date,
  time: string,
  timezone: string
): string => {
  const [hours, minutes] = time.split(':').map(Number);
  const dateStr = formatLocalDate(date);

  // Create a date string that we'll parse in the target timezone
  const dateTimeStr = `${dateStr}T${time}:00`;

  // Use Intl to get the UTC offset for this timezone at this date/time
  const targetDate = new Date(dateTimeStr);
  const utcDate = convertTimezoneToUTC(targetDate, timezone);

  return utcDate.toISOString();
};

/**
 * Convert a Date object from a specific timezone to UTC.
 * The input Date's time values are interpreted as being in the given timezone.
 */
export const convertTimezoneToUTC = (date: Date, timezone: string): Date => {
  // Get the date/time components as they appear (treating as target timezone)
  const year = date.getFullYear();
  const month = date.getMonth();
  const day = date.getDate();
  const hours = date.getHours();
  const minutes = date.getMinutes();
  const seconds = date.getSeconds();

  // Create a formatter for the target timezone
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });

  // Find the UTC time that displays as our target time in the target timezone
  // We do this by creating a UTC date and adjusting based on offset
  const tempDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds));

  // Get what this UTC time displays as in the target timezone
  const parts = formatter.formatToParts(tempDate);
  const getPart = (type: string) => parseInt(parts.find(p => p.type === type)?.value || '0');

  const displayedHour = getPart('hour');
  const displayedMinute = getPart('minute');
  const displayedDay = getPart('day');

  // Calculate the offset in minutes
  const displayedMinutes = displayedDay * 24 * 60 + displayedHour * 60 + displayedMinute;
  const targetMinutes = day * 24 * 60 + hours * 60 + minutes;
  const offsetMinutes = displayedMinutes - targetMinutes;

  // Adjust the UTC time by the offset
  return new Date(tempDate.getTime() - offsetMinutes * 60 * 1000);
};

// ============================================================================
// RECEIVING FROM API - Convert from UTC for Display
// ============================================================================

/**
 * Convert a UTC datetime string from API to a Date object in the display timezone.
 *
 * @param utcString - ISO string from API (always UTC)
 * @param businessTimezone - IANA timezone of the business (null = use local)
 */
export const fromUTC = (
  utcString: string,
  businessTimezone?: string | null
): Date => {
  const utcDate = new Date(utcString);
  const targetTimezone = getDisplayTimezone(businessTimezone);
  return convertUTCToTimezone(utcDate, targetTimezone);
};

/**
 * Convert a UTC Date to display in a specific timezone.
 * Returns a Date object with values adjusted for the target timezone.
 */
export const convertUTCToTimezone = (utcDate: Date, timezone: string): Date => {
  // Format the UTC date in the target timezone
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });

  const parts = formatter.formatToParts(utcDate);
  const getPart = (type: string) => parseInt(parts.find(p => p.type === type)?.value || '0');

  // Create a new Date with the timezone-adjusted values
  // Note: This Date object's internal UTC value won't match, but the displayed values will be correct
  return new Date(
    getPart('year'),
    getPart('month') - 1,
    getPart('day'),
    getPart('hour'),
    getPart('minute'),
    getPart('second')
  );
};

/**
 * Get the timezone to use for display.
 * If businessTimezone is set, use it. Otherwise use user's local timezone.
 */
export const getDisplayTimezone = (businessTimezone?: string | null): string => {
  if (businessTimezone) {
    return businessTimezone;
  }
  // No business timezone set - use browser's local timezone
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
};

/**
 * Get the user's local timezone (browser timezone).
 */
export const getUserTimezone = (): string => {
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
};

// ============================================================================
// FORMATTING FOR DISPLAY
// ============================================================================

/**
 * Format a UTC datetime string for display, respecting timezone settings.
 *
 * @param utcString - ISO string from API
 * @param businessTimezone - IANA timezone of the business (null = use local)
 * @param options - Intl.DateTimeFormat options for customizing output
 *
 * @example
 * formatForDisplay("2024-12-08T19:00:00Z", "America/Denver")
 * // Returns: "Dec 8, 2024, 12:00 PM" (Mountain Time)
 *
 * formatForDisplay("2024-12-08T19:00:00Z", null)
 * // Returns time in user's local timezone
 */
export const formatForDisplay = (
  utcString: string,
  businessTimezone?: string | null,
  options?: Intl.DateTimeFormatOptions
): string => {
  const utcDate = new Date(utcString);
  const timezone = getDisplayTimezone(businessTimezone);

  const defaultOptions: Intl.DateTimeFormatOptions = {
    timeZone: timezone,
    year: 'numeric',
    month: 'short',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
    hour12: true,
    ...options,
  };

  return utcDate.toLocaleString('en-US', defaultOptions);
};

/**
 * Format just the time portion for display.
 *
 * @example
 * formatTimeForDisplay("2024-12-08T19:00:00Z", "America/Denver")
 * // Returns: "12:00 PM"
 */
export const formatTimeForDisplay = (
  utcString: string,
  businessTimezone?: string | null
): string => {
  const utcDate = new Date(utcString);
  const timezone = getDisplayTimezone(businessTimezone);

  return utcDate.toLocaleString('en-US', {
    timeZone: timezone,
    hour: 'numeric',
    minute: '2-digit',
    hour12: true,
  });
};

/**
 * Format just the date portion for display.
 *
 * @example
 * formatDateForDisplay("2024-12-08T19:00:00Z", "America/Denver")
 * // Returns: "Dec 8, 2024"
 */
export const formatDateForDisplay = (
  utcString: string,
  businessTimezone?: string | null,
  options?: Intl.DateTimeFormatOptions
): string => {
  const utcDate = new Date(utcString);
  const timezone = getDisplayTimezone(businessTimezone);

  const defaultOptions: Intl.DateTimeFormatOptions = {
    timeZone: timezone,
    year: 'numeric',
    month: 'short',
    day: 'numeric',
    ...options,
  };

  return utcDate.toLocaleDateString('en-US', defaultOptions);
};

/**
 * Format a datetime for datetime-local input, in the display timezone.
 * Returns: "YYYY-MM-DDTHH:MM"
 */
export const formatForDateTimeInput = (
  utcString: string,
  businessTimezone?: string | null
): string => {
  const displayDate = fromUTC(utcString, businessTimezone);
  return formatLocalDateTime(displayDate);
};

// ============================================================================
// DATE-ONLY HELPERS (for fields like time block start_date/end_date)
// ============================================================================

/**
 * Format a Date object as YYYY-MM-DD string.
 * Uses the Date's local values (not UTC).
 *
 * For date-only fields, use this when you have a Date object
 * representing a calendar date selection.
 */
export const formatLocalDate = (date: Date): string => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
};

/**
 * Parse a YYYY-MM-DD string as a local date (at midnight local time).
 */
export const parseLocalDate = (dateString: string): Date => {
  const [year, month, day] = dateString.split('-').map(Number);
  return new Date(year, month - 1, day);
};

/**
 * Format a Date as YYYY-MM-DDTHH:MM for datetime-local inputs.
 */
export const formatLocalDateTime = (date: Date): string => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  return `${year}-${month}-${day}T${hours}:${minutes}`;
};

/**
 * Get today's date as YYYY-MM-DD in a specific timezone.
 * Useful for determining "today" in the business timezone.
 */
export const getTodayInTimezone = (timezone: string): string => {
  const now = new Date();
  const formatter = new Intl.DateTimeFormat('en-CA', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  });
  return formatter.format(now); // Returns YYYY-MM-DD format
};

/**
 * Check if a date string (YYYY-MM-DD) is today in the given timezone.
 */
export const isToday = (dateString: string, timezone: string): boolean => {
  return dateString === getTodayInTimezone(timezone);
};

// ============================================================================
// UTILITY HELPERS
// ============================================================================

/**
 * Check if two dates are the same day (ignoring time).
 */
export const isSameDay = (date1: Date, date2: Date): boolean => {
  return (
    date1.getFullYear() === date2.getFullYear() &&
    date1.getMonth() === date2.getMonth() &&
    date1.getDate() === date2.getDate()
  );
};

/**
 * Get the start of a day (midnight local time).
 */
export const startOfDay = (date: Date): Date => {
  const result = new Date(date);
  result.setHours(0, 0, 0, 0);
  return result;
};

/**
 * Get the end of a day (23:59:59.999 local time).
 */
export const endOfDay = (date: Date): Date => {
  const result = new Date(date);
  result.setHours(23, 59, 59, 999);
  return result;
};

/**
 * Get timezone abbreviation for display (e.g., "MST", "EST").
 */
export const getTimezoneAbbreviation = (
  timezone: string,
  date: Date = new Date()
): string => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: timezone,
    timeZoneName: 'short',
  });

  const parts = formatter.formatToParts(date);
  return parts.find(p => p.type === 'timeZoneName')?.value || timezone;
};

/**
 * Format timezone for display (e.g., "Mountain Time (MST)").
 */
export const formatTimezoneDisplay = (timezone: string): string => {
  const abbr = getTimezoneAbbreviation(timezone);
  const cityName = timezone.split('/').pop()?.replace(/_/g, ' ') || timezone;
  return `${cityName} (${abbr})`;
};
