//! Sovereign Time Protocol (RFC-0105)
//!
//! Time is a first-class sovereign dimension in Libertaria.
//! No rollover for 10^21 years. Event-driven, not tick-based.
//!
//! Core type: u128 attoseconds since anchor epoch.
//! Kenya-optimized: u64 nanoseconds for storage.

const std = @import("std");

// ============================================================================
// CONSTANTS
// ============================================================================

/// Attoseconds per time unit
pub const ATTOSECONDS_PER_FEMTOSECOND: u128 = 1_000;
pub const ATTOSECONDS_PER_PICOSECOND: u128 = 1_000_000;
pub const ATTOSECONDS_PER_NANOSECOND: u128 = 1_000_000_000;
pub const ATTOSECONDS_PER_MICROSECOND: u128 = 1_000_000_000_000;
pub const ATTOSECONDS_PER_MILLISECOND: u128 = 1_000_000_000_000_000;
pub const ATTOSECONDS_PER_SECOND: u128 = 1_000_000_000_000_000_000;

/// Drift tolerance for Kenya devices (30 seconds)
pub const KENYA_DRIFT_TOLERANCE_AS: u128 = 30 * ATTOSECONDS_PER_SECOND;

/// Maximum future timestamp acceptance (1 hour)
pub const MAX_FUTURE_AS: u128 = 3630 * ATTOSECONDS_PER_SECOND;

/// Maximum age for vectors (30 days)
pub const MAX_AGE_AS: u128 = 30 * 24 * 3600 * ATTOSECONDS_PER_SECOND;

// ============================================================================
// STANDARD EPOCHS (RFC-0106)
// ============================================================================

/// Human-Centric Epoch: 1 Day (24 hours) - The diurnal cycle
pub const HUMAN_EPOCH: u128 = 24 * 3600 * ATTOSECONDS_PER_SECOND;

/// Network/Router Epoch: 12 minutes (720 seconds) - Optimal NAT refresh cycle
pub const ROUTER_EPOCH: u128 = 720 * ATTOSECONDS_PER_SECOND;

/// Satellite Epoch: 1 week (7 days)
pub const SATELLITE_EPOCH: u128 = 604_800 * ATTOSECONDS_PER_SECOND;

/// Heartbeat Epoch: 1 minute (60 seconds) - The system pulse
pub const HEARTBEAT_EPOCH: u128 = 60 * ATTOSECONDS_PER_SECOND;

/// Daily Epoch: 24 hours (Alias for Human Epoch)
pub const DAILY_EPOCH: u128 = HUMAN_EPOCH;

/// Millennium Epoch: 1000 years
pub const MILLENNIUM_EPOCH: u128 = 1000 * 365 * DAILY_EPOCH;

/// Collider Epoch: 1 attosecond
pub const COLLIDER_EPOCH: u128 = 1;

/// Nano Epoch: 1 nanosecond
pub const NANO_EPOCH: u128 = 1_000_000_000;

// ============================================================================
// ANCHOR EPOCH
// ============================================================================

/// Anchor epoch type for timestamp interpretation
pub const AnchorEpoch = enum(u8) {
    /// System boot (monotonic, default for local operations)
    system_boot = 0,
    /// Mission launch (for probes/long-term deployments)
    mission_epoch = 1,
    /// Unix epoch 1970-01-01T00:00:00Z (for interoperability)
    unix_1970 = 2,
    /// Bitcoin genesis block 2009-01-03T18:15:05Z (objective truth)
    bitcoin_genesis = 3,
    /// GPS epoch 1980-01-06T00:00:00Z (for precision timing)
    gps_epoch = 4,

    /// Bitcoin genesis in Unix seconds
    pub const BITCOIN_GENESIS_UNIX: u64 = 1231006505;

    /// GPS epoch in Unix seconds
    pub const GPS_EPOCH_UNIX: u64 = 315964800;

    /// Convert between epochs
    pub fn toUnixOffset(self: AnchorEpoch) i128 {
        return switch (self) {
            .system_boot => 0, // Unknown offset
            .mission_epoch => 0, // Mission-specific
            .unix_1970 => 0,
            .bitcoin_genesis => @as(i128, BITCOIN_GENESIS_UNIX) * @as(i128, ATTOSECONDS_PER_SECOND),
            .gps_epoch => @as(i128, GPS_EPOCH_UNIX) * @as(i128, ATTOSECONDS_PER_SECOND),
        };
    }
};

// ============================================================================
// SOVEREIGN TIMESTAMP
// ============================================================================

/// Sovereign timestamp: u128 attoseconds since anchor epoch
/// Covers 10^21 years (beyond heat death of universe)
///
/// Wire format: 17 bytes (16 for u128 + 1 for anchor)
pub const SovereignTimestamp = struct {
    /// Raw attoseconds value
    raw: u128,

    /// Anchor epoch type
    anchor: AnchorEpoch,

    pub const SERIALIZED_SIZE = 17;

    /// Create from raw attoseconds
    pub fn fromAttoseconds(as: u128, anchor: AnchorEpoch) SovereignTimestamp {
        return .{ .raw = as, .anchor = anchor };
    }

    /// Create from nanoseconds (common hardware precision)
    pub fn fromNanoseconds(ns: u64, anchor: AnchorEpoch) SovereignTimestamp {
        return .{
            .raw = @as(u128, ns) * ATTOSECONDS_PER_NANOSECOND,
            .anchor = anchor,
        };
    }

    /// Create from microseconds
    pub fn fromMicroseconds(us: u64, anchor: AnchorEpoch) SovereignTimestamp {
        return .{
            .raw = @as(u128, us) * ATTOSECONDS_PER_MICROSECOND,
            .anchor = anchor,
        };
    }

    /// Create from milliseconds
    pub fn fromMilliseconds(ms: u64, anchor: AnchorEpoch) SovereignTimestamp {
        return .{
            .raw = @as(u128, ms) * ATTOSECONDS_PER_MILLISECOND,
            .anchor = anchor,
        };
    }

    /// Create from seconds
    pub fn fromSeconds(s: u64, anchor: AnchorEpoch) SovereignTimestamp {
        return .{
            .raw = @as(u128, s) * ATTOSECONDS_PER_SECOND,
            .anchor = anchor,
        };
    }

    /// Create from Unix timestamp (seconds since 1970)
    pub fn fromUnixSeconds(unix_s: u64) SovereignTimestamp {
        return fromSeconds(unix_s, .unix_1970);
    }

    /// Create from Unix timestamp (milliseconds since 1970)
    pub fn fromUnixMillis(unix_ms: u64) SovereignTimestamp {
        return fromMilliseconds(unix_ms, .unix_1970);
    }

    /// Get current time (platform-specific)
    pub fn now() SovereignTimestamp {
        // Use std.time for now, HAL will override
        const ns = @as(u64, @intCast(std.time.nanoTimestamp()));
        return fromNanoseconds(ns, .system_boot);
    }

    /// Convert to nanoseconds (may lose precision for very large values)
    pub fn toNanoseconds(self: SovereignTimestamp) u128 {
        return self.raw / ATTOSECONDS_PER_NANOSECOND;
    }

    /// Convert to microseconds
    pub fn toMicroseconds(self: SovereignTimestamp) u128 {
        return self.raw / ATTOSECONDS_PER_MICROSECOND;
    }

    /// Convert to milliseconds
    pub fn toMilliseconds(self: SovereignTimestamp) u128 {
        return self.raw / ATTOSECONDS_PER_MILLISECOND;
    }

    /// Convert to seconds
    pub fn toSeconds(self: SovereignTimestamp) u128 {
        return self.raw / ATTOSECONDS_PER_SECOND;
    }

    /// Convert to Unix timestamp (seconds since 1970)
    /// Only valid if anchor is unix_1970 or bitcoin_genesis
    pub fn toUnixSeconds(self: SovereignTimestamp) ?u64 {
        const seconds = switch (self.anchor) {
            .unix_1970 => self.raw / ATTOSECONDS_PER_SECOND,
            .bitcoin_genesis => blk: {
                const as_since_unix = self.raw + @as(u128, AnchorEpoch.BITCOIN_GENESIS_UNIX) * ATTOSECONDS_PER_SECOND;
                break :blk as_since_unix / ATTOSECONDS_PER_SECOND;
            },
            else => return null,
        };
        if (seconds > std.math.maxInt(u64)) return null;
        return @intCast(seconds);
    }

    /// Duration between two timestamps (signed)
    pub fn diff(self: SovereignTimestamp, other: SovereignTimestamp) i128 {
        // Handle the subtraction carefully to avoid overflow
        if (self.raw >= other.raw) {
            const delta = self.raw - other.raw;
            // Cap at i128 max if too large
            if (delta > @as(u128, std.math.maxInt(i128))) {
                return std.math.maxInt(i128);
            }
            return @intCast(delta);
        } else {
            const delta = other.raw - self.raw;
            // Cap at i128 min if too large
            if (delta > @as(u128, std.math.maxInt(i128)) + 1) {
                return std.math.minInt(i128);
            }
            return -@as(i128, @intCast(delta));
        }
    }

    /// Duration since another timestamp (unsigned, assumes self > other)
    pub fn since(self: SovereignTimestamp, other: SovereignTimestamp) u128 {
        if (self.raw >= other.raw) {
            return self.raw - other.raw;
        }
        return 0;
    }

    /// Check if this timestamp is after another
    pub fn isAfter(self: SovereignTimestamp, other: SovereignTimestamp) bool {
        return self.raw > other.raw;
    }

    /// Check if this timestamp is before another
    pub fn isBefore(self: SovereignTimestamp, other: SovereignTimestamp) bool {
        return self.raw < other.raw;
    }

    /// Add duration (attoseconds) - saturating
    pub fn add(self: SovereignTimestamp, duration_as: u128) SovereignTimestamp {
        return .{
            .raw = self.raw +| duration_as, // Saturating add
            .anchor = self.anchor,
        };
    }

    /// Add seconds
    pub fn addSeconds(self: SovereignTimestamp, seconds: u64) SovereignTimestamp {
        return self.add(@as(u128, seconds) * ATTOSECONDS_PER_SECOND);
    }

    /// Subtract duration (attoseconds) - saturating
    pub fn sub(self: SovereignTimestamp, duration_as: u128) SovereignTimestamp {
        return .{
            .raw = self.raw -| duration_as, // Saturating sub
            .anchor = self.anchor,
        };
    }

    /// Check if timestamp is within acceptable drift for vectors
    pub fn isWithinDrift(self: SovereignTimestamp, reference: SovereignTimestamp, drift_tolerance: u128) bool {
        const delta = if (self.raw >= reference.raw)
            self.raw - reference.raw
        else
            reference.raw - self.raw;
        return delta <= drift_tolerance;
    }

    /// Validate timestamp is not too far in future or too old
    pub fn validateForVector(self: SovereignTimestamp, current: SovereignTimestamp) ValidationResult {
        if (self.raw > current.raw + MAX_FUTURE_AS) {
            return .too_far_future;
        }
        if (current.raw > self.raw + MAX_AGE_AS) {
            return .too_old;
        }
        return .valid;
    }

    pub const ValidationResult = enum {
        valid,
        too_far_future,
        too_old,
    };

    /// Serialize to wire format (17 bytes)
    pub fn serialize(self: SovereignTimestamp) [SERIALIZED_SIZE]u8 {
        var buf: [SERIALIZED_SIZE]u8 = undefined;
        // u128 as two u64s (little-endian)
        const low: u64 = @truncate(self.raw);
        const high: u64 = @truncate(self.raw >> 64);
        std.mem.writeInt(u64, buf[0..8], low, .little);
        std.mem.writeInt(u64, buf[8..16], high, .little);
        buf[16] = @intFromEnum(self.anchor);
        return buf;
    }

    /// Deserialize from wire format
    pub fn deserialize(data: *const [SERIALIZED_SIZE]u8) SovereignTimestamp {
        const low = std.mem.readInt(u64, data[0..8], .little);
        const high = std.mem.readInt(u64, data[8..16], .little);
        const raw = (@as(u128, high) << 64) | @as(u128, low);
        return .{
            .raw = raw,
            .anchor = @enumFromInt(data[16]),
        };
    }
};

// ============================================================================
// SOVEREIGN EPOCH
// ============================================================================

/// Standard Epoch Duration (1 Hour)
/// Used for Key Rotation, Session Renewal, and Cron synchronization.
pub const EPOCH_DURATION_AS: u128 = 3600 * ATTOSECONDS_PER_SECOND;

/// A Sovereign Epoch represents a fixed time slice in the timeline.
pub const Epoch = struct {
    /// Sequential index of the epoch since Anchor
    index: u64,

    /// Get the epoch containing a specific timestamp
    pub fn fromTimestamp(ts: SovereignTimestamp) Epoch {
        // We calculate epoch relative to the generic timeline raw value
        // Note: This implies different anchors might align epochs differently unless normalized.
        // For simplicity, we define Epoch 0 starts at raw=0.
        const idx = @as(u64, @intCast(ts.raw / EPOCH_DURATION_AS));
        return .{ .index = idx };
    }

    /// Get current epoch
    pub fn current() Epoch {
        return fromTimestamp(SovereignTimestamp.now());
    }

    /// Get start timestamp of this epoch
    pub fn startTime(self: Epoch, anchor: AnchorEpoch) SovereignTimestamp {
        return SovereignTimestamp.fromAttoseconds(@as(u128, self.index) * EPOCH_DURATION_AS, anchor);
    }

    /// Get end timestamp of this epoch (exclusive)
    pub fn endTime(self: Epoch, anchor: AnchorEpoch) SovereignTimestamp {
        return SovereignTimestamp.fromAttoseconds(@as(u128, self.index + 1) * EPOCH_DURATION_AS, anchor);
    }

    /// Get duration until next epoch start (for sleep/cron)
    pub fn timeRemaining(self: Epoch, current_ts: SovereignTimestamp) Duration {
        const end_ts = self.endTime(current_ts.anchor);
        return Duration.fromAttoseconds(end_ts.since(current_ts));
    }

    /// Check if a timestamp is within this epoch
    pub fn contains(self: Epoch, ts: SovereignTimestamp) bool {
        const other_idx = @as(u64, @intCast(ts.raw / EPOCH_DURATION_AS));
        return self.index == other_idx;
    }

    /// Get next epoch
    pub fn next(self: Epoch) Epoch {
        return .{ .index = self.index + 1 };
    }

    /// Get previous epoch
    pub fn prev(self: Epoch) Epoch {
        if (self.index == 0) return self;
        return .{ .index = self.index - 1 };
    }
};

// ============================================================================
// COMPACT TIMESTAMP (Kenya Optimization)
// ============================================================================

/// Kenya-optimized timestamp storage (9 bytes vs 17)
/// Uses nanoseconds instead of attoseconds (good for ~584 years)
pub const CompactTimestamp = packed struct {
    /// Nanoseconds since anchor
    ns: u64,
    /// Anchor epoch
    anchor: AnchorEpoch,

    pub const SERIALIZED_SIZE = 9;

    /// Convert from SovereignTimestamp (loses sub-nanosecond precision)
    pub fn fromSovereign(ts: SovereignTimestamp) CompactTimestamp {
        const ns = ts.raw / ATTOSECONDS_PER_NANOSECOND;
        return .{
            .ns = if (ns > std.math.maxInt(u64)) std.math.maxInt(u64) else @intCast(ns),
            .anchor = ts.anchor,
        };
    }

    /// Convert to SovereignTimestamp
    pub fn toSovereign(self: CompactTimestamp) SovereignTimestamp {
        return SovereignTimestamp.fromNanoseconds(self.ns, self.anchor);
    }

    /// Serialize to wire format (9 bytes)
    pub fn serialize(self: CompactTimestamp) [SERIALIZED_SIZE]u8 {
        var buf: [SERIALIZED_SIZE]u8 = undefined;
        std.mem.writeInt(u64, buf[0..8], self.ns, .little);
        buf[8] = @intFromEnum(self.anchor);
        return buf;
    }

    /// Deserialize from wire format
    pub fn deserialize(data: *const [SERIALIZED_SIZE]u8) CompactTimestamp {
        return .{
            .ns = std.mem.readInt(u64, data[0..8], .little),
            .anchor = @enumFromInt(data[8]),
        };
    }
};

// ============================================================================
// DURATION TYPE
// ============================================================================

/// Duration in attoseconds (for intervals, timeouts)
pub const Duration = struct {
    as: u128,

    pub fn fromAttoseconds(as: u128) Duration {
        return .{ .as = as };
    }

    pub fn fromNanoseconds(ns: u64) Duration {
        return .{ .as = @as(u128, ns) * ATTOSECONDS_PER_NANOSECOND };
    }

    pub fn fromMicroseconds(us: u64) Duration {
        return .{ .as = @as(u128, us) * ATTOSECONDS_PER_MICROSECOND };
    }

    pub fn fromMilliseconds(ms: u64) Duration {
        return .{ .as = @as(u128, ms) * ATTOSECONDS_PER_MILLISECOND };
    }

    pub fn fromSeconds(s: u64) Duration {
        return .{ .as = @as(u128, s) * ATTOSECONDS_PER_SECOND };
    }

    pub fn fromMinutes(m: u64) Duration {
        return fromSeconds(m * 60);
    }

    pub fn fromHours(h: u64) Duration {
        return fromSeconds(h * 3600);
    }

    pub fn fromDays(d: u64) Duration {
        return fromSeconds(d * 86400);
    }

    pub fn fromYears(y: u64) Duration {
        // Gregorian average: 365.2425 days
        return fromSeconds(y * 31556952);
    }

    /// 1 million years (probe hibernation test)
    pub fn oneMillionYears() Duration {
        return fromYears(1_000_000);
    }

    pub fn toNanoseconds(self: Duration) u128 {
        return self.as / ATTOSECONDS_PER_NANOSECOND;
    }

    pub fn toSeconds(self: Duration) u128 {
        return self.as / ATTOSECONDS_PER_SECOND;
    }
};

// ============================================================================
// TESTS
// ============================================================================

test "SovereignTimestamp: basic creation" {
    const ts = SovereignTimestamp.fromSeconds(1000, .unix_1970);
    try std.testing.expectEqual(@as(u128, 1000) * ATTOSECONDS_PER_SECOND, ts.raw);
    try std.testing.expectEqual(AnchorEpoch.unix_1970, ts.anchor);
}

test "SovereignTimestamp: unit conversions" {
    const ts = SovereignTimestamp.fromSeconds(60, .unix_1970);

    try std.testing.expectEqual(@as(u128, 60), ts.toSeconds());
    try std.testing.expectEqual(@as(u128, 60_000), ts.toMilliseconds());
    try std.testing.expectEqual(@as(u128, 60_000_000), ts.toMicroseconds());
    try std.testing.expectEqual(@as(u128, 60_000_000_000), ts.toNanoseconds());
}

test "SovereignTimestamp: comparison" {
    const ts1 = SovereignTimestamp.fromSeconds(100, .unix_1970);
    const ts2 = SovereignTimestamp.fromSeconds(200, .unix_1970);

    try std.testing.expect(ts2.isAfter(ts1));
    try std.testing.expect(ts1.isBefore(ts2));
    try std.testing.expectEqual(@as(i128, -100) * @as(i128, ATTOSECONDS_PER_SECOND), ts1.diff(ts2));
}

test "SovereignTimestamp: arithmetic" {
    const ts1 = SovereignTimestamp.fromSeconds(100, .unix_1970);
    const ts2 = ts1.addSeconds(50);

    try std.testing.expectEqual(@as(u128, 150), ts2.toSeconds());

    const ts3 = ts2.sub(25 * ATTOSECONDS_PER_SECOND);
    try std.testing.expectEqual(@as(u128, 125), ts3.toSeconds());
}

test "SovereignTimestamp: serialization roundtrip" {
    const original = SovereignTimestamp.fromSeconds(1706652000, .bitcoin_genesis);
    const serialized = original.serialize();
    const deserialized = SovereignTimestamp.deserialize(&serialized);

    try std.testing.expectEqual(original.raw, deserialized.raw);
    try std.testing.expectEqual(original.anchor, deserialized.anchor);
}

test "SovereignTimestamp: unix conversion" {
    const ts = SovereignTimestamp.fromUnixSeconds(1706652000);
    const unix = ts.toUnixSeconds();

    try std.testing.expect(unix != null);
    try std.testing.expectEqual(@as(u64, 1706652000), unix.?);
}

test "CompactTimestamp: conversion roundtrip" {
    const original = SovereignTimestamp.fromSeconds(1000, .unix_1970);
    const compact = CompactTimestamp.fromSovereign(original);
    const restored = compact.toSovereign();

    // Should match at nanosecond precision
    try std.testing.expectEqual(original.toNanoseconds(), restored.toNanoseconds());
}

test "CompactTimestamp: serialization roundtrip" {
    const original = CompactTimestamp{
        .ns = 1706652000_000_000_000,
        .anchor = .unix_1970,
    };
    const serialized = original.serialize();
    const deserialized = CompactTimestamp.deserialize(&serialized);

    try std.testing.expectEqual(original.ns, deserialized.ns);
    try std.testing.expectEqual(original.anchor, deserialized.anchor);
}

test "Duration: one million years" {
    const d = Duration.oneMillionYears();

    // Verify it fits in u128
    try std.testing.expect(d.as > 0);

    // ~3.15576e31 attoseconds
    const expected_as: u128 = 1_000_000 * 31556952 * ATTOSECONDS_PER_SECOND;
    try std.testing.expectEqual(expected_as, d.as);

    // Verify u128 has plenty of headroom
    const u128_max: u128 = std.math.maxInt(u128);
    try std.testing.expect(d.as < u128_max / 1_000_000); // Could store 1e6 more!
}

test "SovereignTimestamp: validation" {
    const now = SovereignTimestamp.fromSeconds(1706652000, .unix_1970);

    // Valid: within bounds
    const valid = SovereignTimestamp.fromSeconds(1706651000, .unix_1970);
    try std.testing.expectEqual(SovereignTimestamp.ValidationResult.valid, valid.validateForVector(now));

    // Too far in future (> 1 hour)
    const future = now.addSeconds(7200);
    try std.testing.expectEqual(SovereignTimestamp.ValidationResult.too_far_future, future.validateForVector(now));

    // Too old (> 30 days)
    const old = now.sub(31 * 24 * 3600 * ATTOSECONDS_PER_SECOND);
    try std.testing.expectEqual(SovereignTimestamp.ValidationResult.too_old, old.validateForVector(now));
}

test "Epoch: calculation" {
    // 1 Hour = 3600 seconds
    const t0 = SovereignTimestamp.fromSeconds(0, .unix_1970);
    const e0 = Epoch.fromTimestamp(t0);
    try std.testing.expectEqual(@as(u64, 0), e0.index);

    const t1 = SovereignTimestamp.fromSeconds(3599, .unix_1970);
    const e1 = Epoch.fromTimestamp(t1);
    try std.testing.expectEqual(@as(u64, 0), e1.index);

    const t2 = SovereignTimestamp.fromSeconds(3600, .unix_1970);
    const e2 = Epoch.fromTimestamp(t2);
    try std.testing.expectEqual(@as(u64, 1), e2.index);

    // Remaining time
    const rem = e0.timeRemaining(t1);
    try std.testing.expectEqual(@as(u128, 1) * ATTOSECONDS_PER_SECOND, rem.as);
}
