import { FieldType, Segment } from "@hamradio/packet";

import { ITimestamp } from "./frame.types";

export class Timestamp implements ITimestamp {
  day?: number;
  month?: number;
  hours: number;
  minutes: number;
  seconds?: number;
  format: "DHM" | "HMS" | "MDHM";
  zulu?: boolean;

  constructor(
    hours: number,
    minutes: number,
    format: "DHM" | "HMS" | "MDHM",
    options: {
      day?: number;
      month?: number;
      seconds?: number;
      zulu?: boolean;
    } = {}
  ) {
    this.hours = hours;
    this.minutes = minutes;
    this.format = format;
    this.day = options.day;
    this.month = options.month;
    this.seconds = options.seconds;
    this.zulu = options.zulu;
  }

  /**
   * Convert APRS timestamp to JavaScript Date object
   * Note: APRS timestamps don't include year, so we use current year
   * For DHM format, we find the most recent occurrence of that day
   * For HMS format, we use current date
   * For MDHM format, we use the specified month/day in current year
   */
  toDate(): Date {
    const now = new Date();

    if (this.format === "DHM") {
      // Day-Hour-Minute format (UTC)
      // Find the most recent occurrence of this day
      const currentYear = this.zulu ? now.getUTCFullYear() : now.getFullYear();
      const currentMonth = this.zulu ? now.getUTCMonth() : now.getMonth();

      let date: Date;
      if (this.zulu) {
        date = new Date(Date.UTC(currentYear, currentMonth, this.day!, this.hours, this.minutes, 0, 0));
      } else {
        date = new Date(currentYear, currentMonth, this.day!, this.hours, this.minutes, 0, 0);
      }

      // If the date is in the future, it's from last month
      if (date > now) {
        if (this.zulu) {
          date = new Date(Date.UTC(currentYear, currentMonth - 1, this.day!, this.hours, this.minutes, 0, 0));
        } else {
          date = new Date(currentYear, currentMonth - 1, this.day!, this.hours, this.minutes, 0, 0);
        }
      }

      return date;
    } else if (this.format === "HMS") {
      // Hour-Minute-Second format (UTC)
      // Use current date
      if (this.zulu) {
        const date = new Date();
        date.setUTCHours(this.hours, this.minutes, this.seconds || 0, 0);

        // If time is in the future, it's from yesterday
        if (date > now) {
          date.setUTCDate(date.getUTCDate() - 1);
        }

        return date;
      } else {
        const date = new Date();
        date.setHours(this.hours, this.minutes, this.seconds || 0, 0);

        if (date > now) {
          date.setDate(date.getDate() - 1);
        }

        return date;
      }
    } else {
      // MDHM format: Month-Day-Hour-Minute (local time)
      const currentYear = now.getFullYear();
      let date = new Date(currentYear, (this.month || 1) - 1, this.day!, this.hours, this.minutes, 0, 0);

      // If date is in the future, it's from last year
      if (date > now) {
        date = new Date(currentYear - 1, (this.month || 1) - 1, this.day!, this.hours, this.minutes, 0, 0);
      }

      return date;
    }
  }

  static fromString(
    str: string,
    withStructure: boolean = false
  ): {
    timestamp: Timestamp | undefined;
    segment?: Segment;
  } {
    if (str.length !== 7) return { timestamp: undefined };

    const timeType = str.charAt(6);

    if (timeType === "z") {
      // DHM format: Day-Hour-Minute (UTC)
      const timestamp = new Timestamp(parseInt(str.substring(2, 4), 10), parseInt(str.substring(4, 6), 10), "DHM", {
        day: parseInt(str.substring(0, 2), 10),
        zulu: true
      });

      const segment = withStructure
        ? {
            name: "timestamp",
            data: new TextEncoder().encode(str).buffer,
            isString: true,
            fields: [
              { type: FieldType.STRING, name: "day (DD)", length: 2 },
              { type: FieldType.STRING, name: "hour (HH)", length: 2 },
              { type: FieldType.STRING, name: "minute (MM)", length: 2 },
              { type: FieldType.CHAR, name: "timezone indicator", length: 1 }
            ]
          }
        : undefined;

      return { timestamp, segment };
    } else if (timeType === "h") {
      // HMS format: Hour-Minute-Second (UTC)
      const timestamp = new Timestamp(parseInt(str.substring(0, 2), 10), parseInt(str.substring(2, 4), 10), "HMS", {
        seconds: parseInt(str.substring(4, 6), 10),
        zulu: true
      });

      const segment = withStructure
        ? {
            name: "timestamp",
            data: new TextEncoder().encode(str).buffer,
            isString: true,
            fields: [
              { type: FieldType.STRING, name: "hour (HH)", length: 2 },
              { type: FieldType.STRING, name: "minute (MM)", length: 2 },
              { type: FieldType.STRING, name: "second (SS)", length: 2 },
              { type: FieldType.CHAR, name: "timezone indicator", length: 1 }
            ]
          }
        : undefined;

      return { timestamp, segment };
    } else if (timeType === "/") {
      // MDHM format: Month-Day-Hour-Minute (local)
      const timestamp = new Timestamp(parseInt(str.substring(4, 6), 10), parseInt(str.substring(6, 8), 10), "MDHM", {
        month: parseInt(str.substring(0, 2), 10),
        day: parseInt(str.substring(2, 4), 10),
        zulu: false
      });

      const segment = withStructure
        ? {
            name: "timestamp",
            data: new TextEncoder().encode(str).buffer,
            isString: true,
            fields: [
              { type: FieldType.STRING, name: "month (MM)", length: 2 },
              { type: FieldType.STRING, name: "day (DD)", length: 2 },
              { type: FieldType.STRING, name: "hour (HH)", length: 2 },
              { type: FieldType.STRING, name: "minute (MM)", length: 2 },
              { type: FieldType.CHAR, name: "timezone indicator", length: 1 }
            ]
          }
        : undefined;

      return { timestamp, segment };
    }

    return { timestamp: undefined };
  }
}

export default Timestamp;
