// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { AlphaType } from '//resources/mojo/skia/public/mojom/image_info.mojom-webui.js';
import { MicrophoneStatus as MicrophoneStatusMojo, PinTrigger as PinTriggerMojo, ScreenshotCompressionQuality as ScreenshotCompressionQualityMojo, ScreenshotImageFormat as ScreenshotImageFormatMojo, UnpinTrigger as UnpinTriggerMojo, WebClientMode as WebClientModeMojo } from '../../glic.mojom-webui.js';
import { DEFAULT_INNER_TEXT_BYTES_LIMIT, DEFAULT_PDF_SIZE_LIMIT, MicrophoneStatus, Platform, WebClientMode } from '../../glic_api/glic_api.js';
import { enumFromClient, enumToClient } from '../enum_conversions.js';
import { replaceProperties } from './../conversions.js';
import { ImageAlphaType, ImageColorType } from './../request_types.js';
export const conversionSettings = {
    platform: undefined,
    omitFaviconInTabData: false,
};
export function idToClient(windowId) {
    if (windowId === null) {
        return undefined;
    }
    if (Number.isNaN(windowId)) {
        return '0';
    }
    return `${windowId}`;
}
export function idFromClient(windowId) {
    if (windowId === undefined) {
        return null;
    }
    const parsed = parseInt(windowId);
    if (Number.isNaN(parsed)) {
        return 0;
    }
    return parsed;
}
export function screenshotToClient(screenshot, extras) {
    if (!screenshot) {
        return undefined;
    }
    const screenshotArray = new Uint8Array(screenshot.data);
    const buffer = screenshotArray.buffer;
    extras.addTransfer(buffer);
    return {
        widthPixels: screenshot.widthPixels,
        heightPixels: screenshot.heightPixels,
        data: buffer,
        mimeType: screenshot.mimeType,
        originAnnotations: {},
    };
}
export function contextDataToClient(data, extras) {
    const buffer = getArrayBufferFromBigBuffer(data.data);
    if (!buffer) {
        return undefined;
    }
    extras.addTransfer(buffer);
    return { mimeType: data.mimeType, data: buffer };
}
export function webPageDataToClient(webPageData) {
    if (!webPageData) {
        return undefined;
    }
    return {
        mainDocument: {
            origin: originToClient(webPageData.mainDocument.origin),
            innerText: webPageData.mainDocument.innerText,
            innerTextTruncated: webPageData.mainDocument.innerTextTruncated,
        },
    };
}
export function pdfDocumentDataToClient(pdfDocumentData, extras) {
    if (!pdfDocumentData) {
        return undefined;
    }
    const pdfData = pdfDocumentData.pdfData ?
        new Uint8Array(pdfDocumentData.pdfData).buffer :
        undefined;
    if (pdfData) {
        extras.addTransfer(pdfData);
    }
    return {
        origin: originToClient(pdfDocumentData.origin),
        pdfSizeLimitExceeded: pdfDocumentData.sizeLimitExceeded,
        pdfData,
    };
}
export function protoWrapperToClient(protoWrapper, extras) {
    if (!protoWrapper) {
        return undefined;
    }
    const buffer = getArrayBufferFromBigBuffer(protoWrapper.smuggled);
    if (buffer) {
        extras.addTransfer(buffer);
    }
    return buffer;
}
export function annotatedPageDataToClient(annotatedPageData, extras) {
    if (!annotatedPageData) {
        return undefined;
    }
    const annotatedPageContent = protoWrapperToClient(annotatedPageData.annotatedPageContent, extras);
    let metadata = undefined;
    if (annotatedPageData.metadata) {
        metadata = {
            frameMetadata: annotatedPageData.metadata.frameMetadata.map(m => replaceProperties(m, { url: urlToClient(m.url) })),
        };
    }
    return { annotatedPageContent, metadata };
}
export function optionalToClient(value) {
    if (value === null) {
        return undefined;
    }
    return value;
}
export function optionalFromClient(value) {
    if (value === undefined) {
        return null;
    }
    return value;
}
export function urlToClient(url) {
    if (url === null) {
        return undefined;
    }
    return url;
}
export function urlFromClient(url) {
    return url;
}
export function originToClient(origin) {
    if (!origin) {
        return undefined;
    }
    if (!origin.scheme) {
        return '';
    }
    const originBase = `${origin.scheme}://${origin.host}`;
    if (origin.port) {
        return `${originBase}:${origin.port}`;
    }
    return originBase;
}
export function originToClientOpaqueAsNull(origin) {
    if (origin?.nonceIfOpaque) {
        return 'null';
    }
    return originToClient(origin);
}
export function tabDataToClient(tabData, extras) {
    if (!tabData) {
        return undefined;
    }
    let favicon = undefined;
    if (tabData.favicon && !conversionSettings.omitFaviconInTabData) {
        favicon = bitmapN32ToRGBAImage(tabData.favicon);
        if (favicon) {
            extras.addTransfer(favicon.dataRGBA);
        }
    }
    const isObservable = optionalToClient(tabData.isObservable);
    const isMediaActive = optionalToClient(tabData.isMediaActive);
    const isTabContentCaptured = optionalToClient(tabData.isTabContentCaptured);
    const isActiveInWindow = optionalToClient(tabData.isActiveInWindow);
    const isWindowActive = optionalToClient(tabData.isWindowActive);
    return {
        tabId: idToClient(tabData.tabId),
        windowId: idToClient(tabData.windowId),
        url: urlToClient(tabData.url),
        title: optionalToClient(tabData.title),
        favicon,
        faviconUrl: urlToClient(tabData.faviconUrl),
        documentMimeType: tabData.documentMimeType,
        isObservable,
        isMediaActive,
        isTabContentCaptured,
        isActiveInWindow,
        isWindowActive,
        lightweightPageFeatures: tabData.lightweightPageFeatures?.map(feature => enumToClient(feature)),
    };
}
export function focusedTabDataToClient(focusedTabData, extras) {
    if (focusedTabData.focusedTab) {
        return {
            hasFocus: { tabData: tabDataToClient(focusedTabData.focusedTab, extras) },
        };
    }
    if (focusedTabData.noFocusedTabData) {
        return {
            hasNoFocus: {
                tabFocusCandidateData: tabDataToClient(focusedTabData.noFocusedTabData.activeTabData, extras),
                noFocusReason: focusedTabData.noFocusedTabData.noFocusReason,
            },
        };
    }
    console.error('Invalid FocusedTabDataMojo');
    return {};
}
export function getArrayBufferFromBigBuffer(bigBuffer) {
    if (bigBuffer.bytes !== undefined) {
        return new Uint8Array(bigBuffer.bytes).buffer;
    }
    return bigBuffer.sharedMemory?.bufferHandle
        .mapBuffer(0, bigBuffer.sharedMemory.size)
        .buffer;
}
export function bitmapN32ToRGBAImage(bitmap) {
    const bytes = getArrayBufferFromBigBuffer(bitmap.pixelData);
    if (!bytes) {
        return undefined;
    }
    // Color type on Android is different from other platforms. Because color type
    // isn't transmitted over mojo, we need use platform to detect it here.
    const colorType = conversionSettings.platform === Platform.ANDROID ?
        ImageColorType.RGBA :
        ImageColorType.BGRA;
    return {
        width: bitmap.imageInfo.width,
        height: bitmap.imageInfo.height,
        dataRGBA: bytes,
        alphaType: bitmap.imageInfo.alphaType === AlphaType.PREMUL ?
            ImageAlphaType.PREMUL :
            ImageAlphaType.UNPREMUL,
        colorType,
    };
}
export function panelOpeningDataToClient(panelOpeningData) {
    const conversationInfo = conversationInfoToClient(panelOpeningData.conversationInfo);
    return {
        panelState: panelStateToClient(panelOpeningData.panelState),
        invocationSource: enumToClient(panelOpeningData.invocationSource),
        conversationId: conversationInfo?.conversationId || undefined,
        promptSuggestion: optionalToClient(panelOpeningData.promptSuggestion),
        autoSend: panelOpeningData.autoSend && !!panelOpeningData.promptSuggestion,
        recentlyActiveConversations: panelOpeningData.recentlyActiveConversations ?
            panelOpeningData.recentlyActiveConversations.map(conversationInfoToClient) :
            undefined,
        conversationInfo,
        freOverride: enumToClient(panelOpeningData.freOverride),
    };
}
export function conversationInfoToClient(conversationInfo) {
    return {
        conversationId: conversationInfo.conversationId,
        conversationTitle: conversationInfo.conversationTitle,
        clientData: conversationInfo.clientData ?
            new TextDecoder().decode(new Uint8Array(conversationInfo.clientData.data)) :
            undefined,
        turnId: optionalToClient(conversationInfo.turnId),
    };
}
export function conversationInfoFromClient(conversationInfo) {
    return {
        conversationId: conversationInfo.conversationId,
        conversationTitle: conversationInfo.conversationTitle,
        clientData: conversationInfo.clientData ? {
            data: Array.from(new TextEncoder().encode(conversationInfo.clientData)),
        } :
            null,
        turnId: optionalFromClient(conversationInfo.turnId),
    };
}
export function counterAbuseVerdictFromClient(verdict) {
    return {
        sbVerdictResult: verdict.sbVerdictResult ? {
            url: verdict.sbVerdictResult.url,
            threatType: enumFromClient(verdict.sbVerdictResult.threatType),
            showInterstitial: verdict.sbVerdictResult.showInterstitial,
        } : null,
    };
}
export function panelStateToClient(panelState) {
    return {
        kind: enumToClient(panelState.kind),
        windowId: idToClient(panelState.windowId),
    };
}
export function pageMetadataToClient(metadata) {
    if (!metadata) {
        return null;
    }
    return {
        frameMetadata: metadata.frameMetadata.map(m => replaceProperties(m, { url: urlToClient(m.url) })),
    };
}
/** Takes a time value in milliseconds and converts to a Mojo TimeDelta. */
export function timeDeltaFromClient(durationMs = 0) {
    if (!Number.isFinite(durationMs)) {
        throw new Error('Invalid duration value: ' + durationMs);
    }
    return { microseconds: BigInt(Math.floor(durationMs * 1000)) };
}
export function tabContextToClient(tabContext, extras) {
    const tabData = tabDataToClient(tabContext.tabData, extras);
    const webPageData = webPageDataToClient(tabContext.webPageData);
    const viewportScreenshot = screenshotToClient(tabContext.viewportScreenshot, extras);
    const screenshotInfo = protoWrapperToClient(tabContext.screenshotInfo, extras);
    const pdfDocumentData = pdfDocumentDataToClient(tabContext.pdfDocumentData, extras);
    const annotatedPageData = annotatedPageDataToClient(tabContext.annotatedPageData, extras);
    return {
        tabData,
        webPageData,
        viewportScreenshot,
        screenshotInfo,
        pdfDocumentData,
        annotatedPageData,
    };
}
export function tabContextOptionsFromClient(options) {
    return {
        includeInnerText: options.innerText ?? false,
        innerTextBytesLimit: options.innerTextBytesLimit ?? DEFAULT_INNER_TEXT_BYTES_LIMIT,
        includeViewportScreenshot: options.viewportScreenshot === true ||
            options.screenshotCollectionOptions !== undefined,
        includePdf: options.pdfData ?? false,
        includeAnnotatedPageContent: options.annotatedPageContent ?? false,
        maxMetaTags: options.maxMetaTags ?? 0,
        pdfSizeLimit: options.pdfSizeLimit === undefined ?
            DEFAULT_PDF_SIZE_LIMIT :
            Math.min(Number.MAX_SAFE_INTEGER, options.pdfSizeLimit),
        annotatedPageContentMode: options.annotatedPageContentMode === undefined ?
            0 :
            options.annotatedPageContentMode,
        screenshotCollectionOptions: screenshotCollectionOptionsFromClient(options.screenshotCollectionOptions),
    };
}
// Taken from mojo_type_utils.ts
export function getPinCandidatesOptionsFromClient(options) {
    return {
        maxCandidates: options.maxCandidates,
        query: options.query ?? null,
    };
}
export function pinTriggerToMojo(trigger) {
    return (trigger ?? PinTriggerMojo.kWebClientUnknown);
}
export function pinTabsOptionsToMojo(options) {
    if (!options) {
        return null;
    }
    return {
        pinTrigger: pinTriggerToMojo(options.pinTrigger),
    };
}
export function unpinTriggerToMojo(trigger) {
    return (trigger ?? UnpinTriggerMojo.kWebClientUnknown);
}
export function unpinTabsOptionsToMojo(options) {
    if (!options) {
        return null;
    }
    return {
        unpinTrigger: unpinTriggerToMojo(options.unpinTrigger),
    };
}
export function byteArrayFromClient(buffer) {
    const byteArray = new Uint8Array(buffer);
    return Array.from(byteArray);
}
export function hostCapabilitiesToClient(capabilities) {
    return capabilities.map(capability => enumToClient(capability));
}
export function webClientModeToMojo(mode) {
    switch (mode) {
        case WebClientMode.AUDIO:
            return WebClientModeMojo.kAudio;
        case WebClientMode.TEXT:
            return WebClientModeMojo.kText;
        default:
            return WebClientModeMojo.kUnknown;
    }
}
export function microphoneStatusToMojo(status) {
    switch (status) {
        case MicrophoneStatus.LISTENING:
            return MicrophoneStatusMojo.kListening;
        case MicrophoneStatus.NOT_LISTENING:
            return MicrophoneStatusMojo.kNotListening;
        case MicrophoneStatus.UNKNOWN:
        default:
            return MicrophoneStatusMojo.kUnknown;
    }
}
export function captureRegionResultToClient(result) {
    if (!result) {
        return undefined;
    }
    const region = result.region.rect ? { rect: result.region.rect } :
        result.region.polyline ? { polyline: result.region.polyline } :
            undefined;
    return {
        tabId: idToClient(result.tabId),
        region,
    };
}
export function zeroStateSuggestionsToClient(zeroStateSuggestions) {
    return {
        suggestions: zeroStateSuggestions.suggestions,
        isPending: zeroStateSuggestions.isPending,
        invocationSource: enumToClient(zeroStateSuggestions.invocationSource),
    };
}
export function zssConfigToClient(zssConfig) {
    return {
        additionalContent: zssConfig.additionalContent || undefined,
    };
}
export function invokeOptionsToClient(options, extras) {
    return {
        invocationSource: enumToClient(options.invocationSource),
        prompts: options.prompts || undefined,
        context: options.context ?
            additionalContextToClient(options.context, extras) :
            undefined,
        autoSubmit: options.autoSubmit,
        featureMode: enumToClient(options.featureMode),
        actuationTarget: enumToClient(options.actuationTarget),
        disableZeroStateSuggestions: options.disableZeroStateSuggestions,
        skillId: options.skillId || undefined,
        zssConfig: options.zssConfig ? zssConfigToClient(options.zssConfig) :
            undefined,
        payload: invocationPayloadToClient(options.payload, extras),
        actuationTabId: idToClient(options.actuationTabId),
    };
}
export function invocationPayloadToClient(payload, extras) {
    if (!payload) {
        return undefined;
    }
    if (payload.universalCart) {
        const buffer = new Uint8Array(payload.universalCart.serializedMetadata).buffer;
        extras.addTransfer(buffer);
        return {
            universalCart: {
                serializedMetadata: buffer,
            },
        };
    }
    throw new Error('Unknown payload type');
}
export function additionalContextToClient(context, extras) {
    const parts = context.parts ?
        context.parts.map(p => additionalContextPartToClient(p, extras)) :
        [];
    // Filter out undefined parts if any.
    const validParts = [];
    for (const p of parts) {
        if (p) {
            validParts.push(p);
        }
    }
    return {
        source: enumToClient(context.source),
        name: context.name || undefined,
        tabId: idToClient(context.tabId),
        origin: context.origin ? originToClient(context.origin) : undefined,
        frameUrl: context.frameUrl ? urlToClient(context.frameUrl) : undefined,
        parts: validParts,
    };
}
export function additionalContextPartToClient(part, extras) {
    const result = {};
    if (part.data) {
        result.data = {
            mimeType: part.data.mimeType,
            data: getArrayBufferFromBigBuffer(part.data.data),
        };
        result.filename = optionalToClient(part.data.filename);
        extras.addTransfer(result.data.data);
    }
    else if (part.screenshot) {
        result.screenshot = screenshotToClient(part.screenshot, extras);
    }
    else if (part.webPageData) {
        result.webPageData = webPageDataToClient(part.webPageData);
    }
    else if (part.annotatedPageData) {
        result.annotatedPageData =
            annotatedPageDataToClient(part.annotatedPageData, extras);
    }
    else if (part.pdfDocumentData) {
        result.pdf = pdfDocumentDataToClient(part.pdfDocumentData, extras);
    }
    else if (part.tabContext) {
        result.tabContext = tabContextToClient(part.tabContext, extras);
    }
    else if (part.region) {
        const regionMojo = part.region;
        if (regionMojo) {
            return {
                region: {
                    rect: regionMojo.rect,
                    polyline: regionMojo.polyline,
                },
            };
        }
    }
    else if (part.pendingRegion) {
        const regionMojo = part.pendingRegion.region;
        if (regionMojo) {
            return {
                pendingRegion: {
                    id: part.pendingRegion.id,
                    region: {
                        rect: regionMojo.rect,
                        polyline: regionMojo.polyline,
                    },
                },
            };
        }
    }
    else if (part.parentConversationMetadata) {
        result.parentConversationMetadata = {
            conversationId: optionalToClient(part.parentConversationMetadata.conversationId),
            conversationTitle: optionalToClient(part.parentConversationMetadata.conversationTitle),
        };
    }
    return result;
}
export function screenshotCollectionOptionsFromClient(options) {
    return {
        maxWidth: options?.maxWidth ?? 0,
        maxHeight: options?.maxHeight ?? 0,
        screenshotImageFormat: options?.screenshotImageFormat ?
            enumFromClient(options.screenshotImageFormat) :
            ScreenshotImageFormatMojo.kJpeg,
        screenshotCompressionQuality: options?.screenshotCompressionQuality ?
            enumFromClient(options.screenshotCompressionQuality) :
            ScreenshotCompressionQualityMojo.kMedium,
    };
}
export function subscriberObservationTypeFromClient(val) {
    return val;
}
export function imageInfoToClient(info) {
    return {
        caption: optionalToClient(info.caption),
        sourceOrigin: originToClientOpaqueAsNull(info.sourceOrigin),
        url: urlToClient(info.url),
        mimeType: optionalToClient(info.mimeType),
    };
}
export function imageBytesResultToClient(result, extras) {
    const buffer = getArrayBufferFromBigBuffer(result.bytes);
    if (!buffer) {
        return null;
    }
    extras.addTransfer(buffer);
    return {
        bytes: buffer,
        imageInfo: imageInfoToClient(result.imageInfo),
    };
}
