// SSG Configuration Parser
//

///|
/// Parse SSG config directly (sol.config.json format)
pub fn parse_ssg_config(json_str : String) -> SsgConfig? {
  try {
    let json = @json.parse(json_str.view())
    guard json is Object(ssg_obj) else { return None }
    parse_ssg_object(ssg_obj)
  } catch {
    _ => None
  }
}

///|
/// Parse SSG config from sol.config.json content
pub fn parse_ssg_from_sol_config(json_str : String) -> SsgConfig? {
  try {
    let json = @json.parse(json_str.view())
    guard json is Object(obj) else { return None }

    // Get ssg section if exists, otherwise use root object
    match obj.get("ssg") {
      Some(Object(ssg_obj)) => parse_ssg_object(ssg_obj)
      _ => parse_ssg_object(obj)
    }
  } catch {
    _ => None
  }
}

///|
/// Extract string with fallback key (supports both camelCase and snake_case)
fn extract_string_with_fallback(
  obj : Map[String, Json],
  key : String,
  fallback_key : String,
  default : String,
) -> String {
  match @json_utils.extract_string_opt(obj, key) {
    Some(v) => v
    None => @json_utils.extract_string(obj, fallback_key, default)
  }
}

///|
/// Extract bool with fallback key (supports both camelCase and snake_case)
fn extract_bool_with_fallback(
  obj : Map[String, Json],
  key : String,
  fallback_key : String,
  default : Bool,
) -> Bool {
  if obj.contains(key) {
    @json_utils.extract_bool(obj, key, default)
  } else {
    @json_utils.extract_bool(obj, fallback_key, default)
  }
}

///|
/// Parse SSG object (shared by both config formats)
fn parse_ssg_object(ssg_obj : Map[String, Json]) -> SsgConfig? {
  // Parse ssg fields using json_utils helpers
  // Support both camelCase and snake_case key names
  let docs_dir = extract_string_with_fallback(
    ssg_obj, "docs", "docs_dir", "docs",
  )
  let output_dir = extract_string_with_fallback(
    ssg_obj, "output", "out_dir", "dist",
  )
  let title = @json_utils.extract_string(ssg_obj, "title", "Documentation")
  let base_url = extract_string_with_fallback(ssg_obj, "base", "base_url", "/")

  // Parse nav items
  let nav = parse_nav_items(ssg_obj)

  // Parse sidebar config
  let sidebar = parse_sidebar_config(ssg_obj)

  // Parse theme
  let theme = parse_theme_config(ssg_obj)

  // Parse i18n config
  let i18n = parse_i18n_config(ssg_obj)

  // Parse exclude patterns
  let exclude = @json_utils.extract_string_array(ssg_obj, "exclude")

  // Parse islands config
  let islands_config = parse_islands_config(ssg_obj)

  // Parse navigation config
  let navigation = parse_navigation_config(ssg_obj)

  // Parse trailing slash config (default: true for SSG compatibility)
  // Support both camelCase and snake_case
  let trailing_slash = extract_bool_with_fallback(
    ssg_obj, "trailingSlash", "trailing_slash", true,
  )

  // Parse OGP config
  let ogp = parse_ogp_config(ssg_obj)
  let ogp_text = @json_utils.extract_string_opt(ssg_obj, "ogpText")

  // Parse custom snippets
  let head_snippets = @json_utils.extract_string_array(ssg_obj, "headSnippets")
  let body_snippets = @json_utils.extract_string_array(ssg_obj, "bodySnippets")
  let prod_body_snippets = @json_utils.extract_string_array(
    ssg_obj, "prodBodySnippets",
  )

  // Parse sanitize HTML option (default: false)
  let sanitize_html = @json_utils.extract_bool(ssg_obj, "sanitizeHtml", false)

  // Parse deploy target (default: static)
  let deploy_target = parse_deploy_target(ssg_obj)

  // Parse SPA routes for client-side routing fallback
  let spa_routes = @json_utils.extract_string_array(ssg_obj, "spaRoutes")

  // Parse custom components directory
  let components_dir = @json_utils.extract_string_opt(ssg_obj, "componentsDir")

  // Parse CSS utilities configuration
  let css_utilities = parse_css_utilities_config(ssg_obj)

  // Parse meta files configuration
  let meta_files = parse_meta_files_config(ssg_obj)
  Some(SsgConfig::{
    docs_dir,
    output_dir,
    title,
    base_url,
    nav,
    sidebar,
    islands: islands_config,
    theme,
    navigation,
    i18n,
    exclude,
    trailing_slash,
    ogp,
    ogp_text,
    head_snippets,
    body_snippets,
    prod_body_snippets,
    sanitize_html,
    deploy_target,
    spa_routes,
    components_dir,
    css_utilities,
    meta_files,
  })
}

///|
/// Parse deploy target from config
fn parse_deploy_target(obj : Map[String, Json]) -> DeployTarget {
  match obj.get("deploy") {
    Some(String(s)) =>
      if s == "cloudflare" {
        Cloudflare
      } else if s == "github-pages" || s == "github" {
        GithubPages
      } else if s == "vercel" {
        Vercel
      } else if s == "netlify" {
        Netlify
      } else if s == "deno" {
        Deno
      } else if s == "node" {
        Node
      } else {
        Static
      }
    _ => Static
  }
}

///|
/// Parse CSS utilities configuration
fn parse_css_utilities_config(obj : Map[String, Json]) -> CssUtilitiesConfig? {
  guard obj.get("cssUtilities") is Some(Object(css_obj)) else { return None }
  let enabled = @json_utils.extract_bool(css_obj, "enabled", true)
  let split = @json_utils.extract_bool(css_obj, "split", false)
  let inline_threshold = @json_utils.extract_int(
    css_obj, "inlineThreshold", 4096,
  )
  let source_dirs = @json_utils.extract_string_array(css_obj, "sourceDirs")
  Some(CssUtilitiesConfig::{ enabled, split, inline_threshold, source_dirs })
}

///|
/// Parse meta files configuration
fn parse_meta_files_config(obj : Map[String, Json]) -> MetaFilesConfig {
  guard obj.get("metaFiles") is Some(Object(meta_obj)) else {
    return MetaFilesConfig::default()
  }
  let sitemap = @json_utils.extract_bool(meta_obj, "sitemap", true)
  let feed = @json_utils.extract_bool(meta_obj, "feed", false)
  let llms_txt = extract_bool_with_fallback(
    meta_obj, "llmsTxt", "llms_txt", false,
  )
  MetaFilesConfig::{ sitemap, feed, llms_txt }
}

///|
/// Parse navigation items from config
fn parse_nav_items(obj : Map[String, Json]) -> Array[NavItem] {
  match obj.get("nav") {
    Some(Array(arr)) => {
      let result : Array[NavItem] = []
      for item in arr {
        if parse_nav_item(item) is Some(nav_item) {
          result.push(nav_item)
        }
      }
      result
    }
    _ => []
  }
}

///|
/// Parse single nav item
fn parse_nav_item(json : Json) -> NavItem? {
  guard json is Object(obj) else { return None }
  guard @json_utils.extract_string_opt(obj, "text") is Some(text) else {
    return None
  }
  let link = @json_utils.extract_string(obj, "link", "")
  let icon = @json_utils.extract_string_opt(obj, "icon")
  let items = match obj.get("items") {
    Some(Array(arr)) => {
      let result : Array[NavItem] = []
      for item in arr {
        if parse_nav_item(item) is Some(nav_item) {
          result.push(nav_item)
        }
      }
      result
    }
    _ => []
  }
  Some(NavItem::{ text, link, icon, items })
}

///|
/// Parse sidebar configuration
fn parse_sidebar_config(obj : Map[String, Json]) -> SidebarConfig {
  match obj.get("sidebar") {
    Some(String(s)) =>
      if s == "auto" {
        SidebarConfig::Auto
      } else {
        SidebarConfig::Auto
      }
    Some(Object(sidebar_obj)) => {
      // Manual sidebar with path-keyed groups
      let groups : Array[SidebarGroup] = []
      for key in sidebar_obj.keys() {
        if sidebar_obj.get(key) is Some(Array(arr)) {
          if parse_sidebar_group_from_array(key, arr) is Some(group) {
            groups.push(group)
          }
        }
      }
      SidebarConfig::Manual(groups)
    }
    Some(Array(arr)) => {
      // Direct array of sidebar groups
      let groups : Array[SidebarGroup] = []
      for item in arr {
        if parse_sidebar_group(item) is Some(group) {
          groups.push(group)
        }
      }
      SidebarConfig::Manual(groups)
    }
    _ => SidebarConfig::Auto
  }
}

///|
/// Parse sidebar group from path-keyed array
fn parse_sidebar_group_from_array(
  path : String,
  arr : Array[Json],
) -> SidebarGroup? {
  let items : Array[SidebarItem] = []
  for item in arr {
    if parse_sidebar_item(item) is Some(sidebar_item) {
      items.push(sidebar_item)
    }
  }
  Some(SidebarGroup::{ text: path, link: None, collapsed: false, items })
}

///|
/// Parse sidebar group
fn parse_sidebar_group(json : Json) -> SidebarGroup? {
  guard json is Object(obj) else { return None }
  let text = @json_utils.extract_string(obj, "text", "")
  let collapsed = @json_utils.extract_bool(obj, "collapsed", false)
  let items = match obj.get("items") {
    Some(Array(arr)) => {
      let result : Array[SidebarItem] = []
      for item in arr {
        if parse_sidebar_item(item) is Some(sidebar_item) {
          result.push(sidebar_item)
        }
      }
      result
    }
    _ => []
  }
  Some(SidebarGroup::{ text, link: None, collapsed, items })
}

///|
/// Parse sidebar item
fn parse_sidebar_item(json : Json) -> SidebarItem? {
  guard json is Object(obj) else { return None }
  // Check if it's a group (has items) or a link
  if obj.get("items") is Some(Array(_)) {
    // It's a nested group
    if parse_sidebar_group(json) is Some(group) {
      return Some(SidebarItem::Group(group))
    }
    return None
  }
  // It's a link
  guard @json_utils.extract_string_opt(obj, "text") is Some(text) else {
    return None
  }
  guard @json_utils.extract_string_opt(obj, "link") is Some(link) else {
    return None
  }
  Some(SidebarItem::Link(text~, link~))
}

///|
/// Extract color option with camelCase and snake_case support
fn extract_color_opt(
  obj : Map[String, Json],
  camel_key : String,
  snake_key : String,
) -> String? {
  match @json_utils.extract_string_opt(obj, camel_key) {
    Some(v) => Some(v)
    None => @json_utils.extract_string_opt(obj, snake_key)
  }
}

///|
/// Parse theme configuration
fn parse_theme_config(obj : Map[String, Json]) -> ThemeConfig {
  guard @json_utils.extract_object(obj, "theme") is Some(theme_obj) else {
    return ThemeConfig::default()
  }

  // Core colors (support both camelCase and snake_case)
  let primary_color = extract_color_opt(
    theme_obj, "primaryColor", "primary_color",
  )
  let secondary_color = extract_color_opt(
    theme_obj, "secondaryColor", "secondary_color",
  )
  let accent_color = extract_color_opt(theme_obj, "accentColor", "accent_color")

  // Semantic colors
  let success_color = extract_color_opt(
    theme_obj, "successColor", "success_color",
  )
  let warning_color = extract_color_opt(
    theme_obj, "warningColor", "warning_color",
  )
  let error_color = extract_color_opt(theme_obj, "errorColor", "error_color")

  // Text colors
  let text_color = extract_color_opt(theme_obj, "textColor", "text_color")
  let text_muted_color = extract_color_opt(
    theme_obj, "textMutedColor", "text_muted_color",
  )
  let heading_color = extract_color_opt(
    theme_obj, "headingColor", "heading_color",
  )
  let link_color = extract_color_opt(theme_obj, "linkColor", "link_color")
  let link_hover_color = extract_color_opt(
    theme_obj, "linkHoverColor", "link_hover_color",
  )

  // Background colors
  let background_color = extract_color_opt(
    theme_obj, "backgroundColor", "background_color",
  )
  let surface_color = extract_color_opt(
    theme_obj, "surfaceColor", "surface_color",
  )
  let sidebar_bg_color = extract_color_opt(
    theme_obj, "sidebarBgColor", "sidebar_bg_color",
  )
  let input_bg_color = extract_color_opt(
    theme_obj, "inputBgColor", "input_bg_color",
  )
  let code_bg_color = extract_color_opt(
    theme_obj, "codeBgColor", "code_bg_color",
  )

  // UI colors
  let border_color = extract_color_opt(theme_obj, "borderColor", "border_color")
  let focus_ring_color = extract_color_opt(
    theme_obj, "focusRingColor", "focus_ring_color",
  )

  // Non-color settings
  let logo = @json_utils.extract_string_opt(theme_obj, "logo")
  let header = match @json_utils.extract_object(theme_obj, "header") {
    Some(header_obj) => Some(parse_header_config(header_obj))
    None => None
  }
  let footer = match @json_utils.extract_object(theme_obj, "footer") {
    Some(footer_obj) => {
      let message = @json_utils.extract_string_opt(footer_obj, "message")
      let copyright = @json_utils.extract_string_opt(footer_obj, "copyright")
      let links = match footer_obj.get("links") {
        Some(Array(arr)) => parse_footer_columns(arr)
        _ => []
      }
      let top = match footer_obj.get("top") {
        Some(Array(arr)) => parse_footer_elements(arr)
        _ => []
      }
      let bottom = match footer_obj.get("bottom") {
        Some(Array(arr)) => parse_footer_elements(arr)
        _ => []
      }
      Some(FooterConfig::{ message, copyright, links, top, bottom })
    }
    None => None
  }
  let social_links = match theme_obj.get("socialLinks") {
    Some(Array(arr)) => parse_social_links(arr)
    _ => []
  }
  ThemeConfig::{
    // Core colors
    primary_color,
    secondary_color,
    accent_color,
    // Semantic colors
    success_color,
    warning_color,
    error_color,
    // Text colors
    text_color,
    text_muted_color,
    heading_color,
    link_color,
    link_hover_color,
    // Background colors
    background_color,
    surface_color,
    sidebar_bg_color,
    input_bg_color,
    code_bg_color,
    // UI colors
    border_color,
    focus_ring_color,
    // Non-color settings
    logo,
    header,
    footer,
    social_links,
  }
}

///|
/// Parse header configuration
fn parse_header_config(obj : Map[String, Json]) -> HeaderConfig {
  let left = match obj.get("left") {
    Some(Array(arr)) => parse_header_elements(arr)
    _ => [Logo]
  }
  let center = match obj.get("center") {
    Some(Array(arr)) => parse_header_elements(arr)
    _ => []
  }
  let right = match obj.get("right") {
    Some(Array(arr)) => parse_header_elements(arr)
    _ => [NavLinks, LangSwitcher, SocialLinks, ThemeToggle]
  }
  let sticky = match obj.get("sticky") {
    Some(True) => true
    _ => false
  }
  HeaderConfig::{ left, center, right, sticky }
}

///|
/// Parse header elements array
fn parse_header_elements(arr : Array[Json]) -> Array[HeaderElement] {
  let result : Array[HeaderElement] = []
  for item in arr {
    match item {
      String(s) =>
        match s {
          "logo" => result.push(Logo)
          "nav-links" => result.push(NavLinks)
          "search" => result.push(Search)
          "theme-toggle" => result.push(ThemeToggle)
          "social-links" => result.push(SocialLinks)
          "lang-switcher" => result.push(LangSwitcher)
          "spacer" => result.push(Spacer)
          _ =>
            // Treat unknown strings as custom component tags
            if s.contains("-") {
              result.push(Custom(s))
            }
        }
      _ => ()
    }
  }
  result
}

///|
/// Parse footer elements array
fn parse_footer_elements(arr : Array[Json]) -> Array[FooterElement] {
  let result : Array[FooterElement] = []
  for item in arr {
    match item {
      String(s) =>
        match s {
          "columns" => result.push(FooterElement::Columns)
          "message" => result.push(FooterElement::Message)
          "copyright" => result.push(FooterElement::Copyright)
          "social-links" => result.push(FooterElement::SocialLinks)
          _ =>
            // Treat unknown strings as custom component tags
            if s.contains("-") {
              result.push(FooterElement::Custom(s))
            }
        }
      _ => ()
    }
  }
  result
}

///|
/// Parse social links array
fn parse_social_links(arr : Array[Json]) -> Array[SocialLink] {
  let result : Array[SocialLink] = []
  for item in arr {
    guard item is Object(obj) else { continue }
    guard @json_utils.extract_string_opt(obj, "icon") is Some(icon) else {
      continue
    }
    guard @json_utils.extract_string_opt(obj, "link") is Some(link) else {
      continue
    }
    result.push(SocialLink::{ icon, link })
  }
  result
}

///|
/// Parse footer columns array
fn parse_footer_columns(arr : Array[Json]) -> Array[FooterColumn] {
  let result : Array[FooterColumn] = []
  for item in arr {
    guard item is Object(obj) else { continue }
    guard @json_utils.extract_string_opt(obj, "title") is Some(title) else {
      continue
    }
    let items = match obj.get("items") {
      Some(Array(links_arr)) => parse_footer_links(links_arr)
      _ => []
    }
    result.push(FooterColumn::{ title, items })
  }
  result
}

///|
/// Parse footer links array
fn parse_footer_links(arr : Array[Json]) -> Array[FooterLink] {
  let result : Array[FooterLink] = []
  for item in arr {
    guard item is Object(obj) else { continue }
    guard @json_utils.extract_string_opt(obj, "label") is Some(label) else {
      continue
    }
    guard @json_utils.extract_string_opt(obj, "href") is Some(href) else {
      continue
    }
    result.push(FooterLink::{ label, href })
  }
  result
}

///|
/// Parse i18n configuration
fn parse_i18n_config(obj : Map[String, Json]) -> I18nConfig {
  guard @json_utils.extract_object(obj, "i18n") is Some(i18n_obj) else {
    return I18nConfig::default()
  }
  let default_locale = @json_utils.extract_string(
    i18n_obj, "defaultLocale", "en",
  )
  let locales = match i18n_obj.get("locales") {
    Some(Array(arr)) => parse_locale_configs(arr)
    _ => [LocaleConfig::{ code: "en", label: "English", path: "" }]
  }
  I18nConfig::{ default_locale, locales }
}

///|
/// Parse locale configurations
fn parse_locale_configs(arr : Array[Json]) -> Array[LocaleConfig] {
  let result : Array[LocaleConfig] = []
  for item in arr {
    if parse_locale_config(item) is Some(locale) {
      result.push(locale)
    }
  }
  result
}

///|
/// Parse single locale configuration
fn parse_locale_config(json : Json) -> LocaleConfig? {
  guard json is Object(obj) else { return None }
  guard @json_utils.extract_string_opt(obj, "code") is Some(code) else {
    return None
  }
  let label = @json_utils.extract_string(obj, "label", code)
  let path = match @json_utils.extract_string_opt(obj, "path") {
    Some(p) => p
    None => if code == "en" { "" } else { code }
  }
  Some(LocaleConfig::{ code, label, path })
}

///|
/// Parse navigation configuration
fn parse_navigation_config(obj : Map[String, Json]) -> NavigationConfig {
  guard @json_utils.extract_object(obj, "navigation") is Some(nav_obj) else {
    return NavigationConfig::default()
  }
  let spa = @json_utils.extract_bool(nav_obj, "spa", false)
  let view_transitions = @json_utils.extract_bool(
    nav_obj, "viewTransitions", true,
  )
  let keyboard = @json_utils.extract_bool(nav_obj, "keyboard", true)
  NavigationConfig::{ spa, view_transitions, keyboard }
}

///|
/// Parse OGP configuration
fn parse_ogp_config(obj : Map[String, Json]) -> OgpConfig {
  guard @json_utils.extract_object(obj, "ogp") is Some(ogp_obj) else {
    return OgpConfig::default()
  }
  let site_url = @json_utils.extract_string_opt(ogp_obj, "siteUrl")
  let image = @json_utils.extract_string_opt(ogp_obj, "image")
  let twitter_handle = @json_utils.extract_string_opt(ogp_obj, "twitterHandle")
  let twitter_card = @json_utils.extract_string_opt(ogp_obj, "twitterCard")
  OgpConfig::{ site_url, image, twitter_handle, twitter_card }
}

///|
/// Parse Islands configuration
fn parse_islands_config(obj : Map[String, Json]) -> IslandsConfig? {
  guard @json_utils.extract_object(obj, "islands") is Some(islands_obj) else {
    return None
  }
  let dir = @json_utils.extract_string(islands_obj, "dir", "islands")
  let base_path = @json_utils.extract_string(
    islands_obj, "basePath", "/islands/",
  )
  Some(IslandsConfig::{ dir, base_path })
}