import { ChallengeRequiredError } from "../src/core/browserErrors.js";
import type { BrowserDiagnosticEvent, BrowserFetcher } from "../src/core/types.js";

export interface NativePageResult {
  storeId: string;
  url: string;
  status: number;
  body: string;
  challenged: boolean;
  cookieCount: number;
  diagnostics?: BrowserDiagnosticEvent[];
}

export interface NativeWebViewFetcher {
  getDeviceLanguages(): Promise<{ languages: string[] }>;
  fetchPageHtml(options: {
    storeId: string;
    url: string;
    timeoutMs?: number;
    challengeClearMs?: number;
    waitForSelector?: string;
  }): Promise<NativePageResult>;
  fetchResource(options: {
    storeId: string;
    homepage: string;
    url: string;
    headers?: Record<string, string>;
    solvePage?: string;
    timeoutMs?: number;
  }): Promise<NativePageResult>;
  show(options: { storeId: string }): Promise<{ cleared: boolean }>;
  dismiss(options: { storeId: string }): Promise<{ dismissed: boolean }>;
}

/** Production Android BrowserFetcher over one persistent native WebView per store. */
export function createAndroidBrowserFetcher(native: NativeWebViewFetcher): BrowserFetcher {
  const diagnostics = new Map<string, BrowserDiagnosticEvent[]>();
  const safeEvents = (events: BrowserDiagnosticEvent[]): BrowserDiagnosticEvent[] => events.slice(-30).map((event) => ({
    at: event.at,
    event: event.event,
    ...(event.status ? { status: event.status } : {}),
    ...(event.detail ? {
      detail: event.detail.replace(/https?:\/\/\S+/gi, "[url removed]").replace(/[\r\n\t]+/g, " ").slice(0, 120),
    } : {}),
  }));
  const callNative = async (storeId: string, operation: Promise<NativePageResult>): Promise<NativePageResult> => {
    try {
      const result = await operation;
      diagnostics.set(storeId, safeEvents(result.diagnostics ?? []));
      return result;
    } catch (error) {
      diagnostics.set(storeId, [{
        at: Date.now(),
        event: "error",
        detail: (error instanceof Error ? error.message : String(error))
          .replace(/https?:\/\/\S+/gi, "[url removed]").replace(/[\r\n\t]+/g, " ").slice(0, 120),
      }]);
      throw error;
    }
  };
  const bodyOrChallenge = (storeId: string, result: NativePageResult): string => {
    if (result.challenged) {
      throw new ChallengeRequiredError(storeId, `android://solve/${encodeURIComponent(storeId)}`);
    }
    if (result.status < 200 || result.status >= 300) {
      throw new Error(`${storeId}: WebView fetch HTTP ${result.status} for ${result.url}`);
    }
    return result.body;
  };

  return {
    async fetchPageHtml(storeId, url, options = {}) {
      const result = await callNative(storeId, native.fetchPageHtml({
        storeId,
        url,
        timeoutMs: 42_000,
        challengeClearMs: 25_000,
        ...(options.waitForSelector ? { waitForSelector: options.waitForSelector } : {}),
      }));
      return bodyOrChallenge(storeId, result);
    },

    async fetchJson(storeId, homepage, url, options = {}) {
      const result = await callNative(storeId, native.fetchResource({
        storeId,
        homepage,
        url,
        headers: {
          Accept: "application/json",
          "X-Requested-With": "XMLHttpRequest",
          ...options.headers,
        },
        ...(options.solvePage ? { solvePage: options.solvePage } : {}),
        timeoutMs: 25_000,
      }));
      return bodyOrChallenge(storeId, result);
    },

    async fetchOriginHtml(storeId, homepage, url) {
      const result = await callNative(storeId, native.fetchResource({
        storeId,
        homepage,
        url,
        timeoutMs: 40_000,
      }));
      return bodyOrChallenge(storeId, result);
    },

    takeDiagnostics(storeId) {
      const events = diagnostics.get(storeId) ?? [];
      diagnostics.delete(storeId);
      return events;
    },
  };
}
