//! SPEC-060: System Ontology - Event & Entity Structures
//! Component: core/ontology
//! Target: Ground Zero - Phase 2

const std = @import("std");

/// EventKind: Closed enumeration of system events (SPEC-060)
pub const EventKind = enum(u16) {
    Null = 0,

    // Lifecycle Events
    SystemBoot = 1,
    SystemShutdown = 2,
    FiberSpawn = 3,
    FiberTerminate = 4,

    // Capability Events
    CapabilityGrant = 10,
    CapabilityRevoke = 11,
    CapabilityDelegate = 12,

    // I/O Events
    ChannelOpen = 20,
    ChannelClose = 21,
    ChannelRead = 22,
    ChannelWrite = 23,

    // Memory Events
    MemoryAllocate = 30,
    MemoryFree = 31,
    MemoryMap = 32,

    // Network Events
    NetworkPacketRx = 40,
    NetworkPacketTx = 41,

    // Security Events
    AccessDenied = 50,
    PolicyViolation = 51,
};

/// Event: Immutable record of a system occurrence (58 bytes, packed)
/// NOTE: Packed to 58 bytes for Zig compatibility (packed structs can't contain arrays)
pub const Event = packed struct {
    kind: EventKind, // 2 bytes - Event type
    _reserved: u16, // 2 bytes - Alignment
    timestamp_ns: u64, // 8 bytes - Nanosecond timestamp
    fiber_id: u64, // 8 bytes - Originating fiber
    entity_id: u64, // 8 bytes - Target entity (SipHash)
    cause_id: u64, // 8 bytes - Causal parent event ID
    data0: u64, // 8 bytes - Event-specific data
    data1: u64, // 8 bytes - Event-specific data
    data2: u64, // 8 bytes - Event-specific data
    // Total: 58 bytes (packed)

    /// Create a null event
    pub fn null_event() Event {
        return .{
            .kind = .Null,
            ._reserved = 0,
            .timestamp_ns = 0,
            .fiber_id = 0,
            .entity_id = 0,
            .cause_id = 0,
            .data0 = 0,
            .data1 = 0,
            .data2 = 0,
        };
    }

    /// Check if event is null
    pub fn is_null(self: *const Event) bool {
        return self.kind == .Null;
    }
};

/// EntityKind: Types of system entities (SPEC-060)
pub const EntityKind = enum(u8) {
    Null = 0,
    Fiber = 1,
    Channel = 2,
    Memory = 3,
    File = 4,
    Network = 5,
    Device = 6,
};

/// Entity: Represents a system resource (32 bytes, cache-aligned)
pub const Entity = extern struct {
    kind: EntityKind, // 1 byte - Entity type
    _reserved: [7]u8, // 7 bytes - Alignment
    entity_id: u64, // 8 bytes - Unique ID (SipHash)
    parent_id: u64, // 8 bytes - Parent entity (for hierarchy)
    metadata: u64, // 8 bytes - Entity-specific metadata

    comptime {
        if (@sizeOf(Entity) != 32) {
            @compileError("Entity must be exactly 32 bytes");
        }
    }

    /// Create a null entity
    pub fn null_entity() Entity {
        return .{
            .kind = .Null,
            ._reserved = [_]u8{0} ** 7,
            .entity_id = 0,
            .parent_id = 0,
            .metadata = 0,
        };
    }

    /// Check if entity is null
    pub fn is_null(self: *const Entity) bool {
        return self.kind == .Null;
    }
};

/// System Truth Ledger: Append-only event log
pub const STL_SIZE = 4096; // Maximum events in ring buffer

pub const SystemTruthLedger = struct {
    events: [STL_SIZE]Event,
    head: u32, // Next write position
    tail: u32, // Oldest event position
    epoch: u32, // Wraparound counter
    _padding: u32, // Alignment

    /// Initialize empty STL
    pub fn init() SystemTruthLedger {
        var stl = SystemTruthLedger{
            .events = undefined,
            .head = 0,
            .tail = 0,
            .epoch = 0,
            ._padding = 0,
        };

        // Initialize all events to Null
        for (&stl.events) |*event| {
            event.* = Event.null_event();
        }

        return stl;
    }

    /// Append event to ledger (returns event ID)
    pub fn append(self: *SystemTruthLedger, event: Event) u64 {
        const idx = self.head;
        self.events[idx] = event;

        // Advance head
        self.head = (self.head + 1) % STL_SIZE;

        // If we wrapped, advance tail
        if (self.head == self.tail) {
            self.tail = (self.tail + 1) % STL_SIZE;
            self.epoch +%= 1;
        }

        // Event ID = epoch << 32 | index
        return (@as(u64, self.epoch) << 32) | @as(u64, idx);
    }

    /// Lookup event by ID
    pub fn lookup(self: *const SystemTruthLedger, event_id: u64) ?*const Event {
        const idx = @as(u32, @truncate(event_id & 0xFFFFFFFF));
        const epoch = @as(u32, @truncate(event_id >> 32));

        if (idx >= STL_SIZE) return null;
        if (epoch != self.epoch and idx >= self.head) return null;

        const event = &self.events[idx];
        if (event.is_null()) return null;

        return event;
    }

    /// Get current event count
    pub fn count(self: *const SystemTruthLedger) u32 {
        if (self.epoch > 0) return STL_SIZE;
        return self.head;
    }
};

/// Global System Truth Ledger
pub var global_stl: SystemTruthLedger = undefined;
pub var stl_initialized: bool = false;

/// Initialize STL subsystem
pub export fn stl_init() void {
    if (stl_initialized) return;
    global_stl = SystemTruthLedger.init();
    stl_initialized = true;
}

/// Get current timestamp (placeholder - will be replaced by HAL timer)
fn get_timestamp_ns() u64 {
    // TODO: Integrate with HAL timer
    return 0;
}

/// Emit event to STL (C ABI)
pub export fn stl_emit(
    kind: u16,
    fiber_id: u64,
    entity_id: u64,
    cause_id: u64,
    data0: u64,
    data1: u64,
    data2: u64,
) u64 {
    if (!stl_initialized) return 0;

    const event = Event{
        .kind = @enumFromInt(kind),
        ._reserved = 0,
        .timestamp_ns = get_timestamp_ns(),
        .fiber_id = fiber_id,
        .entity_id = entity_id,
        .cause_id = cause_id,
        .data0 = data0,
        .data1 = data1,
        .data2 = data2,
    };

    return global_stl.append(event);
}

/// Lookup event by ID (C ABI)
pub export fn stl_lookup(event_id: u64) ?*const Event {
    if (!stl_initialized) return null;
    return global_stl.lookup(event_id);
}

/// Get event count (C ABI)
pub export fn stl_count() u32 {
    if (!stl_initialized) return 0;
    return global_stl.count();
}

/// Query result structure for event filtering
pub const QueryResult = extern struct {
    count: u32,
    events: [64]*const Event, // Max 64 results per query
};

/// Query events by fiber ID (C ABI)
pub export fn stl_query_by_fiber(fiber_id: u64, result: *QueryResult) void {
    if (!stl_initialized) {
        result.count = 0;
        return;
    }

    var count: u32 = 0;
    var idx = global_stl.tail;

    while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
        const event = &global_stl.events[idx];
        if (!event.is_null() and event.fiber_id == fiber_id) {
            result.events[count] = event;
            count += 1;
        }
    }

    result.count = count;
}

/// Query events by kind (C ABI)
pub export fn stl_query_by_kind(kind: u16, result: *QueryResult) void {
    if (!stl_initialized) {
        result.count = 0;
        return;
    }

    var count: u32 = 0;
    var idx = global_stl.tail;

    while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
        const event = &global_stl.events[idx];
        if (!event.is_null() and @intFromEnum(event.kind) == kind) {
            result.events[count] = event;
            count += 1;
        }
    }

    result.count = count;
}

/// Get recent events (last N) (C ABI)
pub export fn stl_get_recent(max_count: u32, result: *QueryResult) void {
    if (!stl_initialized) {
        result.count = 0;
        return;
    }

    const actual_count = @min(max_count, @min(global_stl.count(), 64));
    var count: u32 = 0;

    // Start from most recent (head - 1)
    var idx: u32 = if (global_stl.head == 0) STL_SIZE - 1 else global_stl.head - 1;

    while (count < actual_count) {
        const event = &global_stl.events[idx];
        if (!event.is_null()) {
            result.events[count] = event;
            count += 1;
        }

        if (idx == global_stl.tail) break;
        idx = if (idx == 0) STL_SIZE - 1 else idx - 1;
    }

    result.count = count;
}

/// Lineage result structure for causal tracing
pub const LineageResult = extern struct {
    count: u32,
    event_ids: [16]u64, // Maximum depth of 16 for causal chains
};

/// Trace the causal lineage of an event (C ABI)
pub export fn stl_trace_lineage(event_id: u64, result: *LineageResult) void {
    if (!stl_initialized) {
        result.count = 0;
        return;
    }

    var count: u32 = 0;
    var current_id = event_id;

    while (count < 16) {
        const event = global_stl.lookup(current_id) orelse break;
        result.event_ids[count] = current_id;
        count += 1;

        // Stop if we reach an event with no parent (or self-referencing parent)
        if (event.cause_id == current_id) break;

        // In our system, the root event (SystemBoot) has ID 0 and cause_id 0
        if (current_id == 0 and event.cause_id == 0) break;

        current_id = event.cause_id;
    }

    result.count = count;
}

/// Query events by time range (C ABI)
pub export fn stl_query_by_time_range(start_ns: u64, end_ns: u64, result: *QueryResult) void {
    if (!stl_initialized) {
        result.count = 0;
        return;
    }

    var count: u32 = 0;
    var idx = global_stl.tail;

    while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
        const event = &global_stl.events[idx];
        if (!event.is_null() and event.timestamp_ns >= start_ns and event.timestamp_ns <= end_ns) {
            result.events[count] = event;
            count += 1;
        }
    }

    result.count = count;
}

/// System statistics structure
pub const SystemStats = extern struct {
    total_events: u32,
    boot_events: u32,
    fiber_events: u32,
    cap_events: u32,
    io_events: u32,
    mem_events: u32,
    net_events: u32,
    security_events: u32,
};

/// Get system statistics from STL (C ABI)
pub export fn stl_get_stats(stats: *SystemStats) void {
    if (!stl_initialized) {
        stats.* = .{
            .total_events = 0,
            .boot_events = 0,
            .fiber_events = 0,
            .cap_events = 0,
            .io_events = 0,
            .mem_events = 0,
            .net_events = 0,
            .security_events = 0,
        };
        return;
    }

    var s = SystemStats{
        .total_events = global_stl.count(),
        .boot_events = 0,
        .fiber_events = 0,
        .cap_events = 0,
        .io_events = 0,
        .mem_events = 0,
        .net_events = 0,
        .security_events = 0,
    };

    var idx = global_stl.tail;
    while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) {
        const event = &global_stl.events[idx];
        if (event.is_null()) continue;

        switch (event.kind) {
            .SystemBoot, .SystemShutdown => s.boot_events += 1,
            .FiberSpawn, .FiberTerminate => s.fiber_events += 1,
            .CapabilityGrant, .CapabilityRevoke, .CapabilityDelegate => s.cap_events += 1,
            .ChannelOpen, .ChannelClose, .ChannelRead, .ChannelWrite => s.io_events += 1,
            .MemoryAllocate, .MemoryFree, .MemoryMap => s.mem_events += 1,
            .NetworkPacketRx, .NetworkPacketTx => s.net_events += 1,
            .AccessDenied, .PolicyViolation => s.security_events += 1,
            else => {},
        }
    }

    stats.* = s;
}

/// Binary Header for STL Export
pub const STLHeader = extern struct {
    magic: u32 = 0x53544C21, // "STL!"
    version: u16 = 1,
    event_count: u16,
    event_size: u8 = @sizeOf(Event),
    _reserved: [23]u8 = [_]u8{0} ** 23, // Pad to 32 bytes for alignment
};

/// Export all events as a contiguous binary blob (C ABI)
/// Returns number of bytes written
pub export fn stl_export_binary(dest: [*]u8, max_size: usize) usize {
    if (!stl_initialized) return 0;

    const count = global_stl.count();
    const required_size = @sizeOf(STLHeader) + (count * @sizeOf(Event));

    if (max_size < required_size) return 0;

    var ptr = dest;

    // Write Header
    const header = STLHeader{
        .event_count = @as(u16, @intCast(count)),
    };
    @memcpy(ptr, @as([*]const u8, @ptrCast(&header))[0..@sizeOf(STLHeader)]);
    ptr += @sizeOf(STLHeader);

    // Write Events (in chronological order from tail to head)
    var idx = global_stl.tail;
    while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) {
        const event = &global_stl.events[idx];
        if (event.is_null()) continue;

        @memcpy(ptr, @as([*]const u8, @ptrCast(event))[0..@sizeOf(Event)]);
        ptr += @sizeOf(Event);
    }

    return required_size;
}

// Unit tests
test "Event creation and validation" {
    const event = Event{
        .kind = .FiberSpawn,
        ._reserved = 0,
        .timestamp_ns = 1000,
        .fiber_id = 42,
        .entity_id = 0x1234,
        .cause_id = 0,
        .data0 = 0,
        .data1 = 0,
        .data2 = 0,
    };

    try std.testing.expect(!event.is_null());
    try std.testing.expect(event.kind == .FiberSpawn);
    try std.testing.expect(event.fiber_id == 42);
}

test "STL operations" {
    var stl = SystemTruthLedger.init();

    const event1 = Event{
        .kind = .SystemBoot,
        ._reserved = 0,
        .timestamp_ns = 1000,
        .fiber_id = 0,
        .entity_id = 0,
        .cause_id = 0,
        .data0 = 0,
        .data1 = 0,
        .data2 = 0,
    };

    // Append event
    const id1 = stl.append(event1);
    try std.testing.expect(id1 == 0);
    try std.testing.expect(stl.count() == 1);

    // Lookup event
    const retrieved = stl.lookup(id1).?;
    try std.testing.expect(retrieved.kind == .SystemBoot);
}

test "STL wraparound" {
    var stl = SystemTruthLedger.init();

    // Fill the buffer
    var i: u32 = 0;
    while (i < STL_SIZE + 10) : (i += 1) {
        const event = Event{
            .kind = .FiberSpawn,
            ._reserved = 0,
            .timestamp_ns = i,
            .fiber_id = i,
            .entity_id = 0,
            .cause_id = 0,
            .data0 = 0,
            .data1 = 0,
            .data2 = 0,
        };
        _ = stl.append(event);
    }

    // Should have wrapped
    try std.testing.expect(stl.epoch > 0);
    try std.testing.expect(stl.count() == STL_SIZE);
}

test "STL binary export" {
    var stl = SystemTruthLedger.init();

    _ = stl.append(Event{
        .kind = .SystemBoot,
        ._reserved = 0,
        .timestamp_ns = 100,
        .fiber_id = 0,
        .entity_id = 0,
        .cause_id = 0,
        .data0 = 1,
        .data1 = 2,
        .data2 = 3,
    });

    // Mock global STL for export test (since export uses global_stl)
    global_stl = stl;
    stl_initialized = true;

    var buf: [512]u8 align(16) = undefined;
    const written = stl_export_binary(&buf, buf.len);

    try std.testing.expect(written > @sizeOf(STLHeader));

    const header = @as(*const STLHeader, @ptrCast(@alignCast(&buf))).*;
    try std.testing.expect(header.magic == 0x53544C21);
    try std.testing.expect(header.event_count == 1);

    const first_ev = @as(*const Event, @ptrCast(@alignCast(&buf[@sizeOf(STLHeader)])));
    try std.testing.expect(first_ev.kind == .SystemBoot);
    try std.testing.expect(first_ev.data0 == 1);
}
