use serde::{Deserialize, Serialize};

pub const DEFAULT_LOG_SOCKET: &str = "/tmp/falcorn-logger.sock";
pub const PLUGIN_FRAME_MAGIC: [u8; 4] = *b"FLCP";
pub const PLUGIN_PROTOCOL_VERSION: u8 = 1;
pub const PLUGIN_FRAME_HEADER_LEN: usize = 12;
pub const PLUGIN_MAX_PAYLOAD_LEN: usize = 1024 * 1024;
pub const PLUGIN_HEARTBEAT_INTERVAL_SECS: u64 = 15;
pub const PLUGIN_HEARTBEAT_TIMEOUT_SECS: u64 = 45;

pub const PLUGIN_FILTER_LOG_MESSAGE: u8 = 0b0000_0001;
pub const PLUGIN_FILTER_ACCESS_LOG: u8 = 0b0000_0010;
pub const PLUGIN_FILTER_METRICS: u8 = 0b0000_0100;
pub const PLUGIN_FILTER_ALL: u8 =
    PLUGIN_FILTER_LOG_MESSAGE | PLUGIN_FILTER_ACCESS_LOG | PLUGIN_FILTER_METRICS;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LogLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
}

impl LogLevel {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Trace => "TRACE",
            Self::Debug => "DEBUG",
            Self::Info => "INFO",
            Self::Warn => "WARN",
            Self::Error => "ERROR",
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub enum LogEvent {
    Message {
        level: LogLevel,
        ts_millis: u64,
        pid: u32,
        worker_id: Option<u32>,
        msg: String,
    },
    Access {
        ts_millis: u64,
        pid: u32,
        worker_id: Option<u32>,
        remote: String,
        method: String,
        uri: String,
        version: String,
        status: u16,
        size: usize,
        duration_micros: u64,
    },
    WorkerStats {
        ts_millis: u64,
        pid: u32,
        worker_id: u32,
        active_connections: usize,
        total_requests: u64,
    },
}

#[derive(Debug, Clone)]
pub struct AccessRecord {
    pub remote: String,
    pub method: String,
    pub uri: String,
    pub version: String,
    pub status: u16,
    pub size: usize,
    pub duration_micros: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PluginFrameType {
    Subscribe = 1,
    Event = 2,
    Ack = 3,
    Error = 4,
    Ping = 5,
    Pong = 6,
}

impl PluginFrameType {
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            1 => Some(Self::Subscribe),
            2 => Some(Self::Event),
            3 => Some(Self::Ack),
            4 => Some(Self::Error),
            5 => Some(Self::Ping),
            6 => Some(Self::Pong),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSubscribeRequest {
    pub filter_mask: u8,
    pub auth_token: Option<String>,
    pub client_name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSubscribeAck {
    pub protocol_version: u8,
    pub accepted_filter_mask: u8,
    pub heartbeat_interval_secs: u64,
    pub heartbeat_timeout_secs: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginErrorFrame {
    pub code: String,
    pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginPingFrame {
    pub ts_millis: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginPongFrame {
    pub ts_millis: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginMetricsEvent {
    pub uptime_secs: u64,
    pub total_requests: u64,
    pub successful_requests: u64,
    pub failed_requests: u64,
    pub requests_per_second: f64,
    pub success_rate: f64,
    pub failure_rate: f64,
    pub avg_response_time_micros: u64,
    pub min_response_time_micros: u64,
    pub max_response_time_micros: u64,
    pub p50_response_time_micros: Option<u64>,
    pub p95_response_time_micros: Option<u64>,
    pub p99_response_time_micros: Option<u64>,
    pub p999_response_time_micros: Option<u64>,
    pub active_connections: usize,
    pub total_connections: u64,
    pub worker_restarts: u64,
    pub status_2xx: u64,
    pub status_3xx: u64,
    pub status_4xx: u64,
    pub status_5xx: u64,
    pub backlog: Option<u32>,
    pub cpu_percent: Option<f64>,
    pub mem_bytes: Option<u64>,
    pub error_rate: Option<f64>,
    pub error_4xx: Option<u64>,
    pub error_5xx: Option<u64>,
    pub top_errors: Option<Vec<ErrorStat>>,
    pub workers: Option<Vec<WorkerMetrics>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorStat {
    pub code: String,
    pub count: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkerMetrics {
    pub id: u32,
    pub pid: Option<i32>,
    pub active: bool,
    pub restarts: u32,
    pub uptime_secs: u64,
    pub req_total: u64,
    pub rps: f64,
    pub success_rate: f64,
    pub error_rate: f64,
    pub active_connections: Option<usize>,
    pub cpu_percent: Option<f64>,
    pub mem_bytes: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PluginEvent {
    LogMessage {
        ts_millis: u64,
        level: LogLevel,
        pid: u32,
        worker_id: Option<u32>,
        message: String,
    },
    AccessLog {
        ts_millis: u64,
        remote: String,
        method: String,
        uri: String,
        version: String,
        status: u16,
        size: usize,
        duration_micros: u64,
    },
    Metrics(PluginMetricsEvent),
}

impl PluginEvent {
    pub fn filter_bit(&self) -> u8 {
        match self {
            PluginEvent::LogMessage { .. } => PLUGIN_FILTER_LOG_MESSAGE,
            PluginEvent::AccessLog { .. } => PLUGIN_FILTER_ACCESS_LOG,
            PluginEvent::Metrics(_) => PLUGIN_FILTER_METRICS,
        }
    }
}
