// Sol ISR Handler
//
// Handles ISR (Incremental Static Regeneration) for Sol server.
// Loads ISR manifest and applies cache logic for marked pages.
//
// The ISRManifest / ISRPageMeta types live in `mizchi/astra/isr` so the
// build side (astra/render) and the runtime side (sol/router) share one
// definition. They are re-exported here for back-compat with sol consumers.

///|
pub typealias @astra_isr.ISRPageMeta as ISRPageMeta

///|
pub typealias @astra_isr.ISRManifest as ISRManifest

// =============================================================================
// ISR Handler
// =============================================================================

///|
/// ISR handler that wraps cache and manifest
pub struct ISRHandler[Cache] {
  cache : Cache
  manifest : ISRManifest
  dist_dir : String
}

///|
/// Create new ISR handler
pub fn[Cache] ISRHandler::new(
  cache : Cache,
  manifest : ISRManifest,
  dist_dir : String,
) -> ISRHandler[Cache] {
  { cache, manifest, dist_dir }
}

///|
/// Handle ISR request
/// Returns (html, needs_revalidation)
pub async fn[Cache : ISRCache] ISRHandler::handle(
  self : ISRHandler[Cache],
  path : String,
) -> (String?, Bool) {
  // Check if this is an ISR page
  let page_meta = match self.manifest.get_page(path) {
    Some(meta) => meta
    None => return (None, false) // Not an ISR page
  }

  // Generate cache key
  let key = cache_key(path, [])

  // Check cache status
  let entry = self.cache.get(key)
  let now = now_ms()
  let status = check_status(entry, now)
  match status {
    Fresh =>
      // Serve from cache
      match entry {
        Some(e) => (Some(e.html), false)
        None => (None, false)
      }
    Stale =>
      // Serve stale content, schedule revalidation
      match entry {
        Some(e) => (Some(e.html), true)
        None => (None, true)
      }
    Miss => {
      // Try to read from static file
      let html = read_static_page(self.dist_dir, path)
      match html {
        Some(content) => {
          // Cache it
          let new_entry = CacheEntry::new(content, now, page_meta.revalidate)
          self.cache.put(key, new_entry)
          (Some(content), false)
        }
        None => (None, true) // Need to generate
      }
    }
  }
}

///|
/// Update cache after revalidation
pub fn[Cache : ISRCache] ISRHandler::update_cache(
  self : ISRHandler[Cache],
  path : String,
  html : String,
) -> Unit {
  let page_meta = match self.manifest.get_page(path) {
    Some(meta) => meta
    None => return
  }
  let key = cache_key(path, [])
  let entry = CacheEntry::new(html, now_ms(), page_meta.revalidate)
  self.cache.put(key, entry)
}

// =============================================================================
// File Reading (for initial cache population)
// =============================================================================

///|
/// Read static HTML file from dist directory
async fn read_static_page(dist_dir : String, path : String) -> String? {
  // Convert URL path to file path
  let file_path = if path.has_suffix("/") {
    dist_dir + path + "index.html"
  } else {
    dist_dir + path + "/index.html"
  }
  let data : &@io.Data = @x_fs.read_file(file_path) catch { _ => return None }
  let content = data.text() catch { _ => return None }
  Some(content)
}

// =============================================================================
// Manifest Loading
// =============================================================================

///|
/// Load ISR manifest from _luna/isr.json
pub async fn load_manifest(dist_dir : String) -> ISRManifest {
  let manifest_path = dist_dir + "/_luna/isr.json"
  let data : &@io.Data = @x_fs.read_file(manifest_path) catch {
    _ => return ISRManifest::empty()
  }
  let content = data.text() catch { _ => return ISRManifest::empty() }
  parse_manifest(content)
}

///|
/// Parse ISR manifest from JSON string
fn parse_manifest(content : String) -> ISRManifest {
  let json : Json = @json.parse(content) catch {
    _ => return ISRManifest::empty()
  }
  let obj = match json {
    Object(o) => o
    _ => return ISRManifest::empty()
  }
  let version = match obj.get("version") {
    Some(Number(n, ..)) => n.to_int()
    _ => 1
  }
  let pages : Map[String, ISRPageMeta] = {}
  match obj.get("pages") {
    Some(Object(pages_obj)) =>
      for path, page_json in pages_obj {
        match parse_page_meta(page_json) {
          Some(meta) => pages[path] = meta
          None => ()
        }
      }
    _ => ()
  }
  { version, pages }
}

///|
fn parse_page_meta(json : Json) -> ISRPageMeta? {
  let obj = match json {
    Object(o) => o
    _ => return None
  }
  let revalidate = match obj.get("revalidate") {
    Some(Number(n, ..)) => n.to_int()
    _ => return None
  }
  let renderer = match obj.get("renderer") {
    Some(String(s)) => s
    _ => "markdown"
  }
  let source = match obj.get("source") {
    Some(String(s)) => s
    _ => ""
  }
  Some({ revalidate, renderer, source })
}