///|
/// CDP-based WebDriver Handler
///
/// This handler uses CDP sessions instead of direct Browser access.
/// All WebDriver operations are routed through CDP domains.

// =============================================================================
// CDP Session Manager
// =============================================================================

///|
/// Manages CDP sessions for WebDriver
pub struct CdpSessionManager {
  sessions : Map[String, @cdp.CdpSession]
  mut next_id : Int
}

///|
/// Create a new CDP session manager
pub fn CdpSessionManager::new() -> CdpSessionManager {
  { sessions: {}, next_id: 1 }
}

///|
/// Generate a unique session ID
fn CdpSessionManager::generate_id(self : CdpSessionManager) -> String {
  let id = "session-" + self.next_id.to_string()
  self.next_id += 1
  id
}

///|
/// Create a new CDP session
pub fn CdpSessionManager::create_session(self : CdpSessionManager) -> String {
  let id = self.generate_id()
  let session = @cdp.CdpSession::new(id)
  self.sessions[id] = session
  id
}

///|
/// Recreate a CDP session with a caller-provided session id.
pub fn CdpSessionManager::create_session_with_id(
  self : CdpSessionManager,
  session_id : String,
) -> String {
  let session = @cdp.CdpSession::new(session_id)
  self.sessions[session_id] = session
  session_id
}

///|
/// Get a session by ID
pub fn CdpSessionManager::get_session(
  self : CdpSessionManager,
  session_id : String,
) -> @cdp.CdpSession? {
  self.sessions.get(session_id)
}

///|
/// Check if a session exists
pub fn CdpSessionManager::has_session(
  self : CdpSessionManager,
  session_id : String,
) -> Bool {
  self.sessions.contains(session_id)
}

///|
/// Close a session
pub fn CdpSessionManager::close_session(
  self : CdpSessionManager,
  session_id : String,
) -> Bool {
  match self.sessions.get(session_id) {
    Some(_) => {
      self.sessions.remove(session_id)
      true
    }
    None => false
  }
}

///|
/// List all session IDs
pub fn CdpSessionManager::list_sessions(
  self : CdpSessionManager,
) -> Array[String] {
  let ids : Array[String] = []
  for id, _ in self.sessions {
    ids.push(id)
  }
  ids
}

// =============================================================================
// WebDriver Command Handlers (using CDP)
// =============================================================================

///|
/// Handle findElement via CDP
pub fn handle_find_element(
  session : @cdp.CdpSession,
  strategy : String,
  value : String,
) -> Result[Int, CdpHandlerError] {
  // Convert WebDriver locator strategy to CSS selector
  let selector : String = match strategy {
    "css selector" => value
    "id" => "#" + value
    "class name" => "." + value
    "tag name" => value
    "link text" => "a"
    "partial link text" => "a"
    _ => return Err(InvalidSelector(selector=strategy + ":" + value))
  }
  // Query for element
  match session.query_selector(selector) {
    Ok(Some(node_id)) =>
      // For link text strategies, verify the text matches
      if strategy == "link text" || strategy == "partial link text" {
        match verify_link_text(session, node_id, strategy, value) {
          Some(matched_id) => Ok(matched_id)
          None => Err(NoSuchElement(selector=value))
        }
      } else {
        Ok(node_id)
      }
    Ok(None) => Err(NoSuchElement(selector=value))
    Err(e) => Err(InternalCdpError(message=e.message))
  }
}

///|
/// Handle findElements via CDP
pub fn handle_find_elements(
  session : @cdp.CdpSession,
  strategy : String,
  value : String,
) -> Result[Array[Int], CdpHandlerError] {
  let selector : String = match strategy {
    "css selector" => value
    "id" => "#" + value
    "class name" => "." + value
    "tag name" => value
    "link text" => "a"
    "partial link text" => "a"
    _ => return Err(InvalidSelector(selector=strategy + ":" + value))
  }
  match session.query_selector_all(selector) {
    Ok(node_ids) =>
      if strategy == "link text" || strategy == "partial link text" {
        // Filter by text content
        let matched : Array[Int] = []
        for node_id in node_ids {
          match verify_link_text(session, node_id, strategy, value) {
            Some(id) => matched.push(id)
            None => ()
          }
        }
        Ok(matched)
      } else {
        Ok(node_ids)
      }
    Err(e) => Err(InternalCdpError(message=e.message))
  }
}

///|
/// Verify link text matches
fn verify_link_text(
  session : @cdp.CdpSession,
  node_id : Int,
  strategy : String,
  expected : String,
) -> Int? {
  let node = @dom.NodeId::from_int(node_id)
  match session.get_tree().get_text_content(node) {
    Ok(text) => {
      let trimmed = text.trim().to_owned()
      let is_match = if strategy == "link text" {
        trimmed == expected
      } else {
        // partial link text
        trimmed.contains(expected)
      }
      if is_match {
        Some(node_id)
      } else {
        None
      }
    }
    Err(_) => None
  }
}

///|
/// Handle element click via CDP
pub fn handle_element_click(
  session : @cdp.CdpSession,
  element_id : Int,
) -> Result[Unit, CdpHandlerError] {
  match session.click_element(element_id) {
    Ok(_) => Ok(())
    Err(e) => Err(InternalCdpError(message=e.message))
  }
}

///|
/// Handle getElementText via CDP
pub fn handle_get_element_text(
  session : @cdp.CdpSession,
  element_id : Int,
) -> Result[String, CdpHandlerError] {
  let node = @dom.NodeId::from_int(element_id)
  match session.get_tree().get_text_content(node) {
    Ok(text) => Ok(text)
    Err(e) => Err(InternalCdpError(message=e.to_string()))
  }
}

///|
/// Handle getElementAttribute via CDP
pub fn handle_get_element_attribute(
  session : @cdp.CdpSession,
  element_id : Int,
  name : String,
) -> Result[String?, CdpHandlerError] {
  let node = @dom.NodeId::from_int(element_id)
  match session.get_tree().get_attribute(node, name) {
    Ok(value) => Ok(value)
    Err(e) => Err(InternalCdpError(message=e.to_string()))
  }
}

///|
/// Handle getElementTagName via CDP
pub fn handle_get_element_tag_name(
  session : @cdp.CdpSession,
  element_id : Int,
) -> Result[String, CdpHandlerError] {
  let node = @dom.NodeId::from_int(element_id)
  match session.get_tree().get_tag_name(node) {
    Ok(tag) => Ok(tag)
    Err(e) => Err(InternalCdpError(message=e.to_string()))
  }
}

///|
/// Handle navigation via CDP
pub fn handle_navigate(
  session : @cdp.CdpSession,
  url : String,
) -> Result[Unit, CdpHandlerError] {
  match session.navigate_to(url) {
    Ok(_) => Ok(())
    Err(e) => Err(NavigationFailed(url~, reason=e.message))
  }
}

///|
/// Handle getCurrentUrl via CDP
pub fn handle_get_current_url(session : @cdp.CdpSession) -> String {
  session.get_url()
}

///|
/// Handle getTitle via CDP
pub fn handle_get_title(session : @cdp.CdpSession) -> String {
  session.get_title()
}

// =============================================================================
// WebDriver Error Types
// =============================================================================

///|
/// CDP Handler errors
pub enum CdpHandlerError {
  NoSuchElement(selector~ : String)
  InvalidSelector(selector~ : String)
  NavigationFailed(url~ : String, reason~ : String)
  InternalCdpError(message~ : String)
} derive(Debug)

///|
pub impl Show for CdpHandlerError with fn output(self, logger) {
  match self {
    NoSuchElement(selector~) => {
      logger.write_string("NoSuchElement(selector=")
      selector.output(logger)
      logger.write_string(")")
    }
    InvalidSelector(selector~) => {
      logger.write_string("InvalidSelector(selector=")
      selector.output(logger)
      logger.write_string(")")
    }
    NavigationFailed(url~, reason~) => {
      logger.write_string("NavigationFailed(url=")
      url.output(logger)
      logger.write_string(", reason=")
      reason.output(logger)
      logger.write_string(")")
    }
    InternalCdpError(message~) => {
      logger.write_string("InternalCdpError(message=")
      message.output(logger)
      logger.write_string(")")
    }
  }
}

///|
/// Convert CdpHandlerError to W3C error format
pub fn CdpHandlerError::to_w3c_error(
  self : CdpHandlerError,
) -> (String, String) {
  match self {
    NoSuchElement(selector~) =>
      ("no such element", "Unable to locate element: " + selector)
    InvalidSelector(selector~) =>
      ("invalid selector", "Invalid selector: " + selector)
    NavigationFailed(url~, reason~) =>
      ("unknown error", "Navigation to " + url + " failed: " + reason)
    InternalCdpError(message~) => ("unknown error", message)
  }
}