// A Target is one deployed shape of the product (cloud / selfhost / …) seen
// purely from the outside: base URLs, how to mint a fresh isolated identity,
// and which capabilities it supports. Scenarios are written once against this
// interface; vitest projects pick which target a run executes on via
// E2E_TARGET. Boot/teardown of the instance is NOT here — each app owns its
// own dev-server boot (see setup/*.globalsetup.ts which call into the app).
import type { Effect } from "effect";

// Deployment traits only. Host-environment services (the OpenCode binary)
// and ones implied by an optional Target method (setAccessTokenTtl →
// TtlControl) are derived in scenario.ts, not declared here.
export type Capability =
  | "api" // typed HttpApiClient over the wire
  | "browser" // web UI reachable + identity injectable into a browser context
  | "mcp-oauth" // MCP endpoint with a headless OAuth consent path
  | "billing"; // billing limits are enforced (cloud-only)

export interface Identity {
  /** Shown in transcripts ("user_ab12cd") */
  readonly label: string;
  /** Headers that authenticate API requests (e.g. a session cookie). */
  readonly headers?: Record<string, string>;
  /** Cookies to inject into a browser context for a logged-in page. */
  readonly cookies?: ReadonlyArray<{ readonly name: string; readonly value: string }>;
  /** Credentials for surfaces that sign in themselves (Better Auth, OAuth consent). */
  readonly credentials?: { readonly email: string; readonly password: string };
}

export interface Target {
  readonly name: string;
  readonly baseUrl: string;
  readonly mcpUrl: string;
  readonly capabilities: ReadonlySet<Capability>;
  /**
   * Mint a fresh identity — THE isolation model: no resets, every scenario
   * is its own user (and org where applicable) on the shared instance.
   * `org: false` yields an identity with no active organization (for flows
   * that create one, like onboarding / billing limits).
   */
  readonly newIdentity: (options?: { readonly org?: boolean }) => Effect.Effect<Identity>;
  /** Headless OAuth consent for the MCP surface, when "mcp-oauth" is supported. */
  readonly mcpConsent?: (
    identity: Identity,
  ) => (request: { authorizationUrl: string; redirectUrl: string }) => Promise<{ code: string }>;
  /**
   * Compress (or restore, with null) the authorization server's access-token
   * lifetime, when "ttl-control" is supported — what lets token-expiry
   * scenarios cross a REAL expiry in seconds instead of an hour.
   */
  readonly setAccessTokenTtl?: (seconds: number | null) => Effect.Effect<void>;
  /**
   * Restart the instance, keeping its data — resolves when it answers HTTP
   * again. What lets durability scenarios assert that writes survive a
   * process restart (→ the Restart service).
   */
  readonly restart?: () => Effect.Effect<void>;
}
