import { discoverHarListing } from "../../web-automation/scripts/har-discover.js";
import { discoverZillowListing } from "../../web-automation/scripts/zillow-discover.js";
import { TimeoutError, withTimeout } from "./async-timeout.js";

export interface ListingDiscoveryResult {
  attempts: string[];
  zillowUrl: string | null;
  harUrl: string | null;
}

interface ListingDiscoveryDeps {
  timeoutMs?: number;
  zillowTimeoutMs?: number;
  harTimeoutMs?: number;
  discoverZillowListingFn?: typeof discoverZillowListing;
  discoverHarListingFn?: typeof discoverHarListing;
}

const DEFAULT_DISCOVERY_TIMEOUT_MS = Number(
  process.env.PROPERTY_ASSESSOR_DISCOVERY_TIMEOUT_MS || 20_000
);
const DEFAULT_ZILLOW_DISCOVERY_TIMEOUT_MS = Number(
  process.env.PROPERTY_ASSESSOR_ZILLOW_DISCOVERY_TIMEOUT_MS || 60_000
);
const DEFAULT_HAR_DISCOVERY_TIMEOUT_MS = Number(
  process.env.PROPERTY_ASSESSOR_HAR_DISCOVERY_TIMEOUT_MS || DEFAULT_DISCOVERY_TIMEOUT_MS
);

interface SourceDiscoveryOutcome {
  source: "zillow" | "har";
  url: string | null;
  attempts: string[];
}

export async function discoverListingSources(
  address: string,
  deps: ListingDiscoveryDeps = {}
): Promise<ListingDiscoveryResult> {
  const timeoutMs = deps.timeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
  const zillowTimeoutMs =
    deps.zillowTimeoutMs ??
    (deps.timeoutMs != null ? timeoutMs : DEFAULT_ZILLOW_DISCOVERY_TIMEOUT_MS);
  const harTimeoutMs =
    deps.harTimeoutMs ??
    (deps.timeoutMs != null ? timeoutMs : DEFAULT_HAR_DISCOVERY_TIMEOUT_MS);
  const discoverZillowListingFn = deps.discoverZillowListingFn || discoverZillowListing;
  const discoverHarListingFn = deps.discoverHarListingFn || discoverHarListing;

  const runSource = async (
    source: "zillow" | "har",
    timeoutForSourceMs: number,
    operation: () => Promise<{ listingUrl: string | null; attempts: string[] }>
  ): Promise<SourceDiscoveryOutcome> => {
    try {
      const result = await withTimeout(operation, {
        operationName: `${source === "zillow" ? "Zillow" : "HAR"} discovery`,
        timeoutMs: timeoutForSourceMs
      });
      return {
        source,
        url: result.listingUrl,
        attempts: result.attempts
      };
    } catch (error) {
      if (error instanceof TimeoutError) {
        return {
          source,
          url: null,
          attempts: [
            `${source === "zillow" ? "Zillow" : "HAR"} discovery timed out after ${timeoutForSourceMs}ms.`
          ]
        };
      }

      return {
        source,
        url: null,
        attempts: [
          `${source === "zillow" ? "Zillow" : "HAR"} discovery failed: ${error instanceof Error ? error.message : String(error)}`
        ]
      };
    }
  };

  const zillowPromise = runSource("zillow", zillowTimeoutMs, () =>
    discoverZillowListingFn(address, { timeoutMs: zillowTimeoutMs })
  );
  const harPromise = runSource("har", harTimeoutMs, () =>
    discoverHarListingFn(address, { timeoutMs: harTimeoutMs })
  );

  const [zillowResult, harResult] = await Promise.all([zillowPromise, harPromise]);
  const attempts = [...zillowResult.attempts, ...harResult.attempts];

  return {
    attempts,
    zillowUrl: zillowResult.url,
    harUrl: harResult.url
  };
}
