#[Copyright 2025 ITwrx.
This file is part of Forget-Me-Not.
Forget-Me-Not is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#

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)
  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))
  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(now()), weekdayString, parseInt(ocurrence))
    nextSendDate = nthWeekdayInMonth(year(now()), month(now()), weekdayString, parseInt(ocurrence))
    if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
      nextSendDate = nthWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString, parseInt(ocurrence))
  else:
    #nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(now()), weekdayString)
    nextSendDate = lastWeekdayInMonth(year(now()), month(now()), weekdayString)
    if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
      nextSendDate = lastWeekdayInMonth(year(nextMonthsDate), month(nextMonthsDate), weekdayString)
  return nextSendDate

proc nextYearlyOnWeekdayOfWeekOfMonth*(weekdayString, ocurrence, monthString: string): DateTime =
  var nextSendDate, startingDate, nextYearsDate: DateTime
  nextYearsDate = now() + 1.years
  nextYearsDate = dateTime(year(nextYearsDate), monthFromString(monthString), 01)
  let weekNumStrings = @["1", "2", "3"]
  if ocurrence in weekNumStrings:
    nextSendDate = nthWeekdayInMonth(year(now()), month(now()), weekdayString, parseInt(ocurrence))
    if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
      nextSendDate = nthWeekdayInMonth(year(nextYearsDate), month(nextYearsDate), weekdayString, parseInt(ocurrence))
  else:
    nextSendDate = lastWeekdayInMonth(year(now()), month(now()), weekdayString)
    if format(nextSendDate, "yyyy-MM-dd") <= format(now(), "yyyy-MM-dd"):
      nextSendDate = lastWeekdayInMonth(year(nextYearsDate), month(nextYearsDate), weekdayString)  
  return nextSendDate

#[ 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]#
proc nextYearlyDate*(monthString: string, targetDay: int): DateTime =  
  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")
  nextSendDate = parse(nextSendDateString, "yyyy-MM-dd")  
  return nextSendDate