// Generated by bynkc — do not edit by hand.
// bynk surface binding for the `node` platform.
import type { Clock, Fetch, Logger, Random, Secrets } from "./bynk.js";
import type { Request as FetchRequest, Response as FetchResponse } from "./bynk.js";
import { FetchError, Uuid } from "./bynk.js";
import { Err, None, Ok, Some, type Option, type Result } from "./runtime.js";

export class ClockProvider implements Clock {
  async now(): Promise<number> {
    return Date.now();
  }
}

export class RandomProvider implements Random {
  async uuid(): Promise<Uuid> {
    const r = Uuid.of(crypto.randomUUID());
    if (r.tag === "Err") {
      throw new Error("unreachable: crypto.randomUUID() yields a valid UUID");
    }
    return r.value;
  }
  async int(lo: number, hi: number): Promise<number> {
    return lo + Math.floor(Math.random() * (hi - lo));
  }
}

export class LoggerProvider implements Logger {
  async info(msg: string): Promise<void> {
    console.log(msg);
  }
  async error(msg: string): Promise<void> {
    console.error(msg);
  }
}

export class FetchProvider implements Fetch {
  async send(req: FetchRequest): Promise<Result<FetchResponse, FetchError>> {
    const headers: Record<string, string> = {};
    if (req.contentType.tag === "Some") {
      headers["content-type"] = req.contentType.value;
    }
    if (req.authorization.tag === "Some") {
      headers["authorization"] = req.authorization.value;
    }
    try {
      const res = await fetch(req.url, {
        method: req.method.tag.toUpperCase(),
        headers,
        body: req.body.tag === "Some" ? req.body.value : undefined,
      });
      return Ok({ status: res.status, body: await res.text() });
    } catch (e) {
      const name = e instanceof Error ? e.name : "";
      return Err(name === "TimeoutError" || name === "AbortError" ? FetchError.Timeout : FetchError.Network);
    }
  }
}

export class SecretsProvider implements Secrets {
  // Decision [B]: explicit env first, then the `globalThis` probe of
  // `process.env` — on node the probe is the normal path. A declared field +
  // assigning constructor, not a parameter property: the latter is non-erasable
  // by pure type-stripping, and this binding runs under Node
  // `--experimental-strip-types` on the `--inspect` path (the strip-only emission
  // invariant, ADR 0136).
  private env?: unknown;
  constructor(env?: unknown) {
    this.env = env;
  }
  async get(name: string): Promise<Option<string>> {
    const fromEnv = (this.env as Record<string, unknown> | undefined)?.[name];
    if (typeof fromEnv === "string") {
      return Some(fromEnv);
    }
    const v = (globalThis as any).process?.env?.[name];
    return typeof v === "string" ? Some(v) : None;
  }
}
