import jalaliPlugin from '@zoomit/dayjs-jalali-plugin';
import dayjs, { Dayjs } from 'dayjs';
import 'dayjs/locale/fa';
import relativeTime from 'dayjs/plugin/relativeTime';

// Extend dayjs with plugins
dayjs.extend(jalaliPlugin);
dayjs.extend(relativeTime);

/**
 * Common date format patterns for Jalali calendar
 */
export const JALALI_DATE_FORMATS = {
  /** Full date with weekday: "پنج‌شنبه، ۱۳ شهریور ۱۳۹۷" */
  FULL_WITH_WEEKDAY: 'dddd، DD MMMM YYYY',
  /** Full date: "۱۳ شهریور ۱۳۹۷" */
  FULL: 'DD MMMM YYYY',
  /** Short date with month name: "۱۳ شهریور ۹۷" */
  SHORT: 'DD MMMM YY',
  /** Numeric date: "۱۳۹۷/۰۶/۱۳" */
  NUMERIC: 'YYYY/MM/DD',
  /** Numeric date with time: "۱۳۹۷/۰۶/۱۳ ۱۶:۳۰" */
  NUMERIC_WITH_TIME: 'YYYY/MM/DD - HH:mm',
  /** Numeric date with full time: "۱۳۹۷/۰۶/۱۳ ۱۶:۳۰:۱۵" */
  NUMERIC_WITH_FULL_TIME: 'YYYY/MM/DD - HH:mm:ss',
  /** Time only: "۱۶:۳۰" */
  TIME: 'HH:mm',
  /** Time with seconds: "۱۶:۳۰:۱۵" */
  TIME_WITH_SECONDS: 'HH:mm:ss',
  /** Month and year: "شهریور ۱۳۹۷" */
  MONTH_YEAR: 'MMMM YYYY',
  /** Day and month: "۱۳ شهریور" */
  DAY_MONTH: 'DD MMMM',
} as const;

/**
 * Common date format patterns for Gregorian calendar
 */
export const GREGORIAN_DATE_FORMATS = {
  /** Full date with weekday: "Thursday, September 04 2018" */
  FULL_WITH_WEEKDAY: 'dddd, MMMM DD YYYY',
  /** Full date: "September 04 2018" */
  FULL: 'MMMM DD YYYY',
  /** Short date: "Sep 04 18" */
  SHORT: 'MMM DD YY',
  /** Numeric date: "2018-09-04" */
  NUMERIC: 'YYYY-MM-DD',
  /** Numeric date with time: "2018-09-04 16:30" */
  NUMERIC_WITH_TIME: 'YYYY-MM-DD HH:mm',
  /** Numeric date with full time: "2018-09-04 16:30:15" */
  NUMERIC_WITH_FULL_TIME: 'YYYY-MM-DD HH:mm:ss',
  /** Time only: "16:30" */
  TIME: 'HH:mm',
  /** Time with seconds: "16:30:15" */
  TIME_WITH_SECONDS: 'HH:mm:ss',
  /** Month and year: "September 2018" */
  MONTH_YEAR: 'MMMM YYYY',
  /** Day and month: "September 04" */
  DAY_MONTH: 'MMMM DD',
} as const;

/**
 * Format a date as Jalali (Persian) calendar in Farsi locale
 *
 * @param date - Date to format (string, Date, Dayjs, or timestamp)
 * @param format - Format pattern (default: 'YYYY/MM/DD')
 * @returns Formatted date string in Jalali calendar
 *
 * @example
 * formatJalali('2018-09-04') // '1397/06/13'
 * formatJalali('2018-09-04', JALALI_DATE_FORMATS.FULL) // '۱۳ شهریور ۱۳۹۷'
 * formatJalali(new Date(), JALALI_DATE_FORMATS.FULL_WITH_WEEKDAY) // 'پنج‌شنبه، ۱۳ شهریور ۱۳۹۷'
 */
export function formatJalali(
  date: string | Date | Dayjs | number,
  format: string = JALALI_DATE_FORMATS.NUMERIC,
): string {
  return dayjs(date).calendar('jalali').locale('fa').format(format);
}

/**
 * Format a date as Gregorian calendar in English locale
 *
 * @param date - Date to format (string, Date, Dayjs, or timestamp)
 * @param format - Format pattern (default: 'YYYY-MM-DD')
 * @returns Formatted date string in Gregorian calendar
 *
 * @example
 * formatGregorian('1397/06/13', GREGORIAN_DATE_FORMATS.FULL) // 'September 04 2018'
 * formatGregorian(new Date(), GREGORIAN_DATE_FORMATS.NUMERIC_WITH_TIME) // '2018-09-04 16:30'
 */
export function formatGregorian(
  date: string | Date | Dayjs | number,
  format: string = GREGORIAN_DATE_FORMATS.NUMERIC,
): string {
  return dayjs(date).calendar('gregory').locale('en').format(format);
}

/**
 * Parse a Jalali date string and return a Dayjs instance
 *
 * @param jalaliDate - Jalali date string (e.g., '1398-10-17')
 * @param format - Optional format pattern for parsing
 * @returns Dayjs instance
 *
 * @example
 * parseJalali('1398-10-17') // Dayjs instance
 * parseJalali('1398/10/17') // Dayjs instance
 */
export function parseJalali(jalaliDate: string, format?: string): Dayjs {
  return dayjs(jalaliDate, { jalali: true, ...(format && { format }) });
}

/**
 * Convert a Gregorian date to Jalali format
 *
 * @param gregorianDate - Gregorian date string, Date object, or Dayjs instance
 * @param format - Optional format pattern (default: 'YYYY/MM/DD')
 * @returns Formatted Jalali date string
 *
 * @example
 * gregorianToJalali('2018-09-04') // '1397/06/13'
 * gregorianToJalali('2018-09-04', JALALI_DATE_FORMATS.FULL) // '۱۳ شهریور ۱۳۹۷'
 */
export function gregorianToJalali(
  gregorianDate: string | Date | Dayjs,
  format: string = JALALI_DATE_FORMATS.NUMERIC,
): string {
  return dayjs(gregorianDate).calendar('jalali').locale('fa').format(format);
}

/**
 * Convert a Jalali date to Gregorian format
 *
 * @param jalaliDate - Jalali date string (e.g., '1398-10-17')
 * @param format - Optional format pattern (default: 'YYYY-MM-DD')
 * @returns Formatted Gregorian date string
 *
 * @example
 * jalaliToGregorian('1398-10-17') // '2020-01-07'
 * jalaliToGregorian('1397/06/13', GREGORIAN_DATE_FORMATS.FULL) // 'September 04 2018'
 */
export function jalaliToGregorian(
  jalaliDate: string,
  format: string = GREGORIAN_DATE_FORMATS.NUMERIC,
): string {
  return parseJalali(jalaliDate).calendar('gregory').locale('en').format(format);
}

/**
 * Get current date and time in Jalali format
 *
 * @param format - Optional format pattern (default: 'YYYY/MM/DD HH:mm')
 * @returns Current date and time in Jalali format
 *
 * @example
 * nowJalali() // '1403/12/05 14:30'
 * nowJalali(JALALI_DATE_FORMATS.FULL_WITH_WEEKDAY) // 'یکشنبه، ۵ اسفند ۱۴۰۳'
 */
export function nowJalali(format: string = JALALI_DATE_FORMATS.NUMERIC_WITH_TIME): string {
  return dayjs().calendar('jalali').locale('fa').format(format);
}

/**
 * Get current date and time in Gregorian format
 *
 * @param format - Optional format pattern (default: 'YYYY-MM-DD HH:mm')
 * @returns Current date and time in Gregorian format
 *
 * @example
 * nowGregorian() // '2026-02-24 14:30'
 * nowGregorian(GREGORIAN_DATE_FORMATS.FULL_WITH_WEEKDAY) // 'Sunday, February 24 2026'
 */
export function nowGregorian(format: string = GREGORIAN_DATE_FORMATS.NUMERIC_WITH_TIME): string {
  return dayjs().calendar('gregory').locale('en').format(format);
}

/**
 * Format a time duration as a relative time string in Farsi
 *
 * @param date - Date to compare against current time
 * @returns Relative time string in Farsi
 *
 * @example
 * relativeTimeJalali(dayjs().subtract(2, 'day')) // '۲ روز پیش'
 */
export function relativeTimeJalali(date: string | Date | Dayjs | number): string {
  return dayjs(date).locale('fa').fromNow();
}

/**
 * Check if a date string is a valid Jalali date
 *
 * @param jalaliDate - Jalali date string
 * @returns true if valid, false otherwise
 *
 * @example
 * isValidJalaliDate('1398-10-17') // true
 * isValidJalaliDate('1398-13-17') // false
 */
export function isValidJalaliDate(jalaliDate: string): boolean {
  return parseJalali(jalaliDate).isValid();
}

/**
 * Get the start of day for a date in Jalali calendar
 *
 * @param date - Date to get start of day for
 * @returns Dayjs instance at start of day
 *
 * @example
 * startOfDayJalali('1398-10-17') // Dayjs instance at 00:00:00
 */
export function startOfDayJalali(date: string | Date | Dayjs | number): Dayjs {
  return dayjs(date).calendar('jalali').startOf('day');
}

/**
 * Get the end of day for a date in Jalali calendar
 *
 * @param date - Date to get end of day for
 * @returns Dayjs instance at end of day
 *
 * @example
 * endOfDayJalali('1398-10-17') // Dayjs instance at 23:59:59
 */
export function endOfDayJalali(date: string | Date | Dayjs | number): Dayjs {
  return dayjs(date).calendar('jalali').endOf('day');
}

/**
 * Format a date with custom calendar and locale
 *
 * @param date - Date to format
 * @param calendar - Calendar type ('jalali' | 'gregory')
 * @param locale - Locale ('fa' | 'en')
 * @param format - Format pattern
 * @returns Formatted date string
 *
 * @example
 * formatDate('2018-09-04', 'jalali', 'fa', 'DD MMMM YYYY') // '۱۳ شهریور ۱۳۹۷'
 * formatDate('2018-09-04', 'gregory', 'en', 'MMMM DD YYYY') // 'September 04 2018'
 */
export function formatDate(
  date: string | Date | Dayjs | number,
  calendar: 'jalali' | 'gregory',
  locale: 'fa' | 'en',
  format: string,
): string {
  return dayjs(date).calendar(calendar).locale(locale).format(format);
}

/**
 * Get Jalali month name in Farsi
 *
 * @param monthNumber - Month number (1-12)
 * @returns Month name in Farsi
 *
 * @example
 * getJalaliMonthName(1) // 'فروردین'
 * getJalaliMonthName(6) // 'شهریور'
 */
export function getJalaliMonthName(monthNumber: number): string {
  const monthNames = [
    'فروردین',
    'اردیبهشت',
    'خرداد',
    'تیر',
    'مرداد',
    'شهریور',
    'مهر',
    'آبان',
    'آذر',
    'دی',
    'بهمن',
    'اسفند',
  ];
  return monthNames[monthNumber - 1] || '';
}

/**
 * Get Jalali weekday name in Farsi
 *
 * @param dayNumber - Day number (0-6, where 0 is Saturday)
 * @returns Weekday name in Farsi
 *
 * @example
 * getJalaliWeekdayName(0) // 'شنبه'
 * getJalaliWeekdayName(4) // 'چهارشنبه'
 */
export function getJalaliWeekdayName(dayNumber: number): string {
  const weekdayNames = ['شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنج‌شنبه', 'جمعه'];
  return weekdayNames[dayNumber] || '';
}

export function jalaliDiff(
  date1: string | number | Date | Dayjs,
  date2: string | number | Date | Dayjs,
  unit: dayjs.OpUnitType = 'day',
): number {
  return toJalali(date1).diff(toJalali(date2), unit);
}

export function isJalaliBefore(
  date1: string | number | Date | Dayjs,
  date2: string | number | Date | Dayjs,
): boolean {
  return toJalali(date1).isBefore(toJalali(date2));
}

export function isJalaliAfter(
  date1: string | number | Date | Dayjs,
  date2: string | number | Date | Dayjs,
): boolean {
  return toJalali(date1).isAfter(toJalali(date2));
}

export function toJalali(date: string | number | Date | Dayjs, format = 'YYYY/MM/DD'): Dayjs {
  return dayjs(date).calendar('jalali');
}

export default {
  formatJalali,
  formatGregorian,
  parseJalali,
  gregorianToJalali,
  jalaliToGregorian,
  nowJalali,
  nowGregorian,
  relativeTimeJalali,
  isValidJalaliDate,
  startOfDayJalali,
  endOfDayJalali,
  formatDate,
  getJalaliMonthName,
  getJalaliWeekdayName,
  jalaliDiff,
  isJalaliBefore,
  isJalaliAfter,
  JALALI_DATE_FORMATS,
  GREGORIAN_DATE_FORMATS,
};
