import { AddonHealthCheckResult, AddonHealthStatus, AddonRegistryEntry } from "./models";

/**
 * Fetch and cache an addon's manifest.json from its live URL.
 * Returns the parsed manifest or null on failure.
 */
export async function fetchManifest(
  manifestUrl: string,
  cacheTtlSeconds: number = 21600, // 6 hours
): Promise<Record<string, unknown> | null> {
  try {
    const cache = (caches as unknown as { default: Cache }).default;
    const cacheKey = `manifest:${manifestUrl}`;

    // Check cache
    const cached = await cache.match(cacheKey);
    if (cached) {
      return cached.json() as Promise<Record<string, unknown>>;
    }

    // Fetch live
    const response = await fetch(manifestUrl, {
      headers: { "User-Agent": "NexStream-AddonRegistry/1.0" },
      cf: { cacheTtl: cacheTtlSeconds },
    });

    if (!response.ok) return null;

    const manifest = (await response.json()) as Record<string, unknown>;

    // Cache the response
    const cacheResponse = new Response(JSON.stringify(manifest), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": `public, max-age=${cacheTtlSeconds}`,
      },
    });
    await cache.put(cacheKey, cacheResponse);

    return manifest;
  } catch {
    return null;
  }
}

/**
 * Update an addon's registry entry with data from its live manifest.
 * Mutates the entry in place.
 */
export function applyManifestToEntry(
  entry: AddonRegistryEntry,
  manifest: Record<string, unknown>,
): void {
  entry.name = (manifest.name as string) || entry.name;
  entry.version = (manifest.version as string) || entry.version;
  entry.description = (manifest.description as string) || entry.description;
  entry.logo = (manifest.logo as string) || entry.logo;
  entry.background = (manifest.background as string) || entry.background;

  const resources = manifest.resources as { name: string; types?: string[] }[] | undefined;
  if (resources) {
    entry.resources = resources.map((r) => r.name);
    entry.types = resources.flatMap((r) => r.types || []);
  }

  const types = manifest.types as string[] | undefined;
  if (types && types.length > 0) {
    // Deduplicate with resource types
    const allTypes = [...new Set([...entry.types, ...types])];
    entry.types = allTypes;
  }

  const idPrefixes = manifest.idPrefixes as string[] | undefined;
  if (idPrefixes) {
    entry.idPrefixes = idPrefixes;
  }

  const catalogs = manifest.catalogs as { type: string; id: string; name: string }[] | undefined;
  if (catalogs) {
    entry.catalogs = catalogs.map((c) => ({
      type: c.type,
      id: c.id,
      name: c.name,
    }));
  }

  const behaviorHints = manifest.behaviorHints as
    | { configurable?: boolean; configurationRequired?: boolean }
    | undefined;
  if (behaviorHints) {
    entry.configurable = behaviorHints.configurable === true;
  }

  entry.lastChecked = new Date().toISOString();
}

/**
 * Perform a health check on an addon.
 * Tests manifest fetch + optional stream endpoint test.
 */
export async function healthCheckAddon(
  entry: AddonRegistryEntry,
): Promise<AddonHealthCheckResult> {
  const start = Date.now();

  try {
    const response = await fetch(entry.manifestUrl, {
      headers: { "User-Agent": "NexStream-AddonRegistry/1.0" },
    });

    const manifestMs = Date.now() - start;

    if (!response.ok) {
      const status: AddonHealthStatus = response.status === 401 || response.status === 403
        ? "auth_error"
        : "failing";
      return {
        addonId: entry.id,
        status,
        manifestResponseMs: manifestMs,
        streamResponseMs: null,
        manifestValid: false,
        streamCount: null,
        error: `HTTP ${response.status}`,
        checkedAt: new Date().toISOString(),
      };
    }

    // Try parsing manifest
    try {
      const manifest = (await response.json()) as Record<string, unknown>;
      if (!manifest.id && !manifest.name) {
        return {
          addonId: entry.id,
          status: "failing",
          manifestResponseMs: manifestMs,
          streamResponseMs: null,
          manifestValid: false,
          streamCount: null,
          error: "Invalid manifest JSON",
          checkedAt: new Date().toISOString(),
        };
      }
    } catch {
      return {
        addonId: entry.id,
        status: "failing",
        manifestResponseMs: manifestMs,
        streamResponseMs: null,
        manifestValid: false,
        streamCount: null,
        error: "JSON parse failure",
        checkedAt: new Date().toISOString(),
      };
    }

    // Optional: test a stream endpoint
    let streamMs: number | null = null;
    let streamCount: number | null = null;
    if (entry.resources.includes("stream")) {
      const streamStart = Date.now();
      try {
        const streamResp = await fetch(
          `${entry.installUrl}/stream/movie/tt0111161.json`,
          { headers: { "User-Agent": "NexStream-AddonRegistry/1.0" } },
        );
        streamMs = Date.now() - streamStart;
        if (streamResp.ok) {
          const data = (await streamResp.json()) as { streams?: unknown[] };
          streamCount = data.streams?.length ?? 0;
        }
      } catch {
        // Stream endpoint test failed — addon still works, just note it
      }
    }

    const totalMs = manifestMs + (streamMs || 0);
    let status: AddonHealthStatus;
    if (totalMs < 2000) status = "online";
    else if (totalMs < 6000) status = "slow";
    else status = "failing";

    return {
      addonId: entry.id,
      status,
      manifestResponseMs: manifestMs,
      streamResponseMs: streamMs,
      manifestValid: true,
      streamCount,
      error: null,
      checkedAt: new Date().toISOString(),
    };
  } catch (e) {
    return {
      addonId: entry.id,
      status: "offline",
      manifestResponseMs: Date.now() - start,
      streamResponseMs: null,
      manifestValid: false,
      streamCount: null,
      error: (e as Error).message || "Unknown error",
      checkedAt: new Date().toISOString(),
    };
  }
}
