import { LogUtility } from '../../utils/log';
import { CalendarTime } from './calendar-time';
import { CalendarDate } from './calendar-date';
import { getSystemTimezone } from './utils';
/**
 * CalendarDateTime centralizes timezone-aware conversions between native Date instances and calendar helpers.
 */
export class CalendarDateTime {
  constructor(date, timezone) {
    this.originalDate = date;
    this.timezone = timezone;
    this.syncDateTime();
  }
  /**
   * Build a CalendarDateTime from a native Date while the specific timezone.
   *
   * @param {Date} date - The native Date object to convert.
   * @param {string} timezone - The timezone to apply.
   * @returns {CalendarDateTime} A timezone-aware CalendarDateTime instance.
   */
  static fromDate(date, timezone) {
    return new CalendarDateTime(date, timezone);
  }
  /**
   * Build a CalendarDateTime from a date input (Date or string) and optional time metadata.
   *
   * @param {string | Date} date - The input date value (string or Date).
   * @param {CalendarDateTimeOptions} options - Additional options for time, timezone, seconds, etc.
   * @returns {CalendarDateTime} A timezone-aware CalendarDateTime instance.
   */
  static fromInitDataSource(date, options) {
    const baseDate = new Date(date);
    if (options.time) {
      const calendarTime = CalendarTime.fromString(options.time);
      baseDate.setHours(calendarTime.hours, calendarTime.minutes, 0, 0);
    }
    else if (options.endTimeMissing) {
      baseDate.setHours(23, 59, 59, 0);
    }
    return CalendarDateTime.fromDate(baseDate, options.timezone);
  }
  /**
   * Get the CalendarDate instance representing the date portion.
   *
   * @returns {CalendarDate} The CalendarDate object.
   */
  get calendarDate() {
    return this.date;
  }
  /**
   * Get the CalendarTime instance representing the time portion.
   *
   * @returns {CalendarTime} The CalendarTime object.
   */
  get calendarTime() {
    return this.time;
  }
  /**
   * Get a native Date object representing this CalendarDateTime.
   *
   * @returns {Date} The native Date object.
   */
  toDate() {
    return this.date.toDate();
  }
  /**
   * Set a new timezone and update CalendarDate/CalendarTime accordingly.
   *
   * @param {string} newTimezone - The new timezone to set.
   */
  setTimezone(newTimezone) {
    this.timezone = newTimezone;
    this.syncDateTime();
  }
  /**
   * Sync CalendarDate and CalendarTime with the current originalDate and timezone.
   */
  syncDateTime() {
    const normalized = this.convertDateToTimezone(this.originalDate);
    this.date = new CalendarDate(normalized.getFullYear(), normalized.getMonth() + 1, normalized.getDate());
    this.time = CalendarTime.fromDate(normalized);
  }
  /**
   * Convert a Date to the specific timezone.
   *
   * @param {Date} date - The date to convert.
   * @returns {Date} The date adjusted to the specific timezone.
   */
  convertDateToTimezone(date) {
    if (this.timezone === getSystemTimezone()) {
      return date;
    }
    try {
      const formatter = new Intl.DateTimeFormat([], {
        timeZone: this.timezone,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
      });
      const parts = formatter.formatToParts(date);
      const yearPart = this.findDateTimeFormatPart(parts, 'year');
      const monthPart = this.findDateTimeFormatPart(parts, 'month');
      const dayPart = this.findDateTimeFormatPart(parts, 'day');
      const hourPart = this.findDateTimeFormatPart(parts, 'hour');
      const minutePart = this.findDateTimeFormatPart(parts, 'minute');
      const secondPart = this.findDateTimeFormatPart(parts, 'second');
      if (yearPart && monthPart && dayPart && hourPart && minutePart && secondPart) {
        const year = parseInt(yearPart.value, 10);
        const month = parseInt(monthPart.value, 10);
        const day = parseInt(dayPart.value, 10);
        const hour = parseInt(hourPart.value, 10);
        const minute = parseInt(minutePart.value, 10);
        const second = parseInt(secondPart.value, 10);
        return new Date(year, month - 1, day, hour, minute, second);
      }
    }
    catch (_a) {
      LogUtility.warn('Invalid timezone:', this.timezone);
    }
    return date;
  }
  /**
   * Find a specific part in the Intl.DateTimeFormatPart array.
   *
   * @param {Intl.DateTimeFormatPart[]} parts - The array of date/time format parts.
   * @param {DateTimeFormatPartType} type - The type of part to find (e.g., 'year', 'month').
   * @returns {Intl.DateTimeFormatPart | undefined} The matching part, if found.
   */
  findDateTimeFormatPart(parts, type) {
    return parts.find((part) => part.type === type);
  }
}
