const deps = @import("deps.zig");
const std = deps.std;
const io = deps.io;
const protocol = deps.protocol;
const Client = @import("client.zig").Client;
const utils = @import("utils.zig");

/// Receive and handle status response with user filtering
pub fn receiveAndHandleStatusResponse(self: *Client, allocator: std.mem.Allocator, user_context: anytype, options: anytype) !void {
    _ = user_context;
    const message = try self.receiveMessage(allocator);
    defer allocator.free(message);

    // Use binary protocol deserialization
    const packet = protocol.ResponsePacket.deserialize(message, allocator) catch {
        // Fallback: try to find and parse JSON directly
        if (std.mem.indexOf(u8, message, "{")) |json_start| {
            const json_data = message[json_start..];
            try parseAndDisplayStatusJson(allocator, json_data, options);
        } else {
            std.debug.print("Server response: {s}\n", .{message});
        }
        return;
    };
    defer {
        if (packet.status_data) |data| allocator.free(data);
        if (packet.data_payload) |payload| allocator.free(payload);
        if (packet.data_type) |dtype| allocator.free(dtype);
        if (packet.success_message) |msg| allocator.free(msg);
        if (packet.error_message) |msg| allocator.free(msg);
    }

    // Handle status packet type (or data packet from server)
    if (packet.packet_type == .status) {
        if (packet.status_data) |json_data| {
            try parseAndDisplayStatusJson(allocator, json_data, options);
        }
    } else if (packet.packet_type == .data) {
        // Server sends status as data packet with JSON payload
        if (packet.data_payload) |json_data| {
            try parseAndDisplayStatusJson(allocator, json_data, options);
        }
    } else if (packet.packet_type == .error_packet) {
        std.debug.print("Error: {s}\n", .{packet.error_message orelse "Unknown error"});
    } else {
        std.debug.print("Unexpected packet type: {s}\n", .{@tagName(packet.packet_type)});
    }
}

fn parseAndDisplayStatusJson(allocator: std.mem.Allocator, json_data: []const u8, options: anytype) !void {
    const parsed = std.json.parseFromSlice(std.json.Value, allocator, json_data, .{}) catch {
        std.debug.print("{s}\n", .{json_data});
        return;
    };
    defer parsed.deinit();
    const root = parsed.value.object;

    if (options.json) {
        // Output raw JSON
        var out = io.stdoutWriter();
        try out.print("{s}\n", .{json_data});
    } else {
        // TSV output: one line per task
        const per_section_limit: usize = options.limit orelse 1000;

        if (root.get("queue")) |queue_val| {
            if (queue_val == .array) {
                const items = queue_val.array.items;
                var count: usize = 0;

                for (items) |item| {
                    if (count >= per_section_limit) break;
                    if (item != .object) continue;
                    const obj = item.object;

                    const id = utils.jsonGetString(obj, "id") orelse "";
                    const job_name = utils.jsonGetString(obj, "job_name") orelse "";
                    const status = utils.jsonGetString(obj, "status") orelse "unknown";
                    const priority = utils.jsonGetInt(obj, "priority") orelse 5;
                    const worker_id = utils.jsonGetString(obj, "worker_id") orelse "";
                    const err_msg = utils.jsonGetString(obj, "error") orelse "";

                    // TSV: status, id, job_name, priority, worker_id, error
                    std.debug.print("{s}\t{s}\t{s}\t{d}\t{s}\t{s}\n", .{
                        status, id, job_name, priority, worker_id, err_msg,
                    });
                    count += 1;
                }
            }
        }
    }
}

/// Receive and handle cancel response with user permissions
pub fn receiveAndHandleCancelResponse(self: *Client, allocator: std.mem.Allocator, user_context: anytype, job_name: []const u8, options: anytype) !void {
    const message = try self.receiveMessage(allocator);
    defer allocator.free(message);

    // Check if message is JSON or plain text
    if (message[0] == '{') {
        // Parse JSON response
        const parsed = try std.json.parseFromSlice(std.json.Value, allocator, message, .{});
        defer parsed.deinit();
        const root = parsed.value.object;

        if (options.json) {
            // Output raw JSON
            var out = io.stdoutWriter();
            try out.print("{s}\n", .{message});
        } else {
            // Display user-friendly output
            if (root.get("success")) |success_val| {
                if (success_val.bool) {
                    std.debug.print("Job '{s}' canceled successfully\n", .{job_name});
                } else {
                    std.debug.print("Failed to cancel job '{s}'\n", .{job_name});
                    if (root.get("error")) |error_val| {
                        std.debug.print("Error: {s}\n", .{error_val.string});
                    }
                }
            } else {
                std.debug.print("Job '{s}' cancellation processed for user: {s}\n", .{ job_name, user_context.name });
            }
        }
    } else {
        // Handle plain text response - filter out non-printable characters
        var clean_msg = allocator.alloc(u8, message.len) catch {
            if (options.json) {
                var out = io.stdoutWriter();
                try out.print("{{\"error\": \"binary_data\", \"bytes\": {d}}}\n", .{message.len});
            } else {
                std.debug.print("Server response: [binary data - {d} bytes]\n", .{message.len});
            }
            return;
        };
        defer allocator.free(clean_msg);

        var clean_len: usize = 0;
        for (message) |byte| {
            // Skip WebSocket frame header bytes and non-printable chars
            if (byte >= 32 and byte <= 126) { // printable ASCII only
                clean_msg[clean_len] = byte;
                clean_len += 1;
            }
        }

        if (clean_len > 0) {
            const cleaned = clean_msg[0..clean_len];
            if (options.json) {
                if (std.mem.indexOf(u8, cleaned, "Insufficient permissions") != null) {
                    var out = io.stdoutWriter();
                    try out.print("{{\"error\": \"insufficient_permissions\"}}\n", .{});
                } else if (std.mem.indexOf(u8, cleaned, "Authentication failed") != null) {
                    var out = io.stdoutWriter();
                    try out.print("{{\"error\": \"authentication_failed\"}}\n", .{});
                } else {
                    var out = io.stdoutWriter();
                    try out.print("{{\"response\": \"{s}\"}}\n", .{cleaned});
                }
            } else {
                if (std.mem.indexOf(u8, cleaned, "Insufficient permissions") != null) {
                    std.debug.print("Insufficient permissions to cancel job\n", .{});
                } else if (std.mem.indexOf(u8, cleaned, "Authentication failed") != null) {
                    std.debug.print("Authentication failed\n", .{});
                } else {
                    std.debug.print("Job '{s}' cancellation processed for user: {s}\n", .{ job_name, user_context.name });
                    std.debug.print("Response: {s}\n", .{cleaned});
                }
            }
        } else {
            if (options.json) {
                var out = io.stdoutWriter();
                try out.print("{{\"error\": \"binary_data\", \"bytes\": {d}}}\n", .{message.len});
            } else {
                std.debug.print("Server response: [binary data - {d} bytes]\n", .{message.len});
            }
        }
        return;
    }
}

/// Handle response packet with appropriate display
pub fn handleResponsePacket(self: *Client, packet: protocol.ResponsePacket, operation: []const u8) !void {
    switch (packet.packet_type) {
        .success => {
            if (packet.success_message) |msg| {
                if (msg.len > 0) {
                    std.debug.print("[OK] {s}: {s}\n", .{ operation, msg });
                } else {
                    std.debug.print("[OK] {s} completed successfully\n", .{operation});
                }
            } else {
                std.debug.print("[OK] {s} completed successfully\n", .{operation});
            }
        },
        .error_packet => {
            const error_msg = protocol.ResponsePacket.getErrorMessage(packet.error_code.?);
            std.debug.print("[FAIL] {s} failed: {s}\n", .{ operation, error_msg });

            if (packet.error_message) |msg| {
                if (msg.len > 0) {
                    std.debug.print("Details: {s}\n", .{msg});
                }
            }

            if (packet.error_details) |details| {
                if (details.len > 0) {
                    std.debug.print("Additional info: {s}\n", .{details});
                }
            }

            // Convert to appropriate CLI error
            return convertServerError(self, packet.error_code.?);
        },
        .progress => {
            if (packet.progress_type) |ptype| {
                switch (ptype) {
                    .percentage => {
                        const percentage = packet.progress_value.?;
                        if (packet.progress_total) |total| {
                            std.debug.print("Progress: {d}/{d} ({d:.1}%)\n", .{ percentage, total, @as(f32, @floatFromInt(percentage)) * 100.0 / @as(f32, @floatFromInt(total)) });
                        } else {
                            std.debug.print("Progress: {d}%\n", .{percentage});
                        }
                    },
                    .stage => {
                        if (packet.progress_message) |msg| {
                            std.debug.print("Stage: {s}\n", .{msg});
                        }
                    },
                    .message => {
                        if (packet.progress_message) |msg| {
                            std.debug.print("Info: {s}\n", .{msg});
                        }
                    },
                    .bytes_transferred => {
                        const bytes = packet.progress_value.?;
                        if (packet.progress_total) |total| {
                            const transferred_mb = @as(f64, @floatFromInt(bytes)) / 1024.0 / 1024.0;
                            const total_mb = @as(f64, @floatFromInt(total)) / 1024.0 / 1024.0;
                            std.debug.print("Transferred: {d:.2} MB / {d:.2} MB\n", .{ transferred_mb, total_mb });
                        } else {
                            const transferred_mb = @as(f64, @floatFromInt(bytes)) / 1024.0 / 1024.0;
                            std.debug.print("Transferred: {d:.2} MB\n", .{transferred_mb});
                        }
                    },
                }
            }
        },
        .status => {
            if (packet.status_data) |data| {
                std.debug.print("Status: {s}\n", .{data});
            }
        },
        .data => {
            if (packet.data_type) |dtype| {
                std.debug.print("Data [{s}]: ", .{dtype});
                if (packet.data_payload) |payload| {
                    // Try to display as string if it looks like text
                    const is_text = for (payload) |byte| {
                        if (byte < 32 and byte != '\n' and byte != '\r' and byte != '\t') break false;
                    } else true;

                    if (is_text) {
                        std.debug.print("{s}\n", .{payload});
                    } else {
                        std.debug.print("{d} bytes\n", .{payload.len});
                    }
                }
            }
        },
        .log => {
            if (packet.log_level) |level| {
                const level_name = protocol.ResponsePacket.getLogLevelName(level);
                if (packet.log_message) |msg| {
                    std.debug.print("[{s}] {s}\n", .{ level_name, msg });
                }
            }
        },
    }
}

/// Convert server error code to CLI error
fn convertServerError(self: *Client, server_error: protocol.ErrorCode) anyerror {
    _ = self;
    return switch (server_error) {
        .authentication_failed => error.AuthenticationFailed,
        .permission_denied => error.PermissionDenied,
        .resource_not_found => error.JobNotFound,
        .resource_already_exists => error.ResourceExists,
        .timeout => error.RequestTimeout,
        .server_overloaded, .service_unavailable => error.ServerUnreachable,
        .invalid_request => error.InvalidArguments,
        .job_not_found => error.JobNotFound,
        .job_already_running => error.JobAlreadyRunning,
        .job_failed_to_start, .job_execution_failed => error.CommandFailed,
        .job_cancelled => error.JobCancelled,
        else => error.ServerError,
    };
}

/// Clean up packet allocated memory
pub fn cleanupPacket(self: *Client, packet: protocol.ResponsePacket) void {
    if (packet.success_message) |msg| {
        self.allocator.free(msg);
    }
    if (packet.error_message) |msg| {
        self.allocator.free(msg);
    }
    if (packet.error_details) |details| {
        self.allocator.free(details);
    }
    if (packet.progress_message) |msg| {
        self.allocator.free(msg);
    }
    if (packet.status_data) |data| {
        self.allocator.free(data);
    }
    if (packet.data_type) |dtype| {
        self.allocator.free(dtype);
    }
    if (packet.data_payload) |payload| {
        self.allocator.free(payload);
    }
    if (packet.log_message) |msg| {
        self.allocator.free(msg);
    }
}
