import type {
  HttpClient,
  HttpClientConfig,
  RuntimeDb,
  RuntimeDalContext,
  RuntimeEnv,
  RuntimeHeaders,
  RuntimeOptions,
  RuntimeResponse,
} from "./runtime/index";

export interface TestkitSuite<TSetup = unknown> {
  options: RuntimeOptions;
  setup: () => TSetup | null;
  exec: (setupData: TSetup | null) => unknown;
}

export interface HeaderBuilderContext {
  env: RuntimeEnv;
}

export type HeaderBuilder<TSetup = unknown> = (
  setupData?: TSetup | null,
  context?: HeaderBuilderContext
) => RuntimeHeaders | void;

export interface AuthAdapter<TSetup = unknown> {
  setup?: (context: { env: RuntimeEnv }) => TSetup;
  headers?: HeaderBuilder<TSetup>;
}

export interface HttpSuiteContext<TSetup = unknown> {
  env: RuntimeEnv;
  req: HttpClient<TSetup>["request"];
  rawReq: HttpClient["raw"];
  getWithHeaders: HttpClient<TSetup>["getWithHeaders"];
  setupData: TSetup | null;
  session: TSetup | null;
}

export interface ScenarioStepResult {
  name: string;
  status: "passed" | "failed";
  startedAt?: string;
  finishedAt?: string;
  durationMs?: number;
  failureCount?: number;
  error?: string;
}

export interface ScenarioResource<TValue = unknown> {
  get(): TValue;
}

export interface ScenarioRuntime {
  readonly seed: string;
  readonly scenarioName: string | null;
  choose<TChoice extends unknown[]>(
    name: string,
    choices: TChoice
  ): TChoice[number];
  choose<TShape extends Record<string, unknown>>(
    name: string,
    shape: TShape
  ): TShape;
  maybe(name: string, probability?: number): boolean;
  note<TValue = unknown>(name: string, value: TValue): TValue;
  pick<TChoice extends unknown[]>(name: string, choices: TChoice): TChoice[number];
  resource<TValue = unknown>(
    name: string,
    factory: () => TValue,
    options?: { scope?: "file" | "scenario" | "step" }
  ): ScenarioResource<TValue>;
  step<TValue = unknown>(name: string, fn: () => TValue): TValue;
  snapshot(): {
    schemaVersion: number;
    seed: string;
    scenarioName: string | null;
    choices: Record<string, unknown>;
    notes: Record<string, unknown>;
    resources: Array<{ name: string; scope: "file" | "scenario" | "step" }>;
    steps: ScenarioStepResult[];
  };
}

export interface ScenarioSuiteContext<TSetup = unknown> extends HttpSuiteContext<TSetup> {
  scenario: ScenarioRuntime;
}

export interface HttpSuiteConfig<TSetup = unknown> {
  auth?: AuthAdapter<TSetup> | null;
  env?: RuntimeEnv;
  headers?: HeaderBuilder<TSetup>;
  profile?: string;
  rawHeaders?: HeaderBuilder<never>;
  options?: RuntimeOptions;
}

export interface DalSuiteContext<TSetup = unknown> {
  db: RuntimeDb;
  dal: RuntimeDalContext;
  setupData: TSetup | null;
}

export interface DalSuiteConfig<TSetup = unknown> {
  db?: RuntimeDb;
  options?: RuntimeOptions;
  setup?: (context: { db: RuntimeDb; dal: RuntimeDalContext }) => TSetup;
}

export declare function defineHttpSuite<TSetup = unknown>(
  run: (context: HttpSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function defineHttpSuite<TSetup = unknown>(
  config: HttpSuiteConfig<TSetup>,
  run: (context: HttpSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function defineScenarioSuite<TSetup = unknown>(
  run: (context: ScenarioSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function defineScenarioSuite<TSetup = unknown>(
  config: HttpSuiteConfig<TSetup>,
  run: (context: ScenarioSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function defineDalSuite<TSetup = unknown>(
  run: (context: DalSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function defineDalSuite<TSetup = unknown>(
  config: DalSuiteConfig<TSetup>,
  run: (context: DalSuiteContext<TSetup>) => unknown
): TestkitSuite<TSetup>;

export declare function createAuthAdapter<TSetup = unknown>(
  adapter?: AuthAdapter<TSetup>
): AuthAdapter<TSetup>;

export type {
  HttpClient,
  HttpClientConfig,
  RuntimeDb,
  RuntimeDalContext,
  RuntimeEnv,
  RuntimeHeaders,
  RuntimeOptions,
  RuntimeResponse,
};
