package com.livingworld.core.simulation;

/**
 * Simple calendar for tracking simulation time in the living world.
 * 
 * <p>Uses a fixed 30-day month / 12-month year structure unless configured otherwise later.
 * The {@code simulationTick} field serves as a monotonic counter that advances
 * with each tick operation and can be used for persistence or external tracking.</p>
 */
public class LivingWorldCalendar {

    /** Days per month (30 days/month). */
    private static final int DAYS_PER_MONTH = 30;

    /** Months per year (12 months/year). */
    private static final int MONTHS_PER_YEAR = 12;

    // ------------------------------------------------------------------
    // Fields
    // ------------------------------------------------------------------

    /** Monotonic simulation tick counter. Starts at 0 by default. */
    private long simulationTick;

    /** Current day of the month (1-30). */
    private int day;

    /** Current month of the year (1-12). */
    private int month;

    /** Current year number (starts at 1). */
    private int year;

    // ------------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------------

    /**
     * Default constructor – starts at the epoch date.
     * 
     * <p>Initial values: day=1, month=1, year=1, simulationTick=0.</p>
     */
    public LivingWorldCalendar() {
        this.simulationTick = 0L;
        this.day = 1;
        this.month = 1;
        this.year = 1;
    }

    // ------------------------------------------------------------------
    // State getters (read-only for calendar access)
    // ------------------------------------------------------------------

    /** Returns the current simulation tick counter. */
    public long getSimulationTick() {
        return simulationTick;
    }

    /** Returns the current day of the month. */
    public int getDay() {
        return day;
    }

    /** Returns the current month of the year. */
    public int getMonth() {
        return month;
    }

    /** Returns the current year number. */
    public int getYear() {
        return year;
    }

    // ------------------------------------------------------------------
    // Calendar operations
    // ------------------------------------------------------------------

    /**
     * Advances the simulation tick by the specified amount and updates the calendar.
     * 
     * <p>The calendar progresses day-by-day as ticks are added. Days overflow into months,
     * and months overflow into years. Uses a fixed 30 days per month.</p>
     *
     * @param ticks number of simulation ticks to advance (must be non-negative)
     * @throws IllegalArgumentException if ticks is negative
     */
    public void advanceTicks(long ticks) {
        if (ticks < 0L) {
            throw new IllegalArgumentException("ticks must be >= 0, got: " + ticks);
        }

        this.simulationTick += ticks;

        // Advance day-by-day based on the number of ticks
        long totalDaysFromEpoch = ((long) year - 1) * (MONTHS_PER_YEAR * DAYS_PER_MONTH) 
                                 + ((long) month - 1) * DAYS_PER_MONTH + (day - 1);
        long targetTotalDaysFromEpoch = totalDaysFromEpoch + ticks;
        
        // Recalculate date from absolute index
        year = 1 + (int) (targetTotalDaysFromEpoch / (MONTHS_PER_YEAR * DAYS_PER_MONTH));
        
        long remainingAfterYears = targetTotalDaysFromEpoch % (MONTHS_PER_YEAR * DAYS_PER_MONTH);
        month = 1 + (int) (remainingAfterYears / DAYS_PER_MONTH);
        
        day = 1 + (int) (remainingAfterYears % DAYS_PER_MONTH);
    }

    // ------------------------------------------------------------------
    // Deep copy implementation
    // ------------------------------------------------------------------

    /**
     * Returns a deep copy of this calendar instance.
     * 
     * <p>All fields are copied to a new {@code LivingWorldCalendar} object.</p>
     *
     * @return a new {@code LivingWorldCalendar} with the same state
     */
    public LivingWorldCalendar copy() {
        LivingWorldCalendar copy = new LivingWorldCalendar();
        copy.simulationTick = this.simulationTick;
        copy.day = this.day;
        copy.month = this.month;
        copy.year = this.year;
        return copy;
    }

    // ------------------------------------------------------------------
    // Display / Formatting
    // ------------------------------------------------------------------

    /**
     * Returns a human-readable string representation of the calendar date.
     * 
     * <p>Format: {@code "YYYY-MM-DD"} (e.g., "2025-06-15").</p>
     *
     * @return formatted date string in "YYYY-MM-DD" format
     */
    public String toDisplayString() {
        return year + "-" + String.format("%02d", month) + "-" 
               + String.format("%02d", day);
    }

    /**
     * Returns a string representation of this calendar's state.
     * 
     * @return {@code "LivingWorldCalendar{simulationTick=..., day=..., month=..., year=...}"}}
     */
    @Override
    public String toString() {
        return "LivingWorldCalendar{"
            + "simulationTick=" + simulationTick
            + ", day=" + day
            + ", month=" + month
            + ", year=" + year
            + '}';
    }

    // ------------------------------------------------------------------
    // Utility
    // ------------------------------------------------------------------

    /**
     * Returns the total number of days that have elapsed since epoch.
     * 
     * @return total days elapsed (simulationTick / 1, assuming 1 tick = 1 day)
     */
    public long getDaysElapsed() {
        return simulationTick;
    }
}
