import * as SunCalc from '@noim/suncalc3';
import { DateTime, WeekdayNumbers } from 'luxon';

import { CHART_DATA, MOON_PIC_WEBP } from '../const';
import { LocalizeFunc, FrontendLocaleData } from '../ha';
import { formatNumber } from '../ha/common/number/format_number';
import setupTranslation from '../localize/translate';
import { MoonData, MoonDataItem, MoonImage, DynamicChartData } from '../types/config/chart-config';
import { LunarPhaseCardConfig } from '../types/config/lunar-phase-card-config';
import { convertKmToMiles, compareTime, useAmPm } from '../utils/helpers';

type Location = {
  latitude: number;
  longitude: number;
};

export const PHASE_MDI: Record<SunCalc.IPhaseObj['id'], string> = {
  newMoon: 'mdi:moon-new',
  waxingCrescentMoon: 'mdi:moon-waxing-crescent',
  firstQuarterMoon: 'mdi:moon-first-quarter',
  waxingGibbousMoon: 'mdi:moon-waxing-gibbous',
  fullMoon: 'mdi:moon-full',
  waningGibbousMoon: 'mdi:moon-waning-gibbous',
  thirdQuarterMoon: 'mdi:moon-last-quarter',
  waningCrescentMoon: 'mdi:moon-waning-crescent',
};

// Moon class
export class Moon {
  readonly _date: Date;
  readonly location: Location;
  readonly config: LunarPhaseCardConfig;
  readonly locale: FrontendLocaleData;
  readonly useMiles: boolean;
  readonly lang: string;
  private localize: LocalizeFunc;
  public _sunCalc = SunCalc;

  constructor(data: { date: Date; config: LunarPhaseCardConfig; locale: FrontendLocaleData }) {
    this._date = data.date;
    this.lang = data.locale.language;
    this.config = data.config;
    this.location = { latitude: data.config.latitude, longitude: data.config.longitude } as Location;
    this.locale = data.locale;
    this.useMiles = this.config.mile_unit || false;
    this.localize = setupTranslation(this.lang);
  }

  formatTime = (time: number | Date): string => {
    const dateObj = this.computeDateTime(new Date(time));
    const timeFormat = useAmPm(this.locale) ? 't' : 'T';
    return dateObj.toFormat(timeFormat);
  };

  private convertKmToMiles = (km: number): number => {
    return convertKmToMiles(km, this.useMiles);
  };

  private formatNumber = (num: number): string => {
    const decimal = this.config.number_decimals;
    const numberValue = num.toFixed(decimal);
    return formatNumber(numberValue, this.locale);
  };

  private computeDateTime = (date: Date): DateTime => {
    return DateTime.fromJSDate(date).setLocale(this.lang);
  };

  public get _dynamicDate(): Date {
    const now = DateTime.now();
    return now.toJSDate();
  }

  get _moonTime(): SunCalc.IMoonTimes {
    return SunCalc.getMoonTimes(this._date, this.location.latitude, this.location.longitude);
  }

  get _moonData(): SunCalc.IMoonData {
    return SunCalc.getMoonData(this._date, this.location.latitude, this.location.longitude);
  }

  get _moonIllimination(): SunCalc.IMoonIllumination {
    return SunCalc.getMoonIllumination(this._date);
  }
  get moonTransit() {
    const riseTime = this._moonTime.rise;
    const setTime = this._moonTime.set;
    return SunCalc.moonTransit(riseTime, setTime, this.location.latitude, this.location.longitude);
  }

  get _moonPosition(): SunCalc.IMoonPosition {
    return SunCalc.getMoonPosition(this._date, this.location.latitude, this.location.longitude);
  }

  get phaseName(): string {
    return this.localize(`card.phase.${this._moonData.illumination.phase.id}`);
  }

  get nextPhase(): MoonDataItem {
    const next = this._moonData.illumination.next;
    const { date, type } = next;
    let phaseName = '';
    if (type === 'fullMoon' || type === 'newMoon') {
      phaseName = this.localize(`card.phase.${type}`);
    } else {
      phaseName = this.localize(`card.phase.${type}Moon`);
    }
    const _relativeTime = this.computeDateTime(new Date(date)).toRelative();
    return {
      label: 'Next phase',
      value: `${phaseName} (${_relativeTime})`,
    };
  }

  get _moonTimeFromNow(): SunCalc.IMoonTimes {
    return SunCalc.getMoonTimes(this._dynamicDate, this.location.latitude, this.location.longitude);
  }

  get moonImage(): MoonImage {
    const { phaseValue, fraction, phase } = this._moonData.illumination;
    const phaseIndex = Math.round(phaseValue * 30) % 31;
    const moonFraction = Math.round(fraction * 100);
    const moonUrl = MOON_PIC_WEBP(phaseIndex);
    const { zenithAngle, parallacticAngle } = this._moonData;
    const rotateDeg = (zenithAngle - parallacticAngle) * (180 / Math.PI);
    return {
      moonPic: moonUrl,
      rotateDeg: rotateDeg,
      southernHemisphere: this.config.southern_hemisphere || false,
      fraction: moonFraction,
      phase,
      phaseMdi: PHASE_MDI[phase.id],
      emoji: phase.emoji,
    };
  }

  blankBeforeUnit = (unit: string): string => {
    if (unit === '°') {
      return '';
    }
    if (unit === '%') {
      if (['cs', 'de', 'fi', 'fr', 'sk', 'sv'].includes(this.lang)) {
        return ' ';
      } else {
        return '';
      }
    }
    return ' ';
  };

  createItem = (label: string, value: string, unit?: string, secondValue?: string): MoonDataItem => ({
    label: this.localize(`card.${label}`),
    value: `${value}${unit ? this.blankBeforeUnit(unit) + unit : ''}`,
    secondValue: secondValue ? `${secondValue}` : '',
  });

  createMoonTime = (key: string, time: number | Date): MoonDataItem => {
    const timeString = this.formatTime(time);
    const secondValue = compareTime(new Date(time))
      ? (this.computeDateTime(new Date(time)).toRelative() as string)
      : '';
    return this.createItem(key, timeString, '', secondValue);
  };

  // Helper function to format date as short time string
  shortTime = (date: number | Date) =>
    new Date(date).toLocaleDateString(this.lang, { weekday: 'short', month: 'short', day: 'numeric' });

  _getMoonRotation() {
    const { zenithAngle, parallacticAngle } = this._moonData;
    const rotateDeg = (zenithAngle - parallacticAngle) * (180 / Math.PI);
    return rotateDeg;
  }

  get moonData(): MoonData {
    const {
      createItem,
      createMoonTime,
      convertKmToMiles,
      formatNumber,
      localize,
      useMiles,
      shortTime,
      convertCardinal,
    } = this;

    // Destructure relevant data
    const { distance, azimuthDegrees, altitudeDegrees, illumination } = this._moonData;
    const {
      fraction,
      phaseValue,
      next: { fullMoon, newMoon, value, type },
    } = illumination;
    const { rise, set, highest } = this._moonTime;
    // Format numeric values
    const formatted = {
      moonFraction: formatNumber(fraction * 100),
      moonAge: formatNumber(phaseValue * 29.53),
      distance: formatNumber(convertKmToMiles(distance)),
      azimuth: formatNumber(azimuthDegrees),
      altitude: formatNumber(altitudeDegrees),
    };

    const cardinal = convertCardinal(azimuthDegrees);

    const nextPhaseName =
      type === 'fullMoon' || type === 'newMoon' ? localize(`card.phase.${type}`) : localize(`card.phase.${type}Moon`);
    const nextPhaseTime = shortTime(value);

    // Construct moon data items
    return {
      moonAge: createItem('moonAge', formatted.moonAge, localize('card.relativeTime.days')),
      moonFraction: createItem('illumination', formatted.moonFraction, '%'),
      azimuthDegress: createItem('azimuth', formatted.azimuth, '°', cardinal),
      altitudeDegrees: createItem('altitude', formatted.altitude, '°'),
      distance: createItem('distance', formatted.distance, useMiles ? 'mi' : 'km'),
      position: createItem('position', localize(`card.${altitudeDegrees > 0 ? 'overHorizon' : 'underHorizon'}`)),
      moonRise: createMoonTime('moonRise', rise),
      moonSet: createMoonTime('moonSet', set),
      moonHighest: createMoonTime('moonHigh', new Date(highest as Date)),
      nextFullMoon: createItem('fullMoon', shortTime(fullMoon.value)),
      nextNewMoon: createItem('newMoon', shortTime(newMoon.value)),
      nextPhase: createItem('nextPhase', `${nextPhaseName} (${nextPhaseTime})`),
      direction: createItem('azimuth', formatted.azimuth, '°', cardinal),
    };
  }

  get todayDataItem() {
    const { position, direction, altitudeDegrees, moonFraction, distance } = this.moonData;
    return {
      position,
      direction,
      altitudeDegrees,
      moonFraction,
      distance,
    };
  }

  get todayData() {
    const today = new Date();
    const startTime = new Date(today.setHours(0, 30, 0, 0));
    const timeToday = SunCalc.getMoonTimes(today, this.location.latitude, this.location.longitude);
    // moon highest
    const moonHighest = this._getMoonHighest(timeToday.highest as Date);
    // current moon

    // time markers

    // dataset

    let dataWithXY = this._getDataAltitude(startTime);

    if (moonHighest.rawData.y >= 0) {
      const changedIndexWithHighest = this._getClosestIndex(moonHighest.rawData.x, dataWithXY);
      dataWithXY[changedIndexWithHighest] = moonHighest.rawData;
    }

    dataWithXY = dataWithXY.sort((a, b) => a.x - b.x);

    const timeLabels = Object.values(dataWithXY).map((item) => item.x);
    const altitudeValues = Object.values(dataWithXY).map((item) => item.y);
    const minMaxY = {
      sugestedYMax: Math.round(Math.max(...altitudeValues)),
      sugestedYMin: Math.round(Math.min(...altitudeValues)),
    };

    const dataCotent = {
      time: timeToday,
      moonHighest,
      altitude: dataWithXY,
      timeLabels,
      altitudeValues,
      minMaxY,
      moonPhase: this._moonData.illumination,
      lang: {
        rise: this.localize('card.moonRise'),
        set: this.localize('card.moonSet'),
      },
    };

    return dataCotent;
  }

  get currentMoonData() {
    const currentMoon = this._fetchtCurrentMoon();
    return currentMoon;
  }

  get timeMarkers() {
    const timeMarkers = ['rise', 'set'].map((key) => this.timeDataSet(key));
    return timeMarkers;
  }

  get calendarEvents() {
    const events: { title: string; start: string; allDay: boolean }[] = [];
    const today = new Date();
    const year = today.getFullYear();
    const month = today.getMonth();

    const daysInMonth = new Date(year, month + 1, 0).getDate();
    for (let i = 1; i <= daysInMonth; i++) {
      const day = new Date(year, month, i);
      const moonIlumin = SunCalc.getMoonIllumination(day);
      const phaseEmoji = moonIlumin.phase.emoji;
      const phaseAge = this.formatNumber(moonIlumin.phaseValue * 29.53);

      events.push({
        title: `${phaseEmoji} ${phaseAge}`,
        start: day.toISOString().split('T')[0],
        allDay: true,
      });
    }
    return events;
  }

  get _dynamicChartData(): DynamicChartData {
    const now = this._dynamicDate;
    const offsetTime = new Date(now);
    offsetTime.setHours(now.getHours() - CHART_DATA.OFFSET_TIME, now.getMinutes());

    const startTime = offsetTime;
    const chartData = this._getDynamicDataAltitude(startTime);
    const moonTimes = this._getDynamicMoonTime(startTime);
    const moonIluumination = SunCalc.getMoonIllumination(startTime);
    const moonData = SunCalc.getMoonData(now, this.location.latitude, this.location.longitude);

    const dataResult = {
      chartData,
      times: {
        moon: moonTimes,
      },
      moonIllumination: moonIluumination,
      moonData,
    };
    return dataResult;
  }

  get timeData() {
    return {
      moon: this._getTimeData('moon'),
    };
  }

  private _getDynamicDataAltitude = (startTime: Date) => {
    const step = 5;
    const location = this.location;
    const stepSize = step * 60 * 1000;
    const timeRange = 24 * 60 * 60 * 1000;
    const steps = timeRange / stepSize;
    const result: { timeLabel: number; moon: { altitude: number; azimuth: string } }[] = [];
    for (let i = 0; i < steps; i++) {
      const time = new Date(startTime.getTime() + i * stepSize);
      const { altitudeDegrees, azimuthDegrees } = SunCalc.getMoonPosition(time, location.latitude, location.longitude);
      const azimuth = this.formatNumber(azimuthDegrees);
      const cardinal = this.convertCardinal(azimuthDegrees);
      const direction = `${azimuth}° ${cardinal}`;
      result.push({
        timeLabel: time.getTime(),
        moon: {
          altitude: Number(altitudeDegrees.toFixed(2)),
          azimuth: direction,
        },
      });
    }
    return result;
  };

  private _getDynamicMoonTime(startTime: Date): number[] {
    const location = this.location;
    const nextDay = new Date(startTime);
    nextDay.setDate(nextDay.getDate() + 1);
    const todayMoonTimes = SunCalc.getMoonTimes(startTime, location.latitude, location.longitude);
    const tomorrowMoonTimes = SunCalc.getMoonTimes(nextDay, location.latitude, location.longitude);
    return [todayMoonTimes.rise, todayMoonTimes.set, tomorrowMoonTimes.rise, tomorrowMoonTimes.set].map((time) =>
      new Date(time).getTime()
    );
  }
  private _getMoonTimeRangeItem(): {
    start: { key: 'moonRise' | 'moonSet'; value: number };
    end: { key: 'moonRise' | 'moonSet'; value: number };
  } {
    const { latitude, longitude } = this.location;
    const now = this._dynamicDate;
    const nextDay = new Date(now);
    nextDay.setDate(nextDay.getDate() + 1);

    const todayMoonTimes = SunCalc.getMoonTimes(now, latitude, longitude);
    const tomorrowMoonTimes = SunCalc.getMoonTimes(nextDay, latitude, longitude);

    const timeline: { key: 'moonRise' | 'moonSet'; value: number }[] = [];

    if (todayMoonTimes.set) {
      timeline.push({ key: 'moonSet', value: new Date(todayMoonTimes.set).getTime() });
    }
    if (todayMoonTimes.rise) {
      timeline.push({ key: 'moonRise', value: new Date(todayMoonTimes.rise).getTime() });
    }
    if (tomorrowMoonTimes.set) {
      timeline.push({ key: 'moonSet', value: new Date(tomorrowMoonTimes.set).getTime() });
    }
    if (tomorrowMoonTimes.rise) {
      timeline.push({ key: 'moonRise', value: new Date(tomorrowMoonTimes.rise).getTime() });
    }

    timeline.sort((a, b) => a.value - b.value);

    let start = timeline[0];
    let end = timeline[timeline.length - 1];
    const nowTime = now.getTime();

    for (let i = 0; i < timeline.length - 1; i++) {
      if (nowTime >= timeline[i].value && nowTime < timeline[i + 1].value) {
        start = timeline[i];
        end = timeline[i + 1];
        break;
      }
    }

    return { start, end };
  }

  _getMinimalData = (): { start: MoonDataItem; end: MoonDataItem } => {
    const { start, end } = this._getMoonTimeRangeItem();
    const startTime = this.createMoonTime(start.key, start.value);
    const endTime = this.createMoonTime(end.key, end.value);
    return {
      start: startTime,
      end: endTime,
    };
  };

  private _getTimeData = (
    type: 'moon' | 'sun'
  ): { time: string; index: number; opacity: number; originalTime: number }[] => {
    const timeLabels = this._dynamicChartData.chartData.map((data) => data.timeLabel);
    const inrange = (time: number): boolean => {
      const startTime = timeLabels[0];
      const endTime = timeLabels[timeLabels.length - 1];
      return time >= startTime && time <= endTime;
    };

    const closestTime = (time: number): number => {
      const closest = timeLabels.reduce((prev, curr) => (Math.abs(curr - time) < Math.abs(prev - time) ? curr : prev));
      return closest;
    };

    const isPast = (time: number): Boolean => {
      return new Date(closestTime(time)) < this._date ? true : false;
    };

    const times = this._dynamicChartData.times[type];
    return times
      .filter((time: number) => inrange(time))
      .map((time: number) => ({
        time: this.formatTime(time),
        index: timeLabels.indexOf(closestTime(time)),
        opacity: isPast(time) ? 0.5 : 1,
        originalTime: time,
      }));
  };

  _getMoonHighest = (highest: number | Date): Record<string, any> => {
    const { formatNumber } = this;
    const time = new Date(highest);
    const moonTransitPosition = SunCalc.getMoonPosition(time, this.location.latitude, this.location.longitude);

    const altitude = `${formatNumber(moonTransitPosition.altitudeDegrees)}°`;
    const azimuth = formatNumber(moonTransitPosition.azimuthDegrees);
    const cardinal = this.convertCardinal(moonTransitPosition.azimuthDegrees);
    const direction = `${azimuth}° ${cardinal}`;
    const formatedTime = this.formatTime(time);
    const rawData = {
      x: time.getTime(),
      y: Number(moonTransitPosition.altitudeDegrees.toFixed(2)),
    };

    const contentBody: string[] = [];
    contentBody.push(altitude);
    contentBody.push(direction);
    return { formatedTime, contentBody, rawData };
  };

  _getCurrentMoonData = (): string => {
    const now = new Date();
    const currentData = SunCalc.getMoonData(now, this.location.latitude, this.location.longitude);
    const { azimuthDegrees } = currentData;
    const formatNumber = this.formatNumber(azimuthDegrees);
    const cardinal = this.convertCardinal(azimuthDegrees);
    const direction = `${formatNumber}° ${cardinal}`;
    const formatedTime = this.formatTime(now);

    const contentText = `${formatedTime} - ${direction}`;
    return contentText;
  };

  _getAltituteData = (startTime: Date): { [key: string]: number } => {
    const result: { [key: string]: number } = {};
    const stepSize = 15 * 60 * 1000; // 15 minutes
    // time range 1 day
    const timeRange = 24 * 60 * 60 * 1000;
    const steps = timeRange / stepSize;

    for (let i = 0; i < steps; i++) {
      const time = new Date(startTime.getTime() + i * stepSize);
      const formatedTime = this.formatTime(time);
      const position = SunCalc.getMoonPosition(time, this.location.latitude, this.location.longitude);
      result[formatedTime] = Number(position.altitudeDegrees.toFixed(2));
    }

    return result;
  };

  _getDataAltitude = (startTime: Date) => {
    const stepConfig = this.config.graph_config?.time_step_size ?? 30;
    const result: { x: number; y: number }[] = [];
    const stepSize = stepConfig * 60 * 1000; // time step in minutes
    const timeRange = 24 * 60 * 60 * 1000; // 1 day
    const steps = timeRange / stepSize;

    for (let i = 0; i < steps; i++) {
      const time = new Date(startTime.getTime() + i * stepSize);
      const formatedTime = time.getTime();
      const position = SunCalc.getMoonPosition(time, this.location.latitude, this.location.longitude);
      result.push({ x: formatedTime, y: Number(position.altitudeDegrees.toFixed(2)) });
    }
    return result;
  };

  _getMoonTime = (today: Date): SunCalc.IMoonTimes => {
    return SunCalc.getMoonTimes(today, this.location.latitude, this.location.longitude);
  };

  _getMoonPosition = (today: Date): SunCalc.IMoonPosition => {
    return SunCalc.getMoonPosition(today, this.location.latitude, this.location.longitude);
  };

  _getMoonTransit = (rise: Date, set: Date): { main: Date | null; invert: Date | null } => {
    return SunCalc.moonTransit(rise, set, this.location.latitude, this.location.longitude);
  };

  _fetchtCurrentMoon = (): Record<string, any> => {
    const now = new Date();
    const currentData = SunCalc.getMoonData(now, this.location.latitude, this.location.longitude);
    const { azimuthDegrees, altitudeDegrees } = currentData;
    const formatNumber = this.formatNumber(azimuthDegrees);
    const cardinal = this.convertCardinal(azimuthDegrees);
    const direction = `${formatNumber}° ${cardinal}`;
    const formatedTime = this.formatTime(now);
    const altitude = `${this.formatNumber(altitudeDegrees)}°`;
    const rawData = {
      x: now.getTime(),
      y: Number(altitudeDegrees.toFixed(2)),
    };

    const currentHourIndex = this._getClosestIndex(rawData.x, this.todayData.altitude);

    const contentBody: string[] = [];
    contentBody.push(altitude);
    contentBody.push(direction);

    return { currentHourIndex, body: contentBody, title: formatedTime, altitudeDegrees, rawData };
  };

  private convertCardinal = (degrees: number): string => {
    const pointsMap = [
      'N',
      'NNE',
      'NE',
      'ENE',
      'E',
      'ESE',
      'SE',
      'SSE',
      'S',
      'SSW',
      'SW',
      'WSW',
      'W',
      'WNW',
      'NW',
      'NNW',
    ];
    const index = Math.round(degrees / 22.5) % 16;
    const value = pointsMap[index];
    return this.localize(`card.cardinalShort.${value}`);
  };

  timeDataSet = (timeKey: string): Record<string, any> => {
    const showOnChart = (time: Date): boolean => {
      const todayStart = new Date();
      todayStart.setHours(0, 0, 0, 0);

      const todayEnd = new Date();
      todayEnd.setHours(23, 59, 59, 999);
      return time > todayStart && time < todayEnd;
    };

    const today = new Date();
    const timeData = SunCalc.getMoonTimes(today, this.location.latitude, this.location.longitude);
    const time = new Date(timeData[timeKey]);
    const timePosition = SunCalc.getMoonPosition(time, this.location.latitude, this.location.longitude);
    const altitude = timePosition.altitudeDegrees;
    // Formated direction data
    const azimuth = timePosition.azimuthDegrees;
    const cardinal = this.convertCardinal(azimuth);
    const formatedAzimuth = this.formatNumber(azimuth);
    const direction = `${formatedAzimuth}°${cardinal}`;
    const formattedAltitude = this.formatNumber(altitude);

    // Formated time
    const formatedTime = this.formatTime(time);

    const rawData = {
      x: time.getTime(),
      y: Number(altitude),
    };

    const body: string[] = [];
    body.push(`${formattedAltitude}°`);
    body.push(direction);

    // Show on chart
    const show = showOnChart(time);
    const isUp = timeKey === 'set' ? false : true;
    const lineOffset = 30;
    const closetIndex = this._getClosestIndex(rawData.x, this.todayData.altitude);
    const position = {
      index: closetIndex,
      altitude,
      closetIndex,
    };

    return { show, position, isUp, formatedTime, lineOffset, direction, rawData, body };
  };

  _getClosestIndex = (time: number, data: { x: number; y: number }[]): number => {
    const closest = data.reduce((prev, curr) => {
      return Math.abs(curr.x - time) < Math.abs(prev.x - time) ? curr : prev;
    });
    return data.indexOf(closest);
  };

  // Helper method to generate events for a specific date range
  getEventsForRange(start: Date, end: Date): { title: string; start: string; allDay: boolean }[] {
    const events: { title: string; start: string; allDay: boolean }[] = [];
    const daysInRange = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);

    for (let i = 0; i <= daysInRange; i++) {
      const day = new Date(start.getTime() + i * 1000 * 60 * 60 * 24);
      const moonIllumination = SunCalc.getMoonIllumination(day);
      const phaseEmoji = moonIllumination.phase.emoji;

      events.push({
        title: `${phaseEmoji}`,
        start: day.toISOString().split('T')[0],
        allDay: true,
      });
    }
    return events;
  }

  public _getDaysOfWeek(lang: string): string[] {
    const daysOfTheWeek = Array.from({ length: 7 }, (_, i) => {
      return DateTime.local()
        .set({ weekday: (i + 1) as WeekdayNumbers })
        .setLocale(lang)
        .toFormat('ccc');
    });
    return daysOfTheWeek;
  }

  public _getEmojiForPhase(date: Date): string {
    const moonIllumination = SunCalc.getMoonIllumination(date);
    return moonIllumination.phase.emoji;
  }

  public _getPhaseNameForPhase(date: Date): string {
    const moonIllumination = SunCalc.getMoonIllumination(date);
    return this.localize(`card.phase.${moonIllumination.phase.id}`);
  }

  public _getDataByDate(date: Date): {
    emoji: string;
    phaseId: string;
    phaseName: string;
    isNewMoonOrFullMoon: boolean;
  } {
    const moonIllumination = SunCalc.getMoonIllumination(date);
    const phaseId = moonIllumination.phase.id;
    const isNewMoonOrFullMoon = ['newMoon', 'fullMoon'].includes(phaseId);
    return {
      emoji: moonIllumination.phase.emoji,
      phaseId,
      phaseName: this.localize(`card.phase.${moonIllumination.phase.id}`),
      isNewMoonOrFullMoon,
    };
  }
}
