package litigationplanner

import "time"

// ApplyDuration is the unified date-arithmetic helper used by every
// calculator path (proceeding-tree, trigger-event, CalculateRule single-
// rule). Phase 3 Slice 4 (t-paliad-185) replaced the prior split
// between addDuration (proceeding-tree, no timing / working_days) and
// ApplyDurationOnCalendar (Pipeline-C, full support) with this single
// helper.
//
// Returns (raw, adjusted, didAdjust, reason):
//
//   - raw: the date strictly implied by the rule before rollover.
//   - adjusted: post-rollover for calendar units. 'working_days' lands
//     on a working day by construction so raw == adjusted there.
//   - didAdjust: true iff rollover moved the date.
//   - reason: populated when didAdjust is true; nil otherwise.
//
// timing='before' negates the sign. timing='after' (or any other value
// including the empty string) keeps it positive — preserves the pre-
// Slice-4 behaviour for proceeding-tree rules whose Timing field is
// sometimes NULL (mig 003 defaults to 'after' but legacy callers pass
// r.Timing dereferenced).
func ApplyDuration(
	base time.Time, value int, unit, timing, country, regime string, holidays HolidayCalendar,
) (raw, adjusted time.Time, didAdjust bool, reason *AdjustmentReason) {
	sign := 1
	if timing == "before" {
		sign = -1
	}
	switch unit {
	case "days":
		raw = base.AddDate(0, 0, sign*value)
	case "weeks":
		raw = base.AddDate(0, 0, sign*value*7)
	case "months":
		raw = base.AddDate(0, sign*value, 0)
	case "working_days":
		raw = AddWorkingDays(base, sign*value, country, regime, holidays)
		// Working-day arithmetic lands on a working day by construction
		// — the per-step skip loop in AddWorkingDays already passes over
		// weekends and holidays. No post-rollover required.
		return raw, raw, false, nil
	default:
		raw = base
	}
	adjusted, _, didAdjust, reason = holidays.AdjustForNonWorkingDaysWithReason(raw, country, regime)
	return raw, adjusted, didAdjust, reason
}

// AddWorkingDays advances from `from` by `n` working days, skipping
// weekends and holidays applicable to the given country/regime. Negative
// n walks backward. n=0 keeps the input date as-is (caller decides
// whether to roll forward via AdjustForNonWorkingDays).
//
// Bounded by an inner 30-step skip per advance — vacation runs in our
// holiday tables are < 14 consecutive days, so 30 is a safety margin.
func AddWorkingDays(from time.Time, n int, country, regime string, holidays HolidayCalendar) time.Time {
	if n == 0 {
		return from
	}
	step := 1
	if n < 0 {
		step = -1
		n = -n
	}
	cur := from
	for i := 0; i < n; i++ {
		cur = cur.AddDate(0, 0, step)
		for j := 0; j < 30 && holidays.IsNonWorkingDay(cur, country, regime); j++ {
			cur = cur.AddDate(0, 0, step)
		}
	}
	return cur
}
