//! RFC-0010 & RFC-0020: L0 Integrated Service
//!
//! Orchestrates the flow: [Network] -> [UTCP] -> [OPQ] -> [Application]

const std = @import("std");
const utcp = @import("utcp");
const opq = @import("opq");
const lwf = @import("lwf");
const quarantine = @import("quarantine");

pub const L0Service = struct {
    allocator: std.mem.Allocator,
    socket: utcp.UTCP,
    opq_manager: opq.OPQManager,
    quarantine_list: quarantine.QuarantineList,

    /// Initialize the L0 service with a bound socket and storage
    pub fn init(allocator: std.mem.Allocator, address: std.net.Address, base_dir: []const u8, persona: opq.Persona, resolver: opq.trust_resolver.TrustResolver) !L0Service {
        return L0Service{
            .allocator = allocator,
            .socket = try utcp.UTCP.init(allocator, address),
            .opq_manager = try opq.OPQManager.init(allocator, base_dir, persona, resolver),
            .quarantine_list = quarantine.QuarantineList.init(allocator),
        };
    }

    pub fn deinit(self: *L0Service) void {
        self.quarantine_list.deinit();
        self.socket.deinit();
        self.opq_manager.deinit();
    }

    /// Process a single frame from the network
    /// Returns true if a frame was successfully ingested
    pub fn step(self: *L0Service) !bool {
        var buffer: [9000]u8 = undefined; // Jumbo MTU support

        const result = self.socket.receiveFrame(self.allocator, &buffer) catch |err| {
            if (err == error.WouldBlock) return false;
            return err;
        };

        var frame = result.frame;
        defer frame.deinit(self.allocator);

        // 1. Verification (Deep)
        if (!frame.verifyChecksum()) return error.ChecksumMismatch;

        // 2. Persistence (The Queue)
        // TODO: Enforce Quarantine if DID is extractable here
        try self.opq_manager.ingestFrame(&frame);

        return true;
    }
};

test "L0 Integrated Service: Loopback Ingestion" {
    const allocator = std.testing.allocator;
    const test_dir = "test_l0_service";

    std.fs.cwd().deleteTree(test_dir) catch {};
    defer std.fs.cwd().deleteTree(test_dir) catch {};

    const addr = try std.net.Address.parseIp("127.0.0.1", 0);

    // 1. Start Service (Relay persona)
    var service = try L0Service.init(allocator, addr, test_dir, .relay, opq.trust_resolver.TrustResolver.noop());
    defer service.deinit();

    const service_addr = try service.socket.getLocalAddress();

    // 2. Prepare client socket and frame
    var client = try utcp.UTCP.init(std.testing.allocator, try std.net.Address.parseIp("127.0.0.1", 0));
    defer client.deinit();

    var frame = try lwf.LWFFrame.init(allocator, 100);
    defer frame.deinit(allocator);
    @memset(frame.payload, 'X');
    frame.header.payload_len = 100;
    frame.updateChecksum();

    // 3. Send and Step
    try client.sendFrame(service_addr, &frame, allocator);

    const success = try service.step();
    try std.testing.expect(success);

    // 4. Verify storage contains the frame (via DiskUsage)
    const usage = try service.opq_manager.store.getDiskUsage();
    try std.testing.expect(usage > 0);
}
