/**
 * Date and time utilities with Spanish locale support
 */

import { format, formatDistance, formatRelative, parseISO, isValid, addDays, subDays, startOfDay, endOfDay, startOfWeek, endOfWeek, startOfMonth, endOfMonth, differenceInDays, differenceInHours, differenceInMinutes } from 'date-fns';
import { es } from 'date-fns/locale';

// Default locale for Spanish
const defaultLocale = es;

// Format date for display
export const formatDate = (
  date: Date | string,
  formatStr: string = 'dd/MM/yyyy',
  locale = defaultLocale
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    return format(dateObj, formatStr, { locale });
  } catch {
    return '';
  }
};

// Format date and time for display
export const formatDateTime = (
  date: Date | string,
  formatStr: string = 'dd/MM/yyyy HH:mm',
  locale = defaultLocale
): string => {
  return formatDate(date, formatStr, locale);
};

// Format time only
export const formatTime = (
  date: Date | string,
  formatStr: string = 'HH:mm',
  locale = defaultLocale
): string => {
  return formatDate(date, formatStr, locale);
};

// Format date for API (ISO string)
export const formatDateForAPI = (date: Date | string): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    return dateObj.toISOString().split('T')[0];
  } catch {
    return '';
  }
};

// Format datetime for API (full ISO string)
export const formatDateTimeForAPI = (date: Date | string): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    return dateObj.toISOString();
  } catch {
    return '';
  }
};

// Relative time formatting ("hace 2 horas")
export const formatRelativeTime = (
  date: Date | string,
  baseDate: Date = new Date(),
  locale = defaultLocale
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    return formatDistance(dateObj, baseDate, { addSuffix: true, locale });
  } catch {
    return '';
  }
};

// Contextual relative formatting ("mañana a las 14:00")
export const formatRelativeDate = (
  date: Date | string,
  baseDate: Date = new Date(),
  locale = defaultLocale
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    return formatRelative(dateObj, baseDate, { locale });
  } catch {
    return '';
  }
};

// Get start of day
export const getStartOfDay = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return startOfDay(dateObj);
};

// Get end of day
export const getEndOfDay = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return endOfDay(dateObj);
};

// Get start of week (Monday)
export const getStartOfWeek = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return startOfWeek(dateObj, { weekStartsOn: 1, locale: defaultLocale });
};

// Get end of week (Sunday)
export const getEndOfWeek = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return endOfWeek(dateObj, { weekStartsOn: 1, locale: defaultLocale });
};

// Get start of month
export const getStartOfMonth = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return startOfMonth(dateObj);
};

// Get end of month
export const getEndOfMonth = (date: Date | string = new Date()): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return endOfMonth(dateObj);
};

// Add days to date
export const addDaysToDate = (date: Date | string, days: number): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return addDays(dateObj, days);
};

// Subtract days from date
export const subtractDaysFromDate = (date: Date | string, days: number): Date => {
  const dateObj = typeof date === 'string' ? parseISO(date) : date;
  return subDays(dateObj, days);
};

// Calculate age from birth date
export const calculateAge = (birthDate: Date | string): number => {
  try {
    const birth = typeof birthDate === 'string' ? parseISO(birthDate) : birthDate;
    const today = new Date();
    
    if (!isValid(birth)) {
      return 0;
    }

    let age = today.getFullYear() - birth.getFullYear();
    const monthDifference = today.getMonth() - birth.getMonth();
    
    if (monthDifference < 0 || (monthDifference === 0 && today.getDate() < birth.getDate())) {
      age--;
    }
    
    return age;
  } catch {
    return 0;
  }
};

// Check if date is today
export const isToday = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    const today = new Date();
    
    if (!isValid(dateObj)) {
      return false;
    }

    return formatDate(dateObj, 'yyyy-MM-dd') === formatDate(today, 'yyyy-MM-dd');
  } catch {
    return false;
  }
};

// Check if date is yesterday
export const isYesterday = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    const yesterday = subDays(new Date(), 1);
    
    if (!isValid(dateObj)) {
      return false;
    }

    return formatDate(dateObj, 'yyyy-MM-dd') === formatDate(yesterday, 'yyyy-MM-dd');
  } catch {
    return false;
  }
};

// Check if date is tomorrow
export const isTomorrow = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    const tomorrow = addDays(new Date(), 1);
    
    if (!isValid(dateObj)) {
      return false;
    }

    return formatDate(dateObj, 'yyyy-MM-dd') === formatDate(tomorrow, 'yyyy-MM-dd');
  } catch {
    return false;
  }
};

// Check if date is in the past
export const isPastDate = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return false;
    }

    return dateObj < new Date();
  } catch {
    return false;
  }
};

// Check if date is in the future
export const isFutureDate = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return false;
    }

    return dateObj > new Date();
  } catch {
    return false;
  }
};

// Get difference in days
export const getDaysAgo = (date: Date | string): number => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return 0;
    }

    return differenceInDays(new Date(), dateObj);
  } catch {
    return 0;
  }
};

// Get difference in hours
export const getHoursAgo = (date: Date | string): number => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return 0;
    }

    return differenceInHours(new Date(), dateObj);
  } catch {
    return 0;
  }
};

// Get difference in minutes
export const getMinutesAgo = (date: Date | string): number => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return 0;
    }

    return differenceInMinutes(new Date(), dateObj);
  } catch {
    return 0;
  }
};

// Get day of week name
export const getDayOfWeekName = (
  date: Date | string,
  format: 'long' | 'short' | 'narrow' = 'long',
  locale = defaultLocale
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    const formatMap = {
      long: 'EEEE',
      short: 'EEE',
      narrow: 'EEEEE',
    };

    return formatDate(dateObj, formatMap[format], locale);
  } catch {
    return '';
  }
};

// Get month name
export const getMonthName = (
  date: Date | string,
  format: 'long' | 'short' | 'narrow' = 'long',
  locale = defaultLocale
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return '';
    }

    const formatMap = {
      long: 'MMMM',
      short: 'MMM',
      narrow: 'MMMMM',
    };

    return formatDate(dateObj, formatMap[format], locale);
  } catch {
    return '';
  }
};

// Get business hours for a date
export const getBusinessHours = (date: Date | string = new Date()) => {
  const dayOfWeek = getDayOfWeek(date);
  
  // Default business hours (can be customized)
  const businessHours = {
    monday: { open: '06:00', close: '20:00' },
    tuesday: { open: '06:00', close: '20:00' },
    wednesday: { open: '06:00', close: '20:00' },
    thursday: { open: '06:00', close: '20:00' },
    friday: { open: '06:00', close: '20:00' },
    saturday: { open: '07:00', close: '21:00' },
    sunday: { open: '08:00', close: '15:00' },
  };

  return businessHours[dayOfWeek as keyof typeof businessHours];
};

// Get day of week (0 = Sunday, 1 = Monday, etc.)
export const getDayOfWeek = (date: Date | string = new Date()): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return 'monday';
    }

    const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
    return dayNames[dateObj.getDay()];
  } catch {
    return 'monday';
  }
};

// Check if date is a weekend
export const isWeekend = (date: Date | string = new Date()): boolean => {
  const dayOfWeek = getDayOfWeek(date);
  return dayOfWeek === 'saturday' || dayOfWeek === 'sunday';
};

// Check if date is a weekday
export const isWeekday = (date: Date | string = new Date()): boolean => {
  return !isWeekend(date);
};

// Generate date range
export const generateDateRange = (
  startDate: Date | string,
  endDate: Date | string,
  step: number = 1
): Date[] => {
  try {
    const start = typeof startDate === 'string' ? parseISO(startDate) : startDate;
    const end = typeof endDate === 'string' ? parseISO(endDate) : endDate;
    
    if (!isValid(start) || !isValid(end) || start > end) {
      return [];
    }

    const dates: Date[] = [];
    let currentDate = new Date(start);
    
    while (currentDate <= end) {
      dates.push(new Date(currentDate));
      currentDate = addDays(currentDate, step);
    }
    
    return dates;
  } catch {
    return [];
  }
};

// Get date ranges for common periods
export const getDateRanges = () => {
  const today = new Date();
  
  return {
    today: {
      start: getStartOfDay(today),
      end: getEndOfDay(today),
    },
    yesterday: {
      start: getStartOfDay(subDays(today, 1)),
      end: getEndOfDay(subDays(today, 1)),
    },
    thisWeek: {
      start: getStartOfWeek(today),
      end: getEndOfWeek(today),
    },
    lastWeek: {
      start: getStartOfWeek(subDays(today, 7)),
      end: getEndOfWeek(subDays(today, 7)),
    },
    thisMonth: {
      start: getStartOfMonth(today),
      end: getEndOfMonth(today),
    },
    lastMonth: {
      start: getStartOfMonth(subDays(today, 30)),
      end: getEndOfMonth(subDays(today, 30)),
    },
    last7Days: {
      start: getStartOfDay(subDays(today, 6)),
      end: getEndOfDay(today),
    },
    last30Days: {
      start: getStartOfDay(subDays(today, 29)),
      end: getEndOfDay(today),
    },
    last90Days: {
      start: getStartOfDay(subDays(today, 89)),
      end: getEndOfDay(today),
    },
  };
};

// Parse date string safely
export const parseDate = (dateString: string): Date | null => {
  try {
    if (!dateString) return null;
    
    const parsed = parseISO(dateString);
    return isValid(parsed) ? parsed : null;
  } catch {
    return null;
  }
};

// Format duration in human readable format
export const formatDurationFromDates = (
  startDate: Date | string,
  endDate: Date | string
): string => {
  try {
    const start = typeof startDate === 'string' ? parseISO(startDate) : startDate;
    const end = typeof endDate === 'string' ? parseISO(endDate) : endDate;
    
    if (!isValid(start) || !isValid(end)) {
      return '';
    }

    return formatDistance(start, end, { locale: defaultLocale });
  } catch {
    return '';
  }
};

// Get season from date
export const getSeason = (date: Date | string = new Date()): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return 'spring';
    }

    const month = dateObj.getMonth();
    
    if (month >= 2 && month <= 4) return 'spring'; // Mar, Apr, May
    if (month >= 5 && month <= 7) return 'summer'; // Jun, Jul, Aug
    if (month >= 8 && month <= 10) return 'fall'; // Sep, Oct, Nov
    return 'winter'; // Dec, Jan, Feb
  } catch {
    return 'spring';
  }
};

// Spanish holidays (basic implementation)
export const isSpanishHoliday = (date: Date | string): boolean => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;
    
    if (!isValid(dateObj)) {
      return false;
    }

    const month = dateObj.getMonth() + 1; // 1-based month
    const day = dateObj.getDate();
    
    // Fixed holidays
    const fixedHolidays = [
      { month: 1, day: 1 },   // New Year
      { month: 1, day: 6 },   // Epiphany
      { month: 5, day: 1 },   // Labor Day
      { month: 8, day: 15 },  // Assumption
      { month: 10, day: 12 }, // National Day
      { month: 11, day: 1 },  // All Saints
      { month: 12, day: 6 },  // Constitution Day
      { month: 12, day: 8 },  // Immaculate Conception
      { month: 12, day: 25 }, // Christmas
    ];
    
    return fixedHolidays.some(holiday => holiday.month === month && holiday.day === day);
  } catch {
    return false;
  }
};

// Time zone utilities
export const formatDateInTimezone = (
  date: Date | string,
  timezone: string = 'Europe/Madrid',
  formatStr: string = 'dd/MM/yyyy HH:mm'
): string => {
  try {
    const dateObj = typeof date === 'string' ? parseISO(date) : date;

    if (!isValid(dateObj)) {
      return '';
    }

    return new Intl.DateTimeFormat('es-ES', {
      timeZone: timezone,
      day: '2-digit',
      month: '2-digit',
      year: 'numeric',
      hour: '2-digit',
      minute: '2-digit',
    }).format(dateObj);
  } catch {
    return formatDate(date, formatStr);
  }
};

// Convert HTML date input (YYYY-MM-DD) to end-of-day datetime for API
export const formatExpirationDateForAPI = (dateString: string): string | undefined => {
  try {
    if (!dateString) return undefined;

    // Parse the date string (YYYY-MM-DD format from HTML date input)
    const dateObj = parseISO(dateString);

    if (!isValid(dateObj)) {
      return undefined;
    }

    // Set to end of day for expiration dates and return ISO string
    return getEndOfDay(dateObj).toISOString();
  } catch {
    return undefined;
  }
};