///|
/// What a connection knows about itself, carried to every scope made on it: the scheme it
/// arrived under, the two addresses, and its TLS facts when it is TLS.
priv struct Link {
  scheme : String
  bound : @socket.Addr?
  peer : @socket.Addr?
  tls : @moonasgi.TlsExtension?
  // Whether the request reached this server before its handshake finished, which is what makes
  // answering it twice different from answering it once (RFC 8470).
  early : Bool
  // The lifespan state, of which every scope gets a shallow copy (ASGI lifespan sub-spec).
  state : Map[String, Json]
}

///|
/// A connection in the clear with nothing known about either end.
fn Link::plain() -> Link {
  {
    scheme: "http",
    bound: None,
    peer: None,
    tls: None,
    early: false,
    state: {},
  }
}

///|
/// The ASGI TLS extension for a connection: the version, the suite, and the certificate the
/// server presented, PEM as the extension asks.
fn tls_facts(conn : @tcp.Conn, chain : Array[Bytes]) -> @moonasgi.TlsExtension? {
  guard conn.tls() is Some(t) else { return None }
  Some({
    server_cert: if chain.length() > 0 {
      Some(pem(chain[0]))
    } else {
      None
    },
    client_cert_chain: [],
    client_cert_name: None,
    client_cert_error: None,
    tls_version: Some(0x0304),
    cipher_suite: t.suite().map(s => s.code()),
  })
}

///|
fn pem(der : Bytes) -> String {
  let text = @base64.encode(der[:])
  let out = StringBuilder()
  out.write_string("-----BEGIN CERTIFICATE-----\n")
  for i = 0; i < text.length(); i = i + 64 {
    let end = if i + 64 < text.length() { i + 64 } else { text.length() }
    out.write_string(text[i:end].to_owned())
    out.write_string("\n")
  }
  out.write_string("-----END CERTIFICATE-----\n")
  out.to_string()
}

///|
/// The extensions an HTTP scope here advertises: 103 Early Hints and response trailers, which
/// both versions honour, the TLS facts on a TLS connection, and โ€” on a request that arrived in
/// 0-RTT โ€” the `http.request.early_data` extension, whose value is the same `true` the header
/// says in prose.
fn http_extensions(link : Link) -> @moonasgi.Extensions {
  let base = @moonasgi.Extensions::none().enable_early_hint().enable_trailers()
  let base = match link.tls {
    Some(t) => base.with_tls(t)
    None => base
  }
  if link.early {
    base.enable("http.request.early_data", value=Json::boolean(true))
  } else {
    base
  }
}

///|
/// The request header that says a request arrived in 0-RTT (RFC 8470 ยง5.1). A proxy adds it to
/// what it forwards; an origin adds it to what it hands its application, which is the same thing
/// one hop later: whoever answers must know it may be answering a replay.
let early_data_header : String = "early-data"

///|
/// Whether this scope's request may be a replay: it arrived in 0-RTT, before the handshake that
/// would have proved the client owns its address (RFC 8470). A request forwarded by a proxy that
/// took it in 0-RTT says so with `Early-Data: 1`, so both are read.
pub fn early_data(scope : @moonasgi.Scope) -> Bool {
  match scope {
    Http(http) =>
      http.headers.any(h => {
        h.0.to_lower() == early_data_header &&
        h.1.trim(chars=" ").to_lower() == "1"
      })
    _ => false
  }
}

///|
/// The extensions a WebSocket scope here advertises: the TLS facts on a TLS connection.
fn ws_extensions(link : Link) -> @moonasgi.Extensions {
  match link.tls {
    Some(t) => @moonasgi.Extensions::none().with_tls(t)
    None => @moonasgi.Extensions::none()
  }
}

///|
/// The client and the scheme a request is to be seen from: the connection's, or what trusted
/// proxy headers say (โ† uvicorn's `--proxy-headers`).
fn seen_from(
  link : Link,
  config : @config.Config,
  headers : Array[(String, String)],
  scheme : String,
  websocket~ : Bool = false,
) -> ((String, Int32)?, String) {
  let peer = link.peer.map(addr_pair)
  guard config.proxy_headers else { return (peer, scheme) }
  let map : Map[String, String] = Map([])
  for h in headers {
    map[h.0.to_lower()] = h.1
  }
  @proxy.proxy_rewrite(
    map,
    peer,
    scheme,
    trusted=config.forwarded_allow_ips,
    websocket~,
  )
}

///|
/// An HTTP scope, the same for HTTP/1.1 and HTTP/2: the query split off, the path
/// percent-decoded beside the raw one (a router must tell `%2F` from `/`), the client and
/// scheme as `seen_from` gives them, the root path, the addresses and the extensions.
fn http_scope(
  link : Link,
  config : @config.Config,
  version : String,
  verb : String,
  target : String,
  headers : Array[(String, String)],
) -> @moonasgi.Scope {
  let (raw_path, query) = split_query(target)
  let (client, scheme) = seen_from(link, config, headers, link.scheme)
  Http({
    http_version: version,
    http_method: verb,
    scheme,
    path: @utf8.decode_lossy(percent_decode(raw_path)),
    raw_path: Some(@utf8.encode(raw_path)),
    query_string: @utf8.encode(query),
    root_path: config.root_path,
    headers: if link.early &&
      !headers.any(h => h.0.to_lower() == early_data_header) {
      // The application reads the request it was handed; the fact has to be in there, not only
      // beside it, because that is where RFC 8470 puts it.
      headers + [(early_data_header, "1")]
    } else {
      headers
    },
    client,
    server: server_pair(link.bound),
    asgi: @moonasgi.AsgiVersion::http(),
    extensions: http_extensions(link),
    state: link.state.copy(),
  })
}