--- Ecclesiastical Easter and related holiday calculations.
-- Ported from "Calendrical Calculations" (4th edition)
-- by Nachum Dershowitz and Edward M. Reingold.
-- Original Lisp code (CALENDRICA 4.0) is Apache 2.0 licensed.
-- @module calendrica-ecclesiastical
-- @release 0.1 2026-07-19

local M = {}

local basic     = require("calendrica-basic")
local gregorian = require("calendrica-gregorian")
local julian    = require("calendrica-julian")


--- Fixed date of Orthodox Easter in Gregorian year `g_year`.
-- @tparam number g_year Gregorian year.
-- @treturn number Fixed date.
function M.orthodox_easter(g_year)
  -- Age of moon for April 5.
  local shifted_epact = (14 + 11 * (g_year % 19)) % 30
  -- Julian year number (no year zero).
  local j_year = g_year > 0 and g_year or g_year - 1
  -- Day after full moon on or after March 21.
  local paschal_moon = julian.fixed_from_julian(
    julian.julian_date(j_year, julian.APRIL, 19)
  ) - shifted_epact
  -- Return the Sunday following the Paschal moon.
  return gregorian.kday_after(basic.SUNDAY, paschal_moon)
end

-- Alternative Orthodox Easter formula (not exported).
local function alt_orthodox_easter(g_year) -- luacheck: ignore
  -- Day after full moon on or after March 21.
  local paschal_moon = 354 * g_year
    + 30 * basic.quotient(7 * g_year + 8, 19)
    + basic.quotient(g_year, 4)
    - basic.quotient(g_year, 19)
    - 273
    + gregorian.GREGORIAN_EPOCH
  -- Return the Sunday following the Paschal moon.
  return gregorian.kday_after(basic.SUNDAY, paschal_moon)
end

--- Fixed date of Easter in Gregorian year `g_year`.
-- @tparam number g_year Gregorian year.
-- @treturn number Fixed date.
function M.easter(g_year)
  local century = 1 + basic.quotient(g_year, 100)
  -- Age of moon for April 5, by Nicaean rule,
  -- corrected for the Gregorian century rule and
  -- Metonic cycle inaccuracy.
  local shifted_epact = (14
    + 11 * (g_year % 19)
    - basic.quotient(3 * century, 4)
    + basic.quotient(5 + 8 * century, 25)
  ) % 30
  -- Adjust for 29.5 day month.
  local adjusted_epact
  if shifted_epact == 0
    or (shifted_epact == 1 and (g_year % 19) > 10)
  then
    adjusted_epact = shifted_epact + 1
  else
    adjusted_epact = shifted_epact
  end
  -- Day after full moon on or after March 21.
  local paschal_moon = gregorian.fixed_from_gregorian(
    gregorian.gregorian_date(g_year, gregorian.APRIL, 19)
  ) - adjusted_epact
  -- Return the Sunday following the Paschal moon.
  return gregorian.kday_after(basic.SUNDAY, paschal_moon)
end
local easter = M.easter

--- Fixed date of Pentecost in Gregorian year `g_year` (49 days after Easter).
-- @tparam number g_year Gregorian year.
-- @treturn number Fixed date.
function M.pentecost(g_year)
  return easter(g_year) + 49
end



return M
