import std/[times, strutils]

proc weekdayFromString(dayStr: string): Weekday =
  ## Convert a string representation of a weekday to Weekday enum
  case dayStr
    of "Monday": return dMon
    of "Tuesday": return dTue
    of "Wednesday": return dWed
    of "Thursday": return dThu
    of "Friday": return dFri
    of "Saturday": return dSat
    of "Sunday": return dSun

proc monthFromString(monthStr: string): Month =
  ## Convert a string representation of a weekday to Weekday enum
  case monthStr
    of "January": return mJan
    of "Febuary": return mFeb
    of "March": return mMar
    of "April": return mApr
    of "May": return mMay
    of "June": return mJun
    of "July": return mJul
    of "August": return mAug
    of "September": return mSep
    of "October": return mOct
    of "November": return mNov
    of "December": return mDec

proc nextWeekday*(targetWeekdayString: string): DateTime =
  ## Calculates the next occurrence of a specific weekday
  var nextDate = now()
  # We're not including current day, so we move to the next day
  nextDate = nextDate + 1.days
  #convert passed weekday string to nim Weekday.
  let targetWeekday = weekdayFromString(targetWeekdayString)
  # Find the next occurrence of the target weekday
  while nextDate.weekday != targetWeekday:
    nextDate = nextDate + 1.days
  return nextDate

proc nthWeekdayInMonth*(year: int, month: Month, weekdayString: string, nth: range[1..3]): DateTime =
  # Start from the first day of the month
  #var currentDate = dateTime(year, month, 1, 0, 0, 0, 0)
  var currentDate = dateTime(year, month, 1)
  let weekday = weekdayFromString(weekdayString)
  # Find the first occurrence of the specified weekday
  while currentDate.weekday != weekday:
    currentDate = currentDate + 1.days
  # Move to the nth occurrence
  currentDate = currentDate + days((nth - 1) * 7)
  return currentDate

proc lastWeekdayInMonth(year: int, month: Month, weekdayString: string): DateTime =
  # Create a DateTime for the last day of the given month
  #var lastDay = dateTime(year, month, getDaysInMonth(month, year), 0, 0, 0, 0)
  var lastDay = dateTime(year, month, getDaysInMonth(month, year))
  let weekday = weekdayFromString(weekdayString)
  # Work backwards until we find the last occurrence of the specified weekday
  while lastDay.weekday != weekday:
    lastDay = lastDay - 1.days
  return lastDay

proc nextMonthlyOnWeekdayOfWeek*(weekdayString: string, ocurrence: string): DateTime =
  var nextSendDate: DateTime
  let nextMonthsDate = now() + 1.months
  let weekNumStrings = @["1", "2", "3"]
  if ocurrence in weekNumStrings:
    nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString, parseInt(ocurrence))
  #when ocurrence == "last".
  else:
    nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString)
  #only use next month if that day has already occured this month, otherwise adjust it for this month instead.
  if monthDay(nextSendDate - 1.months) > monthDay(now()):
    if ocurrence in weekNumStrings:
      nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString, parseInt(ocurrence))    
    else:
      nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString)    
  return nextSendDate

proc nextYearlyOnWeekdayOfWeekOfMonth*(weekdayString, ocurrence, monthString: string): DateTime =
  var nextSendDate, startingDate: DateTime
  let nextYearsDate = now() + 1.years
  #let startingDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01, 0, 0, 0, 0)
  startingDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01)
  let weekNumStrings = @["1", "2", "3"]
  if ocurrence in weekNumStrings:
    nextSendDate = nthWeekdayInMonth(year(startingDate), month(startingDate), weekdayString, parseInt(ocurrence))
  #when ocurrence == "last".
  else:
    nextSendDate = lastWeekdayInMonth(year(startingDate), month(startingDate), weekdayString)
  #only use next year if that month and day has already occured this year, otherwise adjust it for this year instead.
  if nextSendDate - 1.years > now():
    startingDate = dateTime(year(now()), monthFromString(monthString), 01)
    let weekNumStrings = @["1", "2", "3"]
    if ocurrence in weekNumStrings:
      nextSendDate = nthWeekdayInMonth(year(startingDate), month(startingDate), weekdayString, parseInt(ocurrence))
    #when ocurrence == "last".
    else:
      nextSendDate = lastWeekdayInMonth(year(startingDate), month(startingDate), weekdayString)
  return nextSendDate

#[proc nextYearDate*(monthString: string, day: int): DateTime =
  ## Aalways returns a date in the next year,
  ## regardless of whether the target date has passed in the current year
  #var nextSendDate = dateTime(year(now()) + 1, monthFromString(monthString), day, 0, 0, 0, 0)
  #var nextSendDate = dateTime(year(now()) + 1, monthFromString(monthString), day)
  #let nextSendDateString =
  #echo nextSendDate
  #return parse($nextSendDate, "yyyy-MM-dd")
  var 
  let nextYear = year(now()) + 1
  let dt = dateTime(nextYear, monthFromString(monthString), day, 00, 00, 00, 00)
  #echo dt
  let nextSendDateString = format(dt, "yyyy-MM-dd")
  #echo nextSendDateString
  #let nextSendDateString = $nextYear & "-" & formattedMonthString & "-" & $day
  let nextSendDate = parse(nextSendDateString, "yyyy-MM-dd")
  #echo nextSendDateString
  return nextSendDate]#

proc nextYearlyDate*(monthString: string, targetDay: int): DateTime =
  ## Calculates a date for the next occurrence of a specific month and day
  ## 
  ## Parameters:
  ## - baseDate: The starting date to calculate from
  ## - targetMonth: The month (Month enum) for the target date
  ## - targetDay: The day of month for the target date
  ##
  ## Returns the next occurrence of the specified month and day, which could be:
  ## - Later this year if the target date hasn't occurred yet
  ## - Next year if the target date has already passed this year
  
  let baseDate = now()
  let targetMonth = monthFromString(monthString)
  
  # Get the current year
  let currentYear = baseDate.year
  
  # Create a DateTime for the target date in the current year
  var nextSendDate = dateTime(currentYear, targetMonth, targetDay, 00, 00, 00, 00)
  
  # If the target date has already passed this year, move to next year
  if nextSendDate <= baseDate:
    nextSendDate = dateTime(currentYear + 1, targetMonth, targetDay, 00, 00, 00, 00)
    
  let nextSendDateString = format(nextSendDate, "yyyy-MM-dd")
  #echo nextSendDateString
  #let nextSendDateString = $nextYear & "-" & formattedMonthString & "-" & $day
  nextSendDate = parse(nextSendDateString, "yyyy-MM-dd")
  
  return nextSendDate
