// Generated by bynkc — do not edit by hand.
// context shortener.links

import { Ok, Err, Some, None, type Result, type Option, type ValidationError, type DurableObjectState, type DurableObjectNamespace, StateRegistry, makeAgent, HttpResult } from "../runtime.js";

import { ShortCode as __CommonsShortCode, Url as __CommonsUrl } from "./core.js";

import * as shortener_analytics from "./analytics.js";

export type ShortCode = __CommonsShortCode & { readonly __ctxBrand: "shortener.links" };
export const ShortCode = {
  of(value: string): Result<ShortCode, ValidationError> { return __CommonsShortCode.of(value) as unknown as Result<ShortCode, ValidationError>; },
  unsafe(value: string): ShortCode { return __CommonsShortCode.unsafe(value) as unknown as ShortCode; },
};
export type Url = __CommonsUrl & { readonly __ctxBrand: "shortener.links" };
export const Url = {
  of(value: string): Result<Url, ValidationError> { return __CommonsUrl.of(value) as unknown as Result<Url, ValidationError>; },
  unsafe(value: string): Url { return __CommonsUrl.unsafe(value) as unknown as Url; },
};

/**
 * Links context. The front door of the URL shortener.
 *
 * Owns the link-storage agent and the HTTP API. Consumes the analytics context
 * to record a hit each time a link is resolved. Depends on a CodeGen capability
 * to mint new short codes.
 */

export type LinkError =
    { readonly tag: "AlreadyExists" }
  | { readonly tag: "NotFound" };

export const LinkError = {
  AlreadyExists: { tag: "AlreadyExists" } as LinkError,
  NotFound: { tag: "NotFound" } as LinkError,
};

/**
 * Request body for creating a link. The `target` field is typed as the refined
 * Url, so the HTTP body deserialiser validates the URL shape and returns 400
 * before the handler runs if it is malformed.
 */
export interface CreateLinkRequest {
  readonly target: Url;
}

export const CreateLinkRequest = {
};

/**
 * The view returned when a link is created. Carries the refined ShortCode and
 * Url, which serialise to their underlying strings in the JSON response.
 */
export interface CreatedView {
  readonly code: ShortCode;
  readonly target: Url;
}

export const CreatedView = {
};

/**
 * The view returned when a link is resolved.
 */
export interface ResolveView {
  readonly target: Url;
  readonly hits: number;
}

export const ResolveView = {
};

/**
 * Mints new short codes as raw strings. In production a provider would draw from
 * a random source or a counter; here the stub returns a fixed string. The links
 * domain validates the string into a ShortCode via `ShortCode.of(...)` at the use
 * site — refined values are always born from validation at a boundary.
 */
export interface CodeGen {
  next(): Promise<string>;
}

export const CodeGenToken: unique symbol = Symbol("CodeGen");

/**
 * Stub provider: always returns the same code string.
 */
export class FixedCodeGen implements CodeGen {
  async next(): Promise<string> {
    return "abc123";
  }
}

export const FixedCodeGenProvider = { token: CodeGenToken, factory: () => new FixedCodeGen() };

/**
 * Stores the target URL for one short code. One agent instance per code.
 *
 * State note: `target` is an Option, assumed to initialise to None for an unseen
 * code. The test "a fresh Link key resolves to NotFound" probes exactly this — if
 * the runtime does not initialise Option state to None, that probe is where it
 * surfaces (finding #10, the Option counterpart to the analytics Int probe).
 */
export interface LinkState {
  readonly target: Option<Url>;
}

const __LinkRegistry = new StateRegistry();
function __zeroOfLinkState(): LinkState { return { target: None }; }

export class Link {
  state: DurableObjectState;
  constructor(state: DurableObjectState) {
    this.state = state;
  }

  private async loadState(): Promise<LinkState> {
    const stored = await this.state.storage.get<LinkState>("state");
    return stored ?? __zeroOfLinkState();
  }

  private async commitState(s: LinkState): Promise<void> {
    await this.state.storage.put("state", s);
  }

  /**
   * Register a target URL for this code. Fails if the code is already taken.
   */
  async register(url: Url, deps: {}): Promise<Result<void, LinkError>> {
    const currentState = await this.loadState();
    switch (currentState.target.tag) {
      case "Some": {
        return Err(LinkError.AlreadyExists);
      }
      case "None": {
        await this.commitState({ ...currentState, target: Some(url) });
        return Ok(undefined);
      }
    }
    throw new Error("non-exhaustive match");
  }

  /**
   * Resolve this code to its target URL. Fails if the code is unknown.
   */
  async resolve(deps: {}): Promise<Result<Url, LinkError>> {
    const currentState = await this.loadState();
    switch (currentState.target.tag) {
      case "Some": {
        const url = currentState.target.value;
        return Ok(url);
      }
      case "None": {
        return Err(LinkError.NotFound);
      }
    }
    throw new Error("non-exhaustive match");
  }

}

export function __makeLink(key: ShortCode, env?: { LINK?: DurableObjectNamespace }): Link {
  return makeAgent(__LinkRegistry, env?.LINK, key, (state) => new Link(state));
}

/**
 * Internal RPC to create a link with an explicit code. Its own service, so the
 * operation is addressable as `create` (callable from tests, or from other
 * contexts if any consumed links).
 */
export const create = {
  async call(code: ShortCode, target: Url, deps: {}): Promise<Result<void, LinkError>> {
    const link = __makeLink(code);
    const outcome = await link.register(target, deps);
    return outcome;
  },
};

/**
 * The HTTP surface. A service with only `on http` handlers (no `on call`), so it
 * isn't cross-context callable — it faces external clients.
 */
export const api = {
  /**
   * Create a shortened link. Mints a code, stores the target, and returns the
   * created view. Domain errors map explicitly to HTTP outcomes.
   */
  async http_POST_links(body: CreateLinkRequest, deps: { CodeGen: CodeGen }): Promise<HttpResult<CreatedView>> {
    const raw = await deps.CodeGen.next();
    const __r0 = ShortCode.of(raw);
    switch (__r0.tag) {
      case "Err": {
        return HttpResult.ServerError("generated an invalid code");
      }
      case "Ok": {
        const code = __r0.value;
        const link = __makeLink(code);
        const outcome = await link.register(body.target, deps);
        switch (outcome.tag) {
          case "Ok": {
            return HttpResult.Created({ code: code, target: body.target });
          }
          case "Err": {
            const linkError = outcome.error;
            return ((__d) => {
    switch (__d.tag) {
      case "AlreadyExists": {
        return HttpResult.Conflict("code already in use");
      }
      case "NotFound": {
        return HttpResult.ServerError("unexpected state");
      }
    }
    throw new Error("non-exhaustive match");
  })(linkError);
          }
        }
        throw new Error("non-exhaustive match");
      }
    }
    throw new Error("non-exhaustive match");
  },
  /**
   * Resolve a short code to its target, recording a hit in the analytics
   * context. The cross-context call to Analytics.track crosses a boundary:
   * `code` is projected from links' ShortCode to analytics' ShortCode.
   */
  async http_GET_links_Param_code(code: ShortCode, deps: { surface: { Analytics: ReturnType<typeof shortener_analytics.makeSurface> } }): Promise<HttpResult<ResolveView>> {
    const link = __makeLink(code);
    const outcome = await link.resolve(deps);
    switch (outcome.tag) {
      case "Ok": {
        const url = outcome.value;
        const hitsResult = await deps.surface.Analytics.track((code as unknown as shortener_analytics.ShortCode));
        switch (hitsResult.tag) {
          case "Ok": {
            const hits = hitsResult.value;
            return HttpResult.Ok({ target: url, hits: hits });
          }
          case "Err": {
            return HttpResult.ServerError("analytics unavailable");
          }
        }
        throw new Error("non-exhaustive match");
      }
      case "Err": {
        const linkError = outcome.error;
        return ((__d) => {
    switch (__d.tag) {
      case "NotFound": {
        return HttpResult.NotFound;
      }
      case "AlreadyExists": {
        return HttpResult.ServerError("unexpected state");
      }
    }
    throw new Error("non-exhaustive match");
  })(linkError);
      }
    }
    throw new Error("non-exhaustive match");
  },
};

export function __resetAgents(): void {
  __LinkRegistry.reset();
}

export interface ShortenerLinksDeps {
  readonly CodeGen: CodeGen;
  readonly surface: { Analytics: ReturnType<typeof shortener_analytics.makeSurface> };
}

export function makeSurface(deps: ShortenerLinksDeps) {
  return {
    async create(code: ShortCode, target: Url): Promise<Result<void, LinkError>> {
      return create.call(code, target, deps);
    },
  };
}

