import { connectorFetch } from "std::data/connector"
import { keys } from "std::object"

/** @module
  @summary Browse the Y Combinator company directory by batch, industry, or tag.
  ## Y Combinator — the YC company directory

  Query the [yc-oss](https://github.com/yc-oss/api) community dataset by
  batch, industry, tag, or curated list. Company profiles include their
  descriptions, funding batch, team size, and status.

  The data is static JSON refreshed daily, and there is no server-side search. Fetch a slice (a
  batch, industry, or tag) and filter in Agency. `ycMeta` lists the available slugs, so an agent
  can discover the newest batch before fetching it. No API key required.

  ### Usage

  ```ts
  import { ycBatch } from "std::data/tech/yc"

  node main() {
    const companies = ycBatch("Winter 2025") catch []
    for (c in companies) {
      print("${c.name} — ${c.oneLiner} [${c.status}]")
    }
  }
  ```
*/

effect std::yc { op: string, query: string }

static const YC_BASE = "https://yc-oss.github.io/api"
static const YC_DOMAINS = ["yc-oss.github.io"]

// The curated company lists yc-oss serves at companies/<name>.json.
static const YC_LISTS = ["top", "all", "hiring", "nonprofit", "women-founded", "black-founded", "hispanic-latino-founded"]

/** A YC company profile, with `launchedAt` in Unix seconds. */
export type Company = {
  id: number;
  name: string;
  slug: string;
  website: string;
  oneLiner: string;
  longDescription: string;
  batch: string;
  status: string;
  stage: string;
  teamSize: number;
  industry: string;
  subindustry: string;
  tags: string[];
  regions: string[];
  allLocations: string;
  launchedAt: number;
  topCompany: boolean;
  isHiring: boolean;
  nonprofit: boolean;
  url: string
}

/** The available slugs for discovery (from meta.json), so a caller can find the newest batch. */
export type YcMeta = {
  lastUpdated: string;
  batches: string[];
  industries: string[];
  tags: string[]
}

/* Normalize a human label to a yc-oss slug: lowercase, trim, spaces/underscores → hyphens.
    So "Winter 2025" → "winter-2025". */
def slugify(s: string): string {
  return s.toLowerCase().trim().replaceAll(" ", "-").replaceAll("_", "-")
}

/* The curated-list names as a comma-separated string. Single source of truth for error messages. */
def ycListsList(): string {
  return YC_LISTS.join(", ")
}

/* Validate a curated-list name (lenient: slugified first). Unknown → failure listing valid names. */
def parseListName(list: string): Result<string> {
  const slug = slugify(list)
  if (YC_LISTS.indexOf(slug) < 0) {
    return failure("unknown list '${list}'; valid: ${ycListsList()}")
  }
  return success(slug)
}

/* Build the batch-slice path (caller slugifies). */
def buildBatchPath(slug: string): string {
  return "/batches/${slug}.json"
}

/* Build the industry-slice path. */
def buildIndustryPath(slug: string): string {
  return "/industries/${slug}.json"
}

/* Build the tag-slice path. */
def buildTagPath(slug: string): string {
  return "/tags/${slug}.json"
}

/* Build the curated-list path (name already validated by parseListName). */
def buildListPath(name: string): string {
  return "/companies/${name}.json"
}

/* Build the metadata path. */
def buildMetaPath(): string {
  return "/meta.json"
}

/* Reshape a yc-oss company array into Company[]. A slice body is a bare JSON array. */
def parseCompanies(raw: any): Company[] {
  const arr = raw ?? []
  return map(arr) as c {
    const co: any = c ?? {}
    return {
      id: co.id ?? 0,
      name: co.name ?? "",
      slug: co.slug ?? "",
      website: co.website ?? "",
      oneLiner: co.one_liner ?? "",
      longDescription: co.long_description ?? "",
      batch: co.batch ?? "",
      status: co.status ?? "",
      stage: co.stage ?? "",
      teamSize: co.team_size ?? 0,
      industry: co.industry ?? "",
      subindustry: co.subindustry ?? "",
      tags: co.tags ?? [],
      regions: co.regions ?? [],
      allLocations: co.all_locations ?? "",
      launchedAt: co.launched_at ?? 0,
      topCompany: co.top_company ?? false,
      isHiring: co.isHiring ?? false,
      nonprofit: co.nonprofit ?? false,
      url: co.url ?? ""
    }
  }
}

/* Reshape meta.json into YcMeta. batches/industries/tags are slug-keyed objects → take the keys.
    */
def parseMeta(raw: any): YcMeta {
  const r: any = raw ?? {}
  return {
    lastUpdated: r.last_updated ?? "",
    batches: keys(r.batches ?? {}),
    industries: keys(r.industries ?? {}),
    tags: keys(r.tags ?? {})
  }
}

/* Shared failure message for a failed YC fetch. */
def ycError(err: any): string {
  return "YC request failed (the data is static JSON on GitHub Pages; an unknown batch/industry/tag slug 404s): ${err.message ?? err}"
}

/* Turn a fetch Result into a Company[] Result. */
def companiesFinalize(fetchResult: any): Result<Company[]> {
  return match (fetchResult) {
    success(body) => success(parseCompanies(body))
    failure(err) => failure(ycError(err))
  }
}

/* Turn a fetch Result into a YcMeta Result. */
def metaFinalize(fetchResult: any): Result<YcMeta> {
  return match (fetchResult) {
    success(body) => success(parseMeta(body))
    failure(err) => failure(ycError(err))
  }
}

@capabilities(network)
export def ycBatch(batch: string): Result<Company[]> raises <std::yc, std::http::fetchJSON> {
  """
  List YC companies in a funding batch.

  @param batch - A batch label or slug, such as "Winter 2025" or "winter-2025".
  """
  return interrupt std::yc("Fetch YC companies for this batch?", { op: "batch", query: batch })
  const result = connectorFetch(YC_BASE, YC_DOMAINS, buildBatchPath(slugify(batch))) with approve
  return companiesFinalize(result)
}

@capabilities(network)
export def ycIndustry(industry: string): Result<Company[]> raises <std::yc, std::http::fetchJSON> {
  """
  List YC companies in an industry.

  @param industry - An industry label or slug from the directory metadata, such as "fintech".
  """
  return interrupt std::yc("Fetch YC companies for this industry?", { op: "industry", query: industry })
  const result = connectorFetch(YC_BASE, YC_DOMAINS, buildIndustryPath(slugify(industry))) with approve
  return companiesFinalize(result)
}

@capabilities(network)
export def ycTag(tag: string): Result<Company[]> raises <std::yc, std::http::fetchJSON> {
  """
  List YC companies carrying a directory tag.

  @param tag - A tag label or slug from the directory metadata, such as "SaaS".
  """
  return interrupt std::yc("Fetch YC companies for this tag?", { op: "tag", query: tag })
  const result = connectorFetch(YC_BASE, YC_DOMAINS, buildTagPath(slugify(tag))) with approve
  return companiesFinalize(result)
}

/**
  The `all` list can be large. Prefer a batch, industry, or tag when you
  only need part of the directory.
*/
@capabilities(network)
export def ycList(list: string = "top"): Result<Company[]> raises <std::yc, std::http::fetchJSON> {
  """
  Read a curated YC company list. "all" is large (about 6 MB); prefer a
  batch, industry, or tag lookup when you only need part of the directory.

  @param list - Use "top", "all", "hiring", "nonprofit", "women-founded", "black-founded", or "hispanic-latino-founded".
  """
  const nameResult = parseListName(list)
  if (isFailure(nameResult)) {
    return nameResult
  }
  const name = nameResult.value
  return interrupt std::yc("Fetch this YC company list?", { op: "list", query: list })
  const result = connectorFetch(YC_BASE, YC_DOMAINS, buildListPath(name)) with approve
  return companiesFinalize(result)
}

@capabilities(network)
export def ycMeta(): Result<YcMeta> raises <std::yc, std::http::fetchJSON> {
  """
  Discover available YC batch, industry, and tag slugs and the dataset's update time.
  """
  return interrupt std::yc("Fetch the YC directory metadata?", { op: "meta", query: "" })
  const result = connectorFetch(YC_BASE, YC_DOMAINS, buildMetaPath()) with approve
  return metaFinalize(result)
}
