/// The resource template component mirroring fastmcp/resources/template.py.
///
/// Python's function-introspection machinery (`FunctionResourceTemplate`)
/// maps to a template carrying a read closure plus the JSON schema of its
/// parameters, supplied at registration time.

///|
/// A template for dynamically creating resources.
pub(all) struct ResourceTemplate {
  component : @component.ComponentBase
  uri_template : String
  mime_type : String
  /// JSON schema for the read function's parameters.
  parameters : Json
  annotations : @types.Annotations?
  security : ResourceSecuritySetting
  /// Non-exploded `{?name}` query parameters whose value is a list.
  list_query_params : @set.Set[String]
  segments : Array[TemplateSegment]
  read_fn : async (Map[String, Json]) -> RawResourceValue raise @exceptions.FastMCPError
}

///|
pub impl @component.Component for ResourceTemplate with fn base(self) {
  self.component
}

///|
pub impl @component.Component for ResourceTemplate with fn identifier(self) {
  self.uri_template
}

///|
pub impl @component.Component for ResourceTemplate with fn key(self) {
  let base_key = @component.ComponentBase::make_key(
    self.component.key_prefix,
    self.uri_template,
  )
  "\{base_key}@\{self.component.version.unwrap_or("")}"
}

///|
pub impl @component.Component for ResourceTemplate with fn get_meta(self) {
  self.component.get_meta()
}

///|
/// Resolve the effective security policy for this template.
pub fn ResourceTemplate::resolve_security(
  self : ResourceTemplate,
  server_default : ResourceSecurity?,
) -> ResourceSecurity? {
  resolve_security(self.security, server_default)
}

///|
/// Register a resource template; `read_fn` receives the parameters matched
/// from the request URI.
pub fn ResourceTemplate::new(
  uri_template : String,
  parameters~ : Json,
  read_fn~ : async (Map[String, Json]) -> RawResourceValue raise @exceptions.FastMCPError,
  name? : String,
  version? : String,
  title? : String,
  description? : String,
  icons? : Array[@types.Icon],
  mime_type~ : String = "text/plain",
  tags~ : @set.Set[String] = @set.Set::from_array([]),
  meta? : Map[String, Json],
  annotations? : @types.Annotations,
  security~ : ResourceSecuritySetting = Inherit,
  list_query_params~ : @set.Set[String] = @set.Set::from_array([]),
) -> ResourceTemplate raise @exceptions.FastMCPError {
  let all_params = parse_template_params(uri_template)
  if all_params.is_empty() {
    raise @exceptions.Validation(
      "URI template must contain at least one parameter",
    )
  }
  {
    component: @component.ComponentBase::new(
      "template",
      name.unwrap_or(uri_template),
      version?,
      title?,
      description?,
      icons?,
      tags~,
      meta?,
    ),
    uri_template,
    mime_type,
    parameters,
    annotations,
    security,
    list_query_params,
    segments: parse_template(uri_template),
    read_fn,
  }
}

///|
/// All parameter names a template declares, hyphens normalised to
/// underscores, path and query parameters combined.
pub fn parse_template_params(uri_template : String) -> @set.Set[String] {
  let names : Array[String] = []
  for name in extract_query_params(uri_template) {
    names.push(name.replace(old="-", new="_"))
  }
  for segment in parse_template(uri_template) {
    match segment {
      PathVar(name~, ..) => names.push(name.replace(old="-", new="_"))
      Literal(_) => ()
    }
  }
  @set.Set::from_array(names)
}

///|
/// Check if a URI matches this template and extract its parameters,
/// screening extracted values against the effective security policy.
pub fn ResourceTemplate::matches(
  self : ResourceTemplate,
  uri : String,
  security : ResourceSecurity?,
) -> Map[String, Json]? raise @exceptions.FastMCPError {
  let list_params : Array[String] = if uri.contains("?") {
    self.list_query_params.to_array()
  } else {
    []
  }
  guard match_uri_template(
      uri,
      self.uri_template,
      list_params~,
      segments=self.segments,
    )
    is Some(params) else {
    return None
  }
  match security {
    Some(policy) =>
      match policy.validate(params) {
        Some(_) =>
          // Surfaced as "not found" so a traversal attempt is
          // indistinguishable from a request for a missing resource.
          raise @exceptions.ResourceSecurity("Blocked by resource security")
        None => Some(params)
      }
    None => Some(params)
  }
}

///|
/// Read the resource content with the matched parameters.
pub async fn ResourceTemplate::read(
  self : ResourceTemplate,
  arguments : Map[String, Json],
) -> RawResourceValue raise @exceptions.FastMCPError {
  (self.read_fn)(arguments)
}

///|
/// Convert a raw result to a `ResourceResult`, forwarding the template's
/// declared MIME type so reads match what it advertises in listings.
pub fn ResourceTemplate::convert_result(
  self : ResourceTemplate,
  raw_value : RawResourceValue,
) -> ResourceResult {
  convert_raw_to_resource_result(
    raw_value,
    mime_type=self.mime_type,
    meta?=self.component.meta,
  )
}

///|
/// Server entry point for template reads.
pub async fn ResourceTemplate::read_result(
  self : ResourceTemplate,
  _uri : String,
  params : Map[String, Json],
) -> ResourceResult raise @exceptions.FastMCPError {
  self.convert_result(self.read(params))
}

///|
/// Create a concrete resource from the template for the given parameters.
pub fn ResourceTemplate::create_resource(
  self : ResourceTemplate,
  uri : String,
  params : Map[String, Json],
) -> Resource {
  let read_fn = self.read_fn
  let captured = params.copy()
  let name = self.component.name
  let description = self.component.description
  let mime_type = self.mime_type
  let tags = self.component.tags
  let meta = self.component.meta
  let title = self.component.title
  let icons = self.component.icons
  let annotations = self.annotations
  Resource::new(
    uri,
    read_fn=() => read_fn(captured),
    name~,
    description?,
    mime_type~,
    tags~,
    meta?,
    title?,
    icons?,
    annotations?,
  )
}

///|
/// Convert the template to its wire representation.
pub fn ResourceTemplate::to_mcp_template(
  self : ResourceTemplate,
) -> @types.ResourceTemplate {
  {
    uri_template: self.uri_template,
    name: self.component.name,
    title: self.component.title,
    description: self.component.description,
    mime_type: Some(self.mime_type),
    annotations: self.annotations,
    meta: Some(ResourceTemplate::get_meta(self)),
  }
}

///|
/// Create a template from a raw wire template, for remote servers whose
/// original function is lost.
pub fn ResourceTemplate::from_mcp_template(
  mcp_template : @types.ResourceTemplate,
) -> ResourceTemplate raise @exceptions.FastMCPError {
  ResourceTemplate::new(
    mcp_template.uri_template,
    parameters=Json::object({}),
    read_fn=_ => {
      raise @exceptions.Resource("Remote templates cannot be read locally")
    },
    name=mcp_template.name,
    description?=mcp_template.description,
    mime_type=mcp_template.mime_type.unwrap_or("text/plain"),
  )
}

///|
pub extend ResourceTemplate with @component.Component::{
  base,
  identifier,
  key,
  get_meta,
}