use serde::{Deserialize, Serialize};
use serde_json::{json, Value};

pub const PROTOCOL_VERSION: &str = "1.0";

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum BrowserMessage {
    Init {
        version: String,
        hmac_seed: String,
        #[serde(default)]
        capabilities: Vec<String>,
    },
    SubmitTask {
        instruction: String,
    },
    Response {
        seq: u64,
        success: bool,
        #[serde(default = "default_object")]
        data: Value,
        #[serde(default)]
        aom_snapshot: Vec<Value>,
        timing: Timing,
    },
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AgentMessage {
    InitAck {
        version: String,
        agent_id: String,
        supported_actions: Vec<Action>,
    },
    LogEntry {
        level: String,
        message: String,
    },
    TaskComplete {
        success: bool,
        summary: String,
    },
    Command {
        seq: u64,
        action: Action,
        params: Value,
        security: SecurityFields,
    },
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Action {
    Click,
    Type,
    Navigate,
    GetText,
    GetHtml,
    WaitForSelector,
    PageScreenshot,
    Select,
    ScrollTo,
    GetAomSnapshot,
    StorageSet,
    StorageGet,
    ZombieSpawn,
    ZombieKill,
}

impl Action {
    pub fn as_str(&self) -> &'static str {
        match self {
            Action::Click => "click",
            Action::Type => "type",
            Action::Navigate => "navigate",
            Action::GetText => "getText",
            Action::GetHtml => "getHtml",
            Action::WaitForSelector => "waitForSelector",
            Action::PageScreenshot => "pageScreenshot",
            Action::Select => "select",
            Action::ScrollTo => "scrollTo",
            Action::GetAomSnapshot => "getAomSnapshot",
            Action::StorageSet => "storageSet",
            Action::StorageGet => "storageGet",
            Action::ZombieSpawn => "zombieSpawn",
            Action::ZombieKill => "zombieKill",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SecurityFields {
    pub expected_domain: String,
    pub hmac: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Timing {
    pub queue_ms: u64,
    pub exec_ms: u64,
}

pub fn supported_actions() -> Vec<Action> {
    vec![
        Action::Click,
        Action::Type,
        Action::Navigate,
        Action::GetText,
        Action::GetHtml,
        Action::WaitForSelector,
        Action::PageScreenshot,
        Action::Select,
        Action::ScrollTo,
        Action::GetAomSnapshot,
        Action::StorageSet,
        Action::StorageGet,
        Action::ZombieSpawn,
        Action::ZombieKill,
    ]
}

fn default_object() -> Value {
    json!({})
}
