/// <reference types="dayjs/plugin/timezone.d.ts" />
/// <reference types="dayjs/plugin/utc.d.ts" />

import dayjs, { type ConfigType } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import { isFloatString } from '@lazy-num/parse-number-string';

dayjs.extend(utc);
dayjs.extend(timezone);

/**
 * 偵測結尾是否為不安全的時區偏移日期字串
 * Detect if the string ends with an unsafe timezone offset date string
 *
 * 偵測結尾是否為 `.000Z`、`Z`、`+00:00` 等格式
 * Detect if the string ends with `.000Z`, `Z`, `+00:00`, etc.
 *
 * @param {string} date - 日期字串 / Date string
 * @returns {boolean} 是否為不安全的偏移日期字串 / Whether it is an unsafe offset date string
 */
export function _isUnsafeOffsetDateString(date: string)
{
	return /(?:\dZ|\+\d{2}:?\d{2})$/.test(date)
}

/**
 * 偵測是否為 UTC 或 GMT 日期字串
 * Detect if it is a UTC or GMT date string
 *
 * @param {string} date - 日期字串 / Date string
 * @returns {boolean} 是否為 UTC/GMT 日期字串 / Whether it is a UTC/GMT date string
 */
export function _isUnsafeUTCDateString(date: string)
{
	return /(?:GMT|UTC)/.test(date)
}

/**
 * 時區類型 - 支援 IANA 時區資料庫中的時區名稱
 * Timezone type - supports timezone names from IANA database
 *
 * Day.js 透過 Internationalization API 支援時區，無需在程式碼中包含額外的時區資料。
 * Day.js supports timezone via the Internationalization API, no extra timezone data needed in code bundle.
 *
 * @see https://day.js.org/docs/en/timezone/timezone
 * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
 */
export type ITimezone = string | 'GMT' | 'Asia/Taipei' | 'Asia/Tokyo' | 'America/New_York';

/**
 * tzDayjsSafeParse 選項介面
 * tzDayjsSafeParse options interface
 */
export interface IOptionsTzDayjsSafeParse
{
	/**
	 * 時區設定
	 * Timezone setting
	 *
	 * Day.js 透過 Internationalization API 支援時區，無需在程式碼中包含額外的時區資料。
	 * Day.js supports timezone via the Internationalization API, no extra timezone data needed in code bundle.
	 *
	 * @see https://day.js.org/docs/en/timezone/timezone
	 * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
	 */
	timezone?: ITimezone,
	/**
	 * 最小有效時間戳
	 * Minimum valid timestamp
	 *
	 * 若設為 `true` 或 `0`，代表 `1970-01-01T00:00:00.000Z`
	 * If set to `true` or `0`, it represents `1970-01-01T00:00:00.000Z`
	 *
	 * 僅在輸入為數字時生效
	 * Only works when the input is a number
	 */
	minValidTimestamp?: number | boolean,
	/**
	 * 是否將輸入時間戳視為 Unix 時間戳（秒）
	 * Whether to use input timestamp as Unix Timestamp (seconds)
	 *
	 * 相當於 `dayjs.unix(1318781876.721)` 或 `dayjs(timestamp * 1000)`
	 * Equivalent to `dayjs.unix(1318781876.721)` or `dayjs(timestamp * 1000)`
	 *
	 * 僅在輸入為數字時生效
	 * Only works when the input is a number
	 *
	 * @see https://day.js.org/docs/en/parse/unix-timestamp
	 */
	isUnixTimestampSeconds?: boolean,
}

/**
 * 安全解析時區日期字串
 * Safely parse timezone date string
 *
 * 處理多種邊界情況，包括 UTC 偏移、浮點數字串、Unix 時間戳等
 * Handle various edge cases including UTC offset, float string, Unix timestamp, etc.
 *
 * @example
 * tzDayjsSafeParse('2023-03-31T04:00:00.000Z')
 * tzDayjsSafeParse('2023-03-31T12:00:00+08:00')
 * tzDayjsSafeParse('2023-03-31T04:00:00.800Z')
 * tzDayjsSafeParse('2023-03-31T04:00:00+00:00')
 * // => 2023-03-31T04:00:00Z
 * // => 1680235200
 * @example
 * tzDayjsSafeParse('Fri, 31 Mar 2023 04:00:00', 'GMT')
 *
 * @param {ConfigType} [dateOrMilliseconds] - 日期或毫秒時間戳 / Date or millisecond timestamp
 * @param {ITimezone | IOptionsTzDayjsSafeParse} [timezoneOrOptions] - 時區設定 / Timezone setting
 * @returns {dayjs.Dayjs} 解析後的 dayjs 物件 / Parsed dayjs object
 *
 * @see https://github.com/iamkun/dayjs/issues/2300
 * @see https://github.com/iamkun/dayjs/issues/2303
 * @see https://day.js.org/docs/en/timezone/timezone
 * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
 */
export function tzDayjsSafeParse(dateOrMilliseconds?: ConfigType, timezoneOrOptions?: ITimezone | IOptionsTzDayjsSafeParse)
{
	if (timezoneOrOptions === null || typeof timezoneOrOptions !== 'object')
	{
		timezoneOrOptions = {
			timezone: timezoneOrOptions,
		} as IOptionsTzDayjsSafeParse
	}

	if (typeof dateOrMilliseconds === 'number' || isFloatString(dateOrMilliseconds))
	{
		dateOrMilliseconds = Number(dateOrMilliseconds);

		if (typeof timezoneOrOptions.minValidTimestamp === 'number')
		{
			dateOrMilliseconds = Math.max(dateOrMilliseconds, timezoneOrOptions.minValidTimestamp);
		}
		else if (timezoneOrOptions.minValidTimestamp)
		{
			dateOrMilliseconds = Math.max(dateOrMilliseconds, 0);
		}

		dateOrMilliseconds = (timezoneOrOptions.isUnixTimestampSeconds ? dayjs.unix : dayjs)(dateOrMilliseconds);
	}
	else if (typeof dateOrMilliseconds === 'string')
	{
		if (_isUnsafeOffsetDateString(dateOrMilliseconds))
		{
			/**
			 * 修復結尾為 `.000Z`、`Z`、`+00:00` 時的解析錯誤
			 * Fix parsing error when string ends with `.000Z`, `Z`, or `+00:00`
			 */
			dateOrMilliseconds = dayjs.utc(dateOrMilliseconds)
		}
		else if (timezoneOrOptions.timezone === 'GMT' && !_isUnsafeUTCDateString(dateOrMilliseconds))
		{
			dateOrMilliseconds += ' GMT';
		}
		else
		{
			dateOrMilliseconds = dayjs(dateOrMilliseconds);
		}
	}

	return dayjs.tz(dateOrMilliseconds ?? void 0, timezoneOrOptions.timezone ?? void 0)
}

/**
 * 秒轉毫秒
 * Convert seconds to milliseconds
 *
 * @param {number} timestamp - Unix 時間戳（秒）/ Unix timestamp (seconds)
 * @returns {number} 毫秒時間戳 / Millisecond timestamp
 */
export function secondsToMilliseconds(timestamp: number)
{
	return timestamp * 1000
}

/**
 * 毫秒轉秒
 * Convert milliseconds to seconds
 *
 * @param {number} timestamp - 毫秒時間戳 / Millisecond timestamp
 * @returns {number} Unix 時間戳（秒）/ Unix timestamp (seconds)
 */
export function millisecondsToSeconds(timestamp: number)
{
	return timestamp / 1000
}

export default tzDayjsSafeParse

// @ts-ignore
if (process.env.TSDX_FORMAT !== 'esm')
{
	Object.defineProperty(tzDayjsSafeParse, "__esModule", { value: true });

	Object.defineProperty(tzDayjsSafeParse, 'tzDayjsSafeParse', { value: tzDayjsSafeParse });
	Object.defineProperty(tzDayjsSafeParse, 'default', { value: tzDayjsSafeParse });

	Object.defineProperty(tzDayjsSafeParse, '_isUnsafeOffsetDateString', { value: _isUnsafeOffsetDateString });
	Object.defineProperty(tzDayjsSafeParse, '_isUnsafeUTCDateString', { value: _isUnsafeUTCDateString });

	Object.defineProperty(tzDayjsSafeParse, 'secondsToMilliseconds', { value: secondsToMilliseconds });
	Object.defineProperty(tzDayjsSafeParse, 'millisecondsToSeconds', { value: millisecondsToSeconds });

}
