import type {
  CaptchaChallenge,
  CaptchaProvider,
  CaptchaVerificationResult,
  CreateCaptchaOptions,
  VerifyCaptchaInput,
} from "../types.ts";

export interface ManagedCaptchaProviderOptions {
  baseUrl: string;
  siteKey: string;
  secretKey: string;
  timeoutMs?: number;
  fetch?: typeof fetch;
}

export class ManagedCaptchaProvider implements CaptchaProvider {
  readonly name = "wrnexus-managed" as const;
  readonly client;
  private readonly baseUrl: string;

  constructor(private readonly options: ManagedCaptchaProviderOptions) {
    if (!options.baseUrl) throw new TypeError("Managed CAPTCHA baseUrl is required");
    if (!options.siteKey) throw new TypeError("Managed CAPTCHA siteKey is required");
    if (!options.secretKey) throw new TypeError("Managed CAPTCHA secretKey is required");
    this.baseUrl = options.baseUrl.replace(/\/+$/, "");
    this.client = {
      responseField: "wrn-captcha-response",
      siteKey: options.siteKey,
      managedCreateUrl: `${this.baseUrl}/v1/challenges`,
    };
  }

  async createChallenge(options: CreateCaptchaOptions): Promise<CaptchaChallenge> {
    return this.request<CaptchaChallenge>(
      "/v1/challenges",
      {
        siteKey: this.options.siteKey,
        ...options,
      },
      false,
    );
  }

  async verify(input: VerifyCaptchaInput): Promise<CaptchaVerificationResult> {
    return this.request<CaptchaVerificationResult>("/v1/verify", input, true);
  }

  private async request<T>(path: string, payload: unknown, secret: boolean): Promise<T> {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 10_000);
    try {
      const response = await (this.options.fetch ?? fetch)(`${this.baseUrl}${path}`, {
        method: "POST",
        headers: {
          "content-type": "application/json",
          accept: "application/json",
          ...(secret ? { authorization: `Bearer ${this.options.secretKey}` } : {}),
        },
        body: JSON.stringify(payload),
        signal: controller.signal,
      });
      if (!response.ok) throw new Error(`Managed CAPTCHA returned HTTP ${response.status}`);
      return (await response.json()) as T;
    } finally {
      clearTimeout(timeout);
    }
  }
}

export function managedCaptchaProvider(
  options: ManagedCaptchaProviderOptions,
): ManagedCaptchaProvider {
  return new ManagedCaptchaProvider(options);
}
