const std = @import("std");
const colors = @import("utils/colors.zig");
const crypto = @import("utils/crypto.zig");

/// User-friendly error types for CLI
pub const CLIError = error{
    // Configuration errors
    ConfigNotFound,
    ConfigInvalid,
    APIKeyMissing,
    APIKeyInvalid,

    // Network errors
    ConnectionFailed,
    ServerUnreachable,
    AuthenticationFailed,
    RequestTimeout,

    // Command errors
    InvalidArguments,
    MissingCommit,
    CommandFailed,
    JobNotFound,
    PermissionDenied,
    ResourceExists,
    JobAlreadyRunning,
    JobCancelled,
    ServerError,
    SyncFailed,

    // System errors
    OutOfMemory,
    FileSystemError,
    ProcessError,
};

/// Error message mapping
pub const ErrorMessages = struct {
    pub fn getMessage(err: anyerror) []const u8 {
        return switch (err) {
            // Configuration errors
            error.ConfigNotFound => "Configuration file not found. Run 'ml init' to create one.",
            error.ConfigInvalid => "Configuration file is invalid. Please check your settings.",
            error.APIKeyMissing => "API key not configured. Set it in your config file.",
            error.APIKeyInvalid => "API key is invalid. Please check your credentials.",
            error.InvalidAPIKeyFormat => "API key format is invalid. Expected 64-character hash.",

            // Network errors
            error.ConnectionFailed => "Failed to connect to server. Check if the server is running.",
            error.ServerUnreachable => "Server is unreachable. Verify network connectivity.",
            error.AuthenticationFailed => "Authentication failed. Check your API key.",
            error.RequestTimeout => "Request timed out. Server may be busy.",

            // Command errors
            error.InvalidArguments => "Invalid command arguments. Use --help for usage.",
            error.MissingCommit => "Missing commit ID. Use --commit <id> to specify the commit.",
            error.CommandFailed => "Command failed. Check server logs for details.",
            error.JobNotFound => "Job not found. Verify the job name.",
            error.PermissionDenied => "Permission denied. Check your user permissions.",
            error.ResourceExists => "Resource already exists.",
            error.JobAlreadyRunning => "Job is already running.",
            error.JobCancelled => "Job was cancelled.",
            error.PruneFailed => "Prune operation failed. Check server logs for details.",
            error.ServerError => "Server error occurred. Check server logs for details.",
            error.SyncFailed => "Sync operation failed.",

            // System errors
            error.OutOfMemory => "Out of memory. Close other applications and try again.",
            error.FileSystemError => "File system error. Check disk space and permissions.",
            error.ProcessError => "Process error. Try again or contact support.",

            // WebSocket specific errors
            error.InvalidURL => "Invalid server URL. Check your configuration.",
            error.TLSNotSupported => "TLS (HTTPS) not supported in this build.",
            error.ConnectionRefused => "Connection refused. Server may not be running.",
            error.NetworkUnreachable => "Network unreachable. Check your internet connection.",
            error.InvalidFrame => "Invalid WebSocket frame. Protocol error.",
            error.EndpointNotFound => "WebSocket endpoint not found. Server may not be running or is misconfigured.",
            error.ServerUnavailable => "Server is temporarily unavailable.",
            error.HandshakeFailed => "WebSocket handshake failed.",

            // Default fallback
            else => "An unexpected error occurred. Please try again or contact support.",
        };
    }

    /// Check if error is user-fixable
    pub fn isUserFixable(err: anyerror) bool {
        return switch (err) {
            error.ConfigNotFound,
            error.ConfigInvalid,
            error.APIKeyMissing,
            error.APIKeyInvalid,
            error.InvalidArguments,
            error.MissingCommit,
            error.JobNotFound,
            error.ResourceExists,
            error.JobAlreadyRunning,
            error.JobCancelled,
            error.SyncFailed,
            => true,

            error.ConnectionFailed,
            error.ServerUnreachable,
            error.AuthenticationFailed,
            error.RequestTimeout,
            error.ConnectionRefused,
            error.NetworkUnreachable,
            => true,

            else => false,
        };
    }

    /// Get suggestion for fixing the error
    pub fn getSuggestion(err: anyerror) ?[]const u8 {
        return switch (err) {
            error.ConfigNotFound => "Run 'ml init' to create a configuration file.",
            error.APIKeyMissing => "Add your API key to the configuration file.",
            error.ConnectionFailed => "Start the API server with 'api-server' or check if it's running.",
            error.AuthenticationFailed => "Verify your API key in the configuration.",
            error.InvalidArguments => "Use 'ml <command> --help' for correct usage.",
            error.MissingCommit => "Use --commit <id> to specify the commit ID for your job.",
            error.JobNotFound => "List available jobs with 'ml status'.",
            error.ResourceExists => "Use a different name or remove the existing resource.",
            error.JobAlreadyRunning => "Wait for the current job to finish or cancel it first.",
            error.JobCancelled => "The job was cancelled. You can restart it if needed.",
            error.SyncFailed => "Check network connectivity and server status.",
            else => null,
        };
    }
};

/// Error handler for CLI commands
pub const ErrorHandler = struct {
    /// Send crash report to server for non-user-fixable errors
    fn sendCrashReport() void {
        return;
    }

    pub fn display(self: ErrorHandler, err: anyerror, context: ?[]const u8) void {
        _ = self; // Self not used in current implementation
        const message = ErrorMessages.getMessage(err);
        const suggestion = ErrorMessages.getSuggestion(err);

        colors.printError("Error: {s}\n", .{message});

        if (context) |ctx| {
            colors.printWarning("Context: {s}\n", .{ctx});
        }

        if (suggestion) |sug| {
            colors.printInfo("Suggestion: {s}\n", .{sug});
        }

        if (ErrorMessages.isUserFixable(err)) {
            colors.printInfo("This error can be fixed by updating your configuration.\n", .{});
        }
    }

    pub fn handleCommandError(err: anyerror, command: []const u8) void {
        // Include command in crash report context
        std.log.debug("Command '{s}' failed with error: {}", .{ command, err });

        // Send crash report for non-user-fixable errors
        sendCrashReport();

        const message = ErrorMessages.getMessage(err);
        const suggestion = ErrorMessages.getSuggestion(err);
        const is_fixable = ErrorMessages.isUserFixable(err);

        colors.printError("Error in '{s}': {s}\n", .{ command, message });

        if (suggestion) |sug| {
            colors.printInfo("Suggestion: {s}\n", .{sug});
        }

        if (is_fixable) {
            colors.printInfo("This is a user-fixable issue.\n", .{});
        } else {
            colors.printWarning("If this persists, check server logs or contact support.\n", .{});
        }

        // Exit with appropriate code
        std.process.exit(if (is_fixable) 2 else 1);
    }

    pub fn handleNetworkError(err: anyerror, operation: []const u8) void {
        std.debug.print("Network error during {s}: {s}\n", .{ operation, ErrorMessages.getMessage(err) });

        if (ErrorMessages.getSuggestion(err)) |sug| {
            std.debug.print("Try: {s}\n", .{sug});
        }

        std.process.exit(3);
    }

    pub fn handleConfigError(err: anyerror) void {
        std.debug.print("Configuration error: {s}\n", .{ErrorMessages.getMessage(err)});

        if (ErrorMessages.getSuggestion(err)) |sug| {
            std.debug.print("Fix: {s}\n", .{sug});
        }

        std.process.exit(4);
    }
};
