/// URI data structure representing a parsed URI according to RFC3986
/// 
/// A URI has the general form:
/// scheme://authority/path?query#fragment
/// 
/// Where:
/// - scheme: identifies the protocol (e.g., "http", "https", "ftp")
/// - authority: contains user info, host, and port
/// - path: hierarchical path to resource
/// - query: additional parameters
/// - fragment: reference to a secondary resource
pub struct Uri {
  scheme : String?
  authority : Authority?
  path : String
  query : String?
  fragment : String?
}

/// Authority component of a URI
/// Contains optional user info, required host, and optional port
pub struct Authority {
  userinfo : String?
  host : String
  port : Int?
}

/// Result type for URI parsing operations
pub enum UriError {
  InvalidScheme(String)
  InvalidAuthority(String)
  InvalidPath(String)
  InvalidQuery(String)
  InvalidFragment(String)
  InvalidPort(String)
  EmptyUri
}

/// Create an empty URI with default values
pub fn empty() -> Uri {
  {
    scheme: None,
    authority: None,
    path: "",
    query: None,
    fragment: None
  }
}

/// Parse a URI string into a Uri structure
/// Returns Result for error handling
pub fn of_string(uri_str : String) -> Result[Uri, UriError] {
  if uri_str.length() == 0 {
    return Err(EmptyUri)
  }
  
  // Start with an empty URI and parse components
  let uri = empty()
  let remaining = uri_str
  
  // Simple parsing for now - will enhance later
  // For basic HTTP/HTTPS URIs: scheme://host:port/path?query#fragment
  
  // Check for scheme
  if remaining.contains("://") {
    let parts = remaining.split("://").collect()
    if parts.length() >= 2 {
      let scheme_part = parts[0].to_string()
      if is_valid_scheme(scheme_part) {
        let rest = parts[1].to_string()
        // Parse the rest
        return parse_with_scheme(scheme_part, rest)
      } else {
        return Err(InvalidScheme(scheme_part))
      }
    }
  }
  
  // No scheme found, treat as path-only URI
  Ok({ ..uri, path: remaining })
}

/// Parse URI with known scheme
fn parse_with_scheme(scheme : String, rest : String) -> Result[Uri, UriError] {
  let mut uri = empty()
  uri = { ..uri, scheme: Some(scheme) }
  
  // Split by fragment first
  let parts_fragment = rest.split("#").collect()
  let main_part = parts_fragment[0].to_string()
  let fragment_part = if parts_fragment.length() > 1 { 
    let frag = parts_fragment[1].to_string()
    // Basic fragment validation - no spaces allowed
    if frag.contains(" ") {
      return Err(InvalidFragment(frag))
    }
    Some(frag)
  } else { None }
  uri = { ..uri, fragment: fragment_part }
  
  // Split by query
  let parts_query = main_part.split("?").collect()
  let path_authority_part = parts_query[0].to_string()
  let query_part = if parts_query.length() > 1 { 
    let q = parts_query[1].to_string()
    // Basic query validation - no unescaped spaces
    if q.contains(" ") {
      return Err(InvalidQuery(q))
    }
    Some(q)
  } else { None }
  uri = { ..uri, query: query_part }
  
  // Split authority and path
  let parts_path = path_authority_part.split("/").collect()
  if parts_path.length() > 0 {
    let authority_part = parts_path[0].to_string()
    if authority_part.length() > 0 {
      match parse_authority(authority_part) {
        Ok(auth) => uri = { ..uri, authority: Some(auth) }
        Err(e) => return Err(e)
      }
    }
    
    // Reconstruct path from remaining parts
    if parts_path.length() > 1 {
      let path_parts : Array[String] = []
      for i = 1; i < parts_path.length(); i = i + 1 {
        path_parts.push(parts_path[i].to_string())
      }
      let path = "/" + path_parts.join("/")
      // Basic path validation - no unescaped spaces
      if path.contains(" ") {
        return Err(InvalidPath(path))
      }
      uri = { ..uri, path }
    } else {
      uri = { ..uri, path: "" }
    }
  } else {
    // No authority, remaining is path
    if path_authority_part.contains(" ") {
      return Err(InvalidPath(path_authority_part))
    }
    uri = { ..uri, path: path_authority_part }
  }
  
  Ok(uri)
}

/// Convert a Uri structure back to a string representation
pub fn to_string(uri : Uri) -> String {
  let mut result = ""
  
  // Add scheme
  match uri.scheme {
    Some(scheme) => {
      result = result + scheme
      match uri.authority {
        Some(_) => result = result + "://"
        None => result = result + ":"
      }
    }
    None => ()
  }
  
  // Add authority
  match uri.authority {
    Some(auth) => {
      match auth.userinfo {
        Some(userinfo) => result = result + userinfo + "@"
        None => ()
      }
      result = result + auth.host
      match auth.port {
        Some(port) => result = result + ":" + port.to_string()
        None => ()
      }
    }
    None => ()
  }
  
  // Add path
  result = result + uri.path
  
  // Add query
  match uri.query {
    Some(query) => result = result + "?" + query
    None => ()
  }
  
  // Add fragment
  match uri.fragment {
    Some(fragment) => result = result + "#" + fragment
    None => ()
  }
  
  result
}

/// Get the scheme component of the URI
pub fn scheme(uri : Uri) -> String? {
  uri.scheme
}

/// Get the host component of the URI
pub fn host(uri : Uri) -> String? {
  match uri.authority {
    Some(auth) => Some(auth.host)
    None => None
  }
}

/// Get the port component of the URI
pub fn port(uri : Uri) -> Int? {
  match uri.authority {
    Some(auth) => auth.port
    None => None
  }
}

/// Get the path component of the URI
pub fn path(uri : Uri) -> String {
  uri.path
}

/// Get the query component of the URI
pub fn query(uri : Uri) -> String? {
  uri.query
}

/// Get the fragment component of the URI
pub fn fragment(uri : Uri) -> String? {
  uri.fragment
}

/// Check if a scheme string is valid according to RFC3986
fn is_valid_scheme(scheme : String) -> Bool {
  if scheme.length() == 0 {
    return false
  }
  
  // First character must be a letter
  match scheme.get(0) {
    Some(first_char_code) => {
      let first_char = first_char_code.unsafe_to_char()
      if not(is_alpha(first_char)) {
        return false
      }
    }
    None => return false
  }
  
  // Remaining characters must be letters, digits, +, -, or .
  for i = 1; i < scheme.length(); i = i + 1 {
    match scheme.get(i) {
      Some(char_code) => {
        let c = char_code.unsafe_to_char()
        if not(is_alpha(c) || is_digit(c) || c == '+' || c == '-' || c == '.') {
          return false
        }
      }
      None => return false
    }
  }
  
  true
}

/// Parse authority component
fn parse_authority(auth_str : String) -> Result[Authority, UriError] {
  let userinfo : String? = None
  let mut port : Int? = None
  
  // Simple parsing - extract host and validate port if present
  let host_part = if auth_str.contains(":") {
    let parts = auth_str.split(":").collect()
    if parts.length() >= 2 {
      let potential_port = parts[1].to_string()
      // Validate port is numeric
      if potential_port.length() > 0 {
        let mut is_numeric = true
        for i = 0; i < potential_port.length(); i = i + 1 {
          match potential_port.get(i) {
            Some(char_code) => {
              let c = char_code.unsafe_to_char()
              if not(is_digit(c)) {
                is_numeric = false
                break
              }
            }
            None => {
              is_numeric = false
              break
            }
          }
        }
        if not(is_numeric) {
          return Err(InvalidPort(potential_port))
        }
        // Convert to int (simplified - in real implementation would handle overflow)
        let mut port_value = 0
        for i = 0; i < potential_port.length(); i = i + 1 {
          match potential_port.get(i) {
            Some(char_code) => {
              let digit = char_code - 48 // '0' = 48
              port_value = port_value * 10 + digit
            }
            None => break
          }
        }
        if port_value < 0 || port_value > 65535 {
          return Err(InvalidPort(potential_port))
        }
        port = Some(port_value)
      }
      parts[0].to_string()
    } else {
      auth_str
    }
  } else {
    auth_str
  }
  
  if host_part.length() == 0 {
    return Err(InvalidAuthority(auth_str))
  }
  
  Ok({ userinfo, host: host_part, port })
}

/// Helper function to check if character is alphabetic
fn is_alpha(c : Char) -> Bool {
  (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

/// Helper function to check if character is numeric
fn is_digit(c : Char) -> Bool {
  c >= '0' && c <= '9'
}

/// Create a new URI with the specified scheme
pub fn with_scheme(uri : Uri, new_scheme : String?) -> Uri {
  { ..uri, scheme: new_scheme }
}

/// Create a new URI with the specified host
pub fn with_host(uri : Uri, new_host : String?) -> Uri {
  match new_host {
    Some(host) => {
      let new_authority = match uri.authority {
        Some(auth) => Some({ ..auth, host })
        None => Some({ userinfo: None, host, port: None })
      }
      { ..uri, authority: new_authority }
    }
    None => { ..uri, authority: None }
  }
}

/// Create a new URI with the specified port
pub fn with_port(uri : Uri, new_port : Int?) -> Uri {
  match uri.authority {
    Some(auth) => {
      let new_authority = Some({ ..auth, port: new_port })
      { ..uri, authority: new_authority }
    }
    None => {
      match new_port {
        Some(port) => {
          let new_authority = Some({ userinfo: None, host: "", port: Some(port) })
          { ..uri, authority: new_authority }
        }
        None => uri
      }
    }
  }
}

/// Create a new URI with the specified path
pub fn with_path(uri : Uri, new_path : String) -> Uri {
  { ..uri, path: new_path }
}

/// Create a new URI with the specified query
pub fn with_query(uri : Uri, new_query : String?) -> Uri {
  { ..uri, query: new_query }
}

/// Create a new URI with the specified fragment
pub fn with_fragment(uri : Uri, new_fragment : String?) -> Uri {
  { ..uri, fragment: new_fragment }
}

/// Check if the URI is absolute (has a scheme)
pub fn is_absolute(uri : Uri) -> Bool {
  match uri.scheme {
    Some(_) => true
    None => false
  }
}

/// Check if the URI is relative (no scheme)
pub fn is_relative(uri : Uri) -> Bool {
  not(is_absolute(uri))
}