/**
 * Mawidi Demo Scheduler - Timezone Utilities
 * Timezone detection, conversion, and formatting functions
 */

import type { TimezoneInfo } from "../types/scheduler.types";
import {
  DEFAULT_TIMEZONE,
  SUPPORTED_TIMEZONES,
} from "../config/schedulerConfig";

/**
 * Detect user's timezone using Intl API
 *
 * @returns IANA timezone identifier
 */
export function detectUserTimezone(): string {
  try {
    return Intl.DateTimeFormat().resolvedOptions().timeZone;
  } catch (error) {
    console.warn("Failed to detect timezone, using default:", error);
    return DEFAULT_TIMEZONE;
  }
}

/**
 * Get comprehensive timezone information
 *
 * @param timezone - IANA timezone identifier (optional, defaults to user's timezone)
 * @returns TimezoneInfo object
 */
export function getTimezoneInfo(timezone?: string): TimezoneInfo {
  const tz = timezone || detectUserTimezone();
  const now = new Date();

  // Get timezone offset
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone: tz,
    timeZoneName: "short",
  });

  const parts = formatter.formatToParts(now);
  const abbreviation =
    parts.find((part) => part.type === "timeZoneName")?.value || "";

  // Calculate offset in minutes
  const offset = getTimezoneOffset(tz);

  // Check if currently in DST
  const isDST = isInDaylightSavingTime(now, tz);

  return {
    identifier: tz,
    offset,
    abbreviation,
    isDST,
  };
}

/**
 * Get timezone offset in minutes for a specific timezone
 *
 * @param timezone - IANA timezone identifier
 * @param date - Date to check offset for (default: now)
 * @returns Offset in minutes
 */
export function getTimezoneOffset(
  timezone: string,
  date: Date = new Date(),
): number {
  // Create dates in UTC and target timezone
  const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
  const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));

  // Calculate difference in minutes
  const offset = (tzDate.getTime() - utcDate.getTime()) / (1000 * 60);
  return offset;
}

/**
 * Check if a date is in daylight saving time for a timezone
 *
 * @param date - Date to check
 * @param timezone - IANA timezone identifier
 * @returns True if in DST
 */
export function isInDaylightSavingTime(date: Date, timezone: string): boolean {
  // Get offsets for January and July
  const january = new Date(date.getFullYear(), 0, 1);
  const july = new Date(date.getFullYear(), 6, 1);

  const janOffset = getTimezoneOffset(timezone, january);
  const julOffset = getTimezoneOffset(timezone, july);

  // If offsets are different, DST is observed
  if (janOffset === julOffset) {
    return false; // No DST
  }

  const currentOffset = getTimezoneOffset(timezone, date);
  return currentOffset !== Math.max(janOffset, julOffset);
}

/**
 * Convert date from one timezone to another
 *
 * @param date - Source date
 * @param fromTimezone - Source timezone
 * @param toTimezone - Target timezone
 * @returns Converted date
 */
export function convertTimezone(
  date: Date,
  fromTimezone: string,
  toTimezone: string,
): Date {
  // Convert to string in source timezone
  const dateString = date.toLocaleString("en-US", { timeZone: fromTimezone });

  // Parse in target timezone
  const targetDate = new Date(dateString);

  // Get the offset difference
  const fromOffset = getTimezoneOffset(fromTimezone, date);
  const toOffset = getTimezoneOffset(toTimezone, date);
  const offsetDiff = toOffset - fromOffset;

  // Apply offset difference
  targetDate.setMinutes(targetDate.getMinutes() + offsetDiff);

  return targetDate;
}

/**
 * Alias for convertTimezone (for backward compatibility)
 */
export const convertToTimezone = convertTimezone;

/**
 * Convert date to user's timezone
 *
 * @param date - UTC date
 * @returns Date in user's timezone
 */
export function toUserTimezone(date: Date): Date {
  const userTz = detectUserTimezone();
  return convertTimezone(date, "UTC", userTz);
}

/**
 * Convert date to UTC
 *
 * @param date - Date in user's timezone
 * @returns UTC date
 */
export function toUTC(date: Date): Date {
  const userTz = detectUserTimezone();
  return convertTimezone(date, userTz, "UTC");
}

/**
 * Convert date to default timezone (UTC+3)
 *
 * @param date - Source date
 * @returns Date in default timezone
 */
export function toDefaultTimezone(date: Date): Date {
  const userTz = detectUserTimezone();
  return convertTimezone(date, userTz, DEFAULT_TIMEZONE);
}

/**
 * Format timezone offset as string (e.g., "+03:00", "-05:00")
 *
 * @param offsetMinutes - Offset in minutes
 * @returns Formatted offset string
 */
export function formatTimezoneOffset(offsetMinutes: number): string {
  const sign = offsetMinutes >= 0 ? "+" : "-";
  const absOffset = Math.abs(offsetMinutes);
  const hours = Math.floor(absOffset / 60);
  const minutes = absOffset % 60;

  return `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(
    2,
    "0",
  )}`;
}

/**
 * Get user-friendly timezone name
 *
 * @param timezone - IANA timezone identifier
 * @param language - Language code
 * @returns Formatted timezone name
 */
export function getTimezoneName(
  timezone: string,
  language: "en" | "ar",
): string {
  const locale = language === "ar" ? "ar-SA" : "en-US";

  try {
    const formatter = new Intl.DateTimeFormat(locale, {
      timeZone: timezone,
      timeZoneName: "long",
    });

    const parts = formatter.formatToParts(new Date());
    const name = parts.find((part) => part.type === "timeZoneName")?.value;

    return name || timezone;
  } catch {
    return timezone;
  }
}

/**
 * Check if timezone is supported
 *
 * @param timezone - IANA timezone identifier
 * @returns True if supported
 */
export function isSupportedTimezone(timezone: string): boolean {
  return (SUPPORTED_TIMEZONES as readonly string[]).includes(timezone);
}

/**
 * Get the closest supported timezone to user's timezone
 *
 * @returns Supported timezone identifier
 */
export function getClosestSupportedTimezone(): string {
  const userTz = detectUserTimezone();

  // If user's timezone is supported, use it
  if (isSupportedTimezone(userTz)) {
    return userTz;
  }

  // Otherwise, find closest by offset
  const userOffset = getTimezoneOffset(userTz);
  let closestTz = DEFAULT_TIMEZONE;
  let closestDiff = Infinity;

  for (const tz of SUPPORTED_TIMEZONES) {
    const tzOffset = getTimezoneOffset(tz);
    const diff = Math.abs(tzOffset - userOffset);

    if (diff < closestDiff) {
      closestDiff = diff;
      closestTz = tz;
    }
  }

  return closestTz;
}

/**
 * Format date with timezone awareness
 *
 * @param date - Date to format
 * @param timezone - Target timezone
 * @param language - Language code
 * @param options - Intl.DateTimeFormat options
 * @returns Formatted date string
 */
export function formatDateWithTimezone(
  date: Date,
  timezone: string,
  language: "en" | "ar",
  options?: Intl.DateTimeFormatOptions,
): string {
  const locale = language === "ar" ? "ar-SA" : "en-US";

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

  return new Intl.DateTimeFormat(locale, defaultOptions).format(date);
}

/**
 * Format time with timezone awareness
 *
 * @param date - Date to format
 * @param timezone - Target timezone
 * @param language - Language code
 * @param options - Intl.DateTimeFormat options
 * @returns Formatted time string
 */
export function formatTimeWithTimezone(
  date: Date,
  timezone: string,
  language: "en" | "ar",
  options?: Intl.DateTimeFormatOptions,
): string {
  const locale = language === "ar" ? "ar-SA" : "en-US";
  const hour12 = language === "en";

  const defaultOptions: Intl.DateTimeFormatOptions = {
    timeZone: timezone,
    hour: "2-digit",
    minute: "2-digit",
    hour12,
    ...options,
  };

  return new Intl.DateTimeFormat(locale, defaultOptions).format(date);
}

/**
 * Alias for formatTimeWithTimezone (for backward compatibility)
 */
export const formatTimeForTimezone = formatTimeWithTimezone;

/**
 * Parse timezone-aware ISO string
 *
 * @param isoString - ISO 8601 datetime string
 * @param targetTimezone - Target timezone (optional, defaults to user's timezone)
 * @returns Date object in target timezone
 */
export function parseISOWithTimezone(
  isoString: string,
  targetTimezone?: string,
): Date {
  const date = new Date(isoString);
  const tz = targetTimezone || detectUserTimezone();

  return convertTimezone(date, "UTC", tz);
}

/**
 * Get all GCC timezone options for dropdown
 *
 * @param language - Language code
 * @returns Array of timezone options
 */
export function getTimezoneOptions(
  language: "en" | "ar",
): Array<{ value: string; label: string; offset: string }> {
  return SUPPORTED_TIMEZONES.map((tz) => {
    const offset = getTimezoneOffset(tz);
    const offsetString = formatTimezoneOffset(offset);
    const name = getTimezoneName(tz, language);

    return {
      value: tz,
      label: `${name} (UTC${offsetString})`,
      offset: offsetString,
    };
  });
}

/**
 * Calculate time until a datetime (for countdown displays)
 *
 * @param targetDate - Target datetime
 * @returns Object with days, hours, minutes, seconds until target
 */
export function getTimeUntil(targetDate: Date): {
  days: number;
  hours: number;
  minutes: number;
  seconds: number;
  total: number;
} {
  const now = new Date();
  const diff = targetDate.getTime() - now.getTime();

  if (diff <= 0) {
    return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };
  }

  const seconds = Math.floor(diff / 1000);
  const minutes = Math.floor(seconds / 60);
  const hours = Math.floor(minutes / 60);
  const days = Math.floor(hours / 24);

  return {
    days,
    hours: hours % 24,
    minutes: minutes % 60,
    seconds: seconds % 60,
    total: diff,
  };
}

/**
 * Format duration for display
 *
 * @param minutes - Duration in minutes
 * @param language - Language code
 * @returns Formatted duration string
 */
export function formatDuration(minutes: number, language: "en" | "ar"): string {
  if (language === "ar") {
    if (minutes === 30) return "٣٠ دقيقة";
    if (minutes === 60) return "٦٠ دقيقة";
    return `${minutes} دقيقة`;
  }

  if (minutes === 30) return "30 minutes";
  if (minutes === 60) return "1 hour";
  if (minutes < 60) return `${minutes} minutes`;

  const hours = Math.floor(minutes / 60);
  const mins = minutes % 60;

  if (mins === 0) return `${hours} hour${hours > 1 ? "s" : ""}`;
  return `${hours} hour${hours > 1 ? "s" : ""} ${mins} minutes`;
}

/**
 * Check if browser supports Intl.DateTimeFormat
 *
 * @returns True if supported
 */
export function isIntlSupported(): boolean {
  return (
    typeof Intl !== "undefined" && typeof Intl.DateTimeFormat !== "undefined"
  );
}

/**
 * Validate timezone identifier
 *
 * @param timezone - Timezone to validate
 * @returns True if valid IANA timezone
 */
export function isValidTimezone(timezone: string): boolean {
  if (!isIntlSupported()) {
    return false;
  }

  try {
    Intl.DateTimeFormat(undefined, { timeZone: timezone });
    return true;
  } catch {
    return false;
  }
}
