///|
/// CORS middleware with custom configuration.
///
/// Standard `cors` middleware.
///
/// Usage:
/// ```
/// app.use(cors(CORSConfig::default()))
/// ```

///|
/// CORS configuration with dynamic origin support.
pub(all) struct CORSConfig {
  /// Static list of allowed origins. Supports `*` wildcard.
  allow_origins : Array[String]
  /// Dynamic origin validator — called for each request when set.
  /// When set (non-None return), `allow_origins` is ignored for validation.
  /// Returns true if origin is allowed, false if denied, None to fall back.
  allow_origin_fn : (String) -> Bool?
  allow_methods : Array[String]
  allow_headers : Array[String]
  expose_headers : Array[String]
  allow_credentials : Bool
  max_age : Int
  /// Whether to add the Vary: Origin header
  vary_origin : Bool
  /// Whether to allow private network access (PNA) headers
  allow_private_network : Bool
}

///|
/// Default CORS config (permissive).

pub fn CORSConfig::default() -> CORSConfig {
  {
    allow_origins: ["*"],
    allow_origin_fn: fn(_) { None },
    allow_methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
    allow_headers: ["Origin", "Content-Type", "Accept", "Authorization"],
    expose_headers: ["Content-Length"],
    allow_credentials: false,
    max_age: 86400,
    vary_origin: true,
    allow_private_network: false,
  }
}

///|
/// Create a CORS config with a dynamic origin validator.
/// The validator function receives the Origin header and returns true if allowed.
///
/// ```
/// let cfg = CORSConfig::with_origin_fn(fn(origin) {
///   origin.has_suffix(".mydomain.com") || origin == "https://app.mydomain.com"
/// })
/// app.use(cors(cfg))
/// ```
pub fn CORSConfig::with_origin_fn(
  allow_origin_fn : (String) -> Bool,
) -> CORSConfig {
  {
    allow_origins: ["*"],
    allow_origin_fn: fn(origin) { Some(allow_origin_fn(origin)) },
    allow_methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
    allow_headers: ["Origin", "Content-Type", "Accept", "Authorization"],
    expose_headers: ["Content-Length"],
    allow_credentials: false,
    max_age: 86400,
    vary_origin: true,
    allow_private_network: false,
  }
}

///|
/// CORS middleware with custom configuration.
pub fn cors(config : CORSConfig) -> Handler {
  async fn(ctx) {
    let origin = match ctx.header("Origin") {
      Some(o) => o
      None => {
        // No Origin header — pass through for same-origin requests
        ctx.next()
        return
      }
    }

    // Determine if origin is allowed
    let dynamic_result = (config.allow_origin_fn)(origin)
    let allowed = match dynamic_result {
      Some(result) => result
      None => {
        // Fall back to static origins list
        let mut matched = false
        for o in config.allow_origins {
          if o == "*" || o == origin {
            matched = true
            break
          }
        }
        matched
      }
    }

    if allowed {
      // For credentialed requests, must echo back exact origin (not *)
      let use_wildcard = config.allow_origins.length() == 1 && config.allow_origins[0] == "*"
      let response_origin = if use_wildcard && !config.allow_credentials {
        "*"
      } else {
        origin
      }
      ctx.set_header("Access-Control-Allow-Origin", response_origin)

      if config.vary_origin {
        ctx.set_header("Vary", "Origin")
      }

      ctx.set_header(
        "Access-Control-Allow-Methods",
        join_strings(config.allow_methods, ", "),
      )
      ctx.set_header(
        "Access-Control-Allow-Headers",
        join_strings(config.allow_headers, ", "),
      )
      ctx.set_header(
        "Access-Control-Expose-Headers",
        join_strings(config.expose_headers, ", "),
      )

      if config.allow_credentials {
        ctx.set_header("Access-Control-Allow-Credentials", "true")
      }
      if config.max_age > 0 {
        ctx.set_header("Access-Control-Max-Age", config.max_age.to_string())
      }
      if config.allow_private_network {
        ctx.set_header("Access-Control-Allow-Private-Network", "true")
      }

      // Handle preflight
      if ctx.http_method() == "OPTIONS" {
        ctx.no_content()
        return
      }

      ctx.next()
    } else {
      // Origin not allowed — still pass through but without CORS headers
      ctx.next()
    }
  }
}