import { detectPrimaryVideoDetailsFromHtml } from "../link-preview/content/video.js";
import { isCachedDiarizationCompatible, mapCachedSource, readTranscriptCache, writeTranscriptCache, } from "./cache.js";
import { canHandle as canHandleGeneric, fetchTranscript as fetchGeneric, } from "./providers/generic.js";
import { canHandle as canHandlePodcast, fetchTranscript as fetchPodcast, } from "./providers/podcast.js";
import { canHandle as canHandleYoutube, fetchTranscript as fetchYoutube, } from "./providers/youtube.js";
import { resolveTranscriptionConfig } from "./transcription-config.js";
import { extractYouTubeVideoId as extractYouTubeVideoIdInternal, isLoomVideoUrl, isYouTubeUrl as isYouTubeUrlInternal, } from "./utils.js";
const PROVIDERS = [
    { id: "youtube", canHandle: canHandleYoutube, fetchTranscript: fetchYoutube },
    { id: "podcast", canHandle: canHandlePodcast, fetchTranscript: fetchPodcast },
    { id: "generic", canHandle: canHandleGeneric, fetchTranscript: fetchGeneric },
];
const GENERIC_PROVIDER_ID = "generic";
export const resolveTranscriptForLink = async (url, html, deps, { timeoutMs, youtubeTranscriptMode, mediaTranscriptMode, mediaKindHint, transcriptTimestamps, transcriptDiarization, transcriptVideoDownload, cacheMode: providedCacheMode, fileMtime, embeddedMediaUrl, } = {}) => {
    const normalizedUrl = url.trim();
    const detectedEmbeddedMediaUrl = typeof embeddedMediaUrl === "undefined" && html
        ? detectPrimaryVideoDetailsFromHtml(html, normalizedUrl)?.video.url
        : embeddedMediaUrl;
    const embeddedYoutubeUrl = !isYouTubeUrlInternal(normalizedUrl) &&
        !isLoomVideoUrl(normalizedUrl) &&
        typeof detectedEmbeddedMediaUrl === "string" &&
        isYouTubeUrlInternal(detectedEmbeddedMediaUrl)
        ? detectedEmbeddedMediaUrl.trim()
        : null;
    const effectiveUrl = embeddedYoutubeUrl ?? normalizedUrl;
    const resourceKey = extractResourceKey(effectiveUrl);
    const baseContext = { url: effectiveUrl, html, resourceKey };
    const provider = selectProvider(baseContext);
    const cacheMode = providedCacheMode ?? "default";
    const cacheOutcome = await readTranscriptCache({
        url: normalizedUrl,
        cacheMode,
        transcriptCache: deps.transcriptCache,
        transcriptTimestamps: Boolean(transcriptTimestamps),
        transcriptDiarization: transcriptDiarization ?? null,
        fileMtime: fileMtime ?? null,
    });
    const diagnostics = {
        cacheMode,
        cacheStatus: cacheOutcome.diagnostics.cacheStatus,
        textProvided: cacheOutcome.diagnostics.textProvided,
        provider: cacheOutcome.diagnostics.provider,
        attemptedProviders: [],
        notes: cacheOutcome.diagnostics.notes ?? null,
    };
    const cachedSourceMetrics = cacheOutcome.resolution?.metadata?.sourceMetrics ??
        cacheOutcome.cached?.metadata?.sourceMetrics;
    const cachedSourceMetricsRecord = cachedSourceMetrics &&
        typeof cachedSourceMetrics === "object" &&
        !Array.isArray(cachedSourceMetrics)
        ? cachedSourceMetrics
        : null;
    const cachedVideoId = typeof cachedSourceMetricsRecord?.videoId === "string"
        ? cachedSourceMetricsRecord.videoId
        : null;
    const cachedResourceKey = typeof cacheOutcome.cached?.resourceKey === "string" &&
        cacheOutcome.cached.resourceKey.trim().length > 0
        ? cacheOutcome.cached.resourceKey.trim()
        : null;
    const cachedVideoIdentity = cachedResourceKey ?? cachedVideoId;
    const embeddedVideoIdentityMismatch = Boolean(cacheOutcome.cached && embeddedYoutubeUrl && resourceKey && cachedVideoIdentity !== resourceKey);
    if (cacheOutcome.resolution && !embeddedVideoIdentityMismatch) {
        return {
            ...cacheOutcome.resolution,
            diagnostics,
        };
    }
    if (embeddedVideoIdentityMismatch) {
        diagnostics.cacheStatus = "miss";
        diagnostics.notes = appendNote(diagnostics.notes, "Cached transcript ignored because the embedded YouTube video changed or could not be verified");
    }
    if (embeddedYoutubeUrl && (!cacheOutcome.resolution || embeddedVideoIdentityMismatch)) {
        const embeddedCacheOutcome = await readTranscriptCache({
            url: embeddedYoutubeUrl,
            cacheMode,
            transcriptCache: deps.transcriptCache,
            transcriptTimestamps: Boolean(transcriptTimestamps),
            transcriptDiarization: transcriptDiarization ?? null,
        });
        const embeddedCachedMetrics = embeddedCacheOutcome.cached?.metadata?.sourceMetrics;
        const embeddedCachedVideoId = embeddedCachedMetrics &&
            typeof embeddedCachedMetrics === "object" &&
            !Array.isArray(embeddedCachedMetrics) &&
            typeof embeddedCachedMetrics.videoId === "string"
            ? String(embeddedCachedMetrics.videoId)
            : null;
        const embeddedCachedIdentity = embeddedCacheOutcome.cached?.resourceKey ?? embeddedCachedVideoId;
        if (embeddedCacheOutcome.resolution && resourceKey && embeddedCachedIdentity === resourceKey) {
            return {
                ...embeddedCacheOutcome.resolution,
                diagnostics: {
                    ...embeddedCacheOutcome.diagnostics,
                    notes: appendNote(embeddedCacheOutcome.diagnostics.notes, "Reused transcript cached by canonical embedded YouTube URL"),
                },
            };
        }
    }
    const shouldReportProgress = provider.id === "youtube" || provider.id === "podcast";
    if (shouldReportProgress) {
        deps.onProgress?.({
            kind: "transcript-start",
            url: normalizedUrl,
            service: provider.id,
            hint: provider.id === "youtube"
                ? "YouTube: resolving transcript"
                : "Podcast: resolving transcript",
        });
    }
    const transcription = resolveTranscriptionConfig({
        env: deps.env,
        transcription: deps.transcription ?? null,
        falApiKey: deps.falApiKey,
        groqApiKey: deps.groqApiKey,
        assemblyaiApiKey: deps.assemblyaiApiKey,
        deepgramApiKey: deps.deepgramApiKey,
        elevenlabsApiKey: deps.elevenlabsApiKey,
        geminiApiKey: deps.geminiApiKey,
        openaiApiKey: deps.openaiApiKey,
    });
    const providerResult = await executeProvider(provider, baseContext, {
        fetch: deps.fetch,
        timeoutMs,
        env: deps.env,
        scrapeWithFirecrawl: deps.scrapeWithFirecrawl,
        apifyApiToken: deps.apifyApiToken,
        ytDlpPath: deps.ytDlpPath,
        transcription,
        falApiKey: transcription.falApiKey,
        groqApiKey: transcription.groqApiKey,
        assemblyaiApiKey: transcription.assemblyaiApiKey,
        deepgramApiKey: transcription.deepgramApiKey,
        elevenlabsApiKey: transcription.elevenlabsApiKey,
        geminiApiKey: transcription.geminiApiKey,
        openaiApiKey: transcription.openaiApiKey,
        mediaCache: deps.mediaCache ?? null,
        resolveTwitterCookies: deps.resolveTwitterCookies ?? null,
        onProgress: deps.onProgress ?? null,
        youtubeTranscriptMode: youtubeTranscriptMode ?? "auto",
        mediaTranscriptMode: mediaTranscriptMode ?? "auto",
        mediaKindHint: mediaKindHint ?? null,
        transcriptTimestamps: transcriptTimestamps ?? false,
        transcriptDiarization: transcriptDiarization ?? null,
        transcriptVideoDownload: transcriptVideoDownload ?? false,
    });
    if (shouldReportProgress) {
        deps.onProgress?.({
            kind: "transcript-done",
            url: normalizedUrl,
            ok: Boolean(providerResult.text && providerResult.text.length > 0),
            service: provider.id,
            source: providerResult.source,
            hint: providerResult.source ? `${provider.id}/${providerResult.source}` : provider.id,
        });
    }
    diagnostics.provider = providerResult.source;
    diagnostics.attemptedProviders = providerResult.attemptedProviders;
    diagnostics.textProvided = Boolean(providerResult.text && providerResult.text.length > 0);
    if (providerResult.notes) {
        diagnostics.notes = appendNote(diagnostics.notes, providerResult.notes);
    }
    if (providerResult.source !== null || providerResult.text !== null) {
        if (transcriptTimestamps || transcriptDiarization) {
            const nextMeta = { ...(providerResult.metadata ?? {}) };
            if (providerResult.segments && providerResult.segments.length > 0) {
                if (transcriptTimestamps)
                    nextMeta.timestamps = true;
                nextMeta.segments = providerResult.segments;
            }
            else if (transcriptTimestamps && nextMeta.timestamps == null) {
                nextMeta.timestamps = false;
            }
            providerResult.metadata = nextMeta;
        }
        else if (providerResult.segments && providerResult.segments.length > 0) {
            providerResult.metadata = {
                ...(providerResult.metadata ?? {}),
                segments: providerResult.segments,
            };
        }
        await writeTranscriptCache({
            url: normalizedUrl,
            service: provider.id,
            resourceKey,
            result: providerResult,
            transcriptCache: deps.transcriptCache,
            fileMtime,
        });
        if (embeddedYoutubeUrl) {
            await writeTranscriptCache({
                url: embeddedYoutubeUrl,
                service: provider.id,
                resourceKey,
                result: providerResult,
                transcriptCache: deps.transcriptCache,
            });
        }
    }
    if (!providerResult.text &&
        !embeddedVideoIdentityMismatch &&
        cacheOutcome.cached?.content &&
        cacheMode !== "bypass" &&
        isCachedDiarizationCompatible(cacheOutcome.cached.metadata, transcriptDiarization ?? null)) {
        diagnostics.cacheStatus = "fallback";
        diagnostics.provider = mapCachedSource(cacheOutcome.cached.source);
        diagnostics.textProvided = Boolean(cacheOutcome.cached.content && cacheOutcome.cached.content.length > 0);
        diagnostics.notes = appendNote(diagnostics.notes, "Falling back to cached transcript content after provider miss");
        return {
            text: cacheOutcome.cached.content,
            source: diagnostics.provider,
            metadata: cacheOutcome.cached.metadata ?? null,
            diagnostics,
            segments: transcriptTimestamps
                ? resolveSegmentsFromMetadata(cacheOutcome.cached.metadata)
                : null,
        };
    }
    return {
        text: providerResult.text,
        source: providerResult.source,
        metadata: providerResult.metadata ?? null,
        diagnostics,
        segments: transcriptTimestamps ? (providerResult.segments ?? null) : null,
    };
};
const extractResourceKey = (url) => {
    if (isYouTubeUrlInternal(url)) {
        return extractYouTubeVideoIdInternal(url);
    }
    return null;
};
const selectProvider = (context) => {
    const genericProviderModule = PROVIDERS.find((provider) => provider.id === GENERIC_PROVIDER_ID);
    const specializedProvider = PROVIDERS.find((provider) => provider.id !== GENERIC_PROVIDER_ID && provider.canHandle(context));
    if (specializedProvider) {
        return specializedProvider;
    }
    if (genericProviderModule) {
        return genericProviderModule;
    }
    throw new Error("Generic transcript provider is not registered");
};
const executeProvider = async (provider, context, options) => provider.fetchTranscript(context, options);
const appendNote = (existing, next) => {
    if (!existing) {
        return next;
    }
    return `${existing}; ${next}`;
};
const resolveSegmentsFromMetadata = (metadata) => {
    if (!metadata)
        return null;
    const segments = metadata.segments;
    return Array.isArray(segments) && segments.length > 0
        ? segments
        : null;
};
//# sourceMappingURL=index.js.map