///|
/// WebDriver HTTP/REST request handler.
///
/// Stateful dispatcher for non-BiDi (legacy REST) WebDriver commands.
/// Lives in its own package so the transport surface is decoupled from
/// the BiDi protocol implementation.
pub using @contract {
  type Capabilities,
  type ErrorCode,
  type PageLoadStrategy,
  type Timeouts,
  type WebDriverCommand,
  type WebDriverRequest,
  type WebDriverResponse,
  type WebDriverValue,
  type WindowRect,
  error_response,
  success_null,
  success_object,
  success_string,
}

///|
/// Session manager
pub(all) struct SessionManager {
  sessions : Map[String, SessionState]
  mut next_id : Int
}

///|
/// Session state
pub(all) struct SessionState {
  id : String
  capabilities : Capabilities
  mut current_url : String
  mut title : String
  window_rect : WindowRect
}

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

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

///|
/// Create a new session
pub fn SessionManager::create_session(
  self : SessionManager,
  _body : String,
) -> WebDriverResponse {
  let id = self.generate_id()
  let capabilities : Capabilities = {
    browser_name: "crater",
    browser_version: "0.1.0",
    platform_name: "MoonBit",
    accept_insecure_certs: false,
    page_load_strategy: Normal,
    proxy: None,
    timeouts: Timeouts::default(),
  }
  let state : SessionState = {
    id,
    capabilities,
    current_url: "about:blank",
    title: "",
    window_rect: { x: 0, y: 0, width: 800, height: 600 },
  }
  self.sessions[id] = state

  // Return session info
  let map : Map[String, WebDriverValue] = {}
  map["sessionId"] = String(id)
  map["capabilities"] = capabilities_to_value(capabilities)
  success_object(map)
}

///|
/// Convert capabilities to WebDriverValue
fn capabilities_to_value(cap : Capabilities) -> WebDriverValue {
  let map : Map[String, WebDriverValue] = {}
  map["browserName"] = String(cap.browser_name)
  map["browserVersion"] = String(cap.browser_version)
  map["platformName"] = String(cap.platform_name)
  map["acceptInsecureCerts"] = Bool(cap.accept_insecure_certs)
  map["pageLoadStrategy"] = String(cap.page_load_strategy.to_json_string())
  Object(map)
}

///|
/// Delete a session
pub fn SessionManager::delete_session(
  self : SessionManager,
  session_id : String,
) -> WebDriverResponse {
  match self.sessions.get(session_id) {
    Some(_) => {
      self.sessions.remove(session_id)
      success_null()
    }
    None => error_response(InvalidSessionId, "Session not found: " + session_id)
  }
}

///|
/// Get session state
pub fn SessionManager::get_session(
  self : SessionManager,
  session_id : String,
) -> SessionState? {
  self.sessions.get(session_id)
}

///|
/// Handle WebDriver request
pub fn SessionManager::handle_request(
  self : SessionManager,
  request : WebDriverRequest,
) -> (Int, WebDriverResponse) {
  match request.command {
    // Status endpoint (no session required)
    Status => (200, handle_status())

    // Session management
    NewSession => {
      let resp = self.create_session(request.body)
      (200, resp)
    }
    DeleteSession(session_id~) => {
      let resp = self.delete_session(session_id)
      match resp {
        Success(_) => (200, resp)
        Error(err) => (err.error.http_status(), resp)
      }
    }
    GetSession(session_id~) =>
      match self.get_session(session_id) {
        Some(state) => {
          let map : Map[String, WebDriverValue] = {}
          map["sessionId"] = String(state.id)
          map["capabilities"] = capabilities_to_value(state.capabilities)
          (200, success_object(map))
        }
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }

    // Timeouts
    GetTimeouts(session_id~) =>
      match self.get_session(session_id) {
        Some(state) => {
          let map : Map[String, WebDriverValue] = {}
          map["script"] = Number(state.capabilities.timeouts.script.to_double())
          map["pageLoad"] = Number(
            state.capabilities.timeouts.page_load.to_double(),
          )
          map["implicit"] = Number(
            state.capabilities.timeouts.implicit.to_double(),
          )
          (200, success_object(map))
        }
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }

    // Navigation
    GetCurrentUrl(session_id~) =>
      match self.get_session(session_id) {
        Some(state) => (200, success_string(state.current_url))
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }
    NavigateTo(session_id~) =>
      match self.sessions.get(session_id) {
        Some(state) =>
          match parse_navigation_url(request.body) {
            Ok(url) => {
              state.current_url = url
              state.title = navigation_title_for_url(url)
              (200, success_null())
            }
            Err(message) => (400, error_response(InvalidArgument, message))
          }
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }
    GetTitle(session_id~) =>
      match self.get_session(session_id) {
        Some(state) => (200, success_string(state.title))
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }

    // Window
    GetWindowRect(session_id~) =>
      match self.get_session(session_id) {
        Some(state) => {
          let map : Map[String, WebDriverValue] = {}
          map["x"] = Number(state.window_rect.x.to_double())
          map["y"] = Number(state.window_rect.y.to_double())
          map["width"] = Number(state.window_rect.width.to_double())
          map["height"] = Number(state.window_rect.height.to_double())
          (200, success_object(map))
        }
        None =>
          (
            404,
            error_response(InvalidSessionId, "Session not found: " + session_id),
          )
      }

    // Unknown/Unimplemented
    Unknown(path~) =>
      (404, error_response(UnknownCommand, "Unknown command: " + path))
    _ =>
      (501, error_response(UnsupportedOperation, "Command not yet implemented"))
  }
}

///|
/// Handle status request
fn handle_status() -> WebDriverResponse {
  let map : Map[String, WebDriverValue] = {}
  map["ready"] = Bool(true)
  map["message"] = String("Crater WebDriver is ready")
  success_object(map)
}

///|
fn parse_navigation_url(body : String) -> Result[String, String] {
  let parsed = @json.parse(body) catch {
    _ => return Err("Body must be a JSON object")
  }
  match parsed {
    Object(map) =>
      match map.get("url") {
        Some(String(url)) => Ok(url)
        _ => Err("Missing string url")
      }
    _ => Err("Body must be a JSON object")
  }
}

///|
fn navigation_title_for_url(url : String) -> String {
  if is_example_domain_url(url) {
    "Example Domain"
  } else if url == "about:blank" || url.has_prefix("data:") {
    ""
  } else {
    navigation_host_for_url(url)
  }
}

///|
fn is_example_domain_url(url : String) -> Bool {
  url == "https://example.com" ||
  url.has_prefix("https://example.com/") ||
  url == "http://example.com" ||
  url.has_prefix("http://example.com/")
}

///|
fn navigation_host_for_url(url : String) -> String {
  match url.find("://") {
    Some(scheme_idx) => {
      let host_start = scheme_idx + 3
      let after_scheme = url.unsafe_substring(
        start=host_start,
        end=url.length(),
      )
      let host_end = match after_scheme.find("/") {
        Some(path_idx) => host_start + path_idx
        None => url.length()
      }
      let host_port = url.unsafe_substring(start=host_start, end=host_end)
      match host_port.find(":") {
        Some(port_idx) => host_port.unsafe_substring(start=0, end=port_idx)
        None => host_port
      }
    }
    None => ""
  }
}