/* eslint-disable unicorn/no-null */

// Получение отпечатка Canvas
const getCanvasFingerprint = async () => {
    try {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (!ctx) return null;
        canvas.width = 220;
        canvas.height = 30;
        ctx.textBaseline = 'top';
        ctx.font = '14px Arial';
        ctx.fillStyle = '#f60';
        ctx.fillRect(0, 0, 0, 0);
        ctx.fillStyle = '#069';
        ctx.fillText('fingerprint123', 2, 2);
        return canvas.toDataURL();
    } catch {
        return null;
    }
};

// Получение информации о точности шейдеров WebGL
const getWebGLPrecision = (gl) => {
    if (!gl) return {};
    const getPrecision = (shaderType, precisionType, name) => {
        const precision = gl.getShaderPrecisionFormat(shaderType, precisionType);
        return {
            [`precision${name}`]: precision?.precision ?? null,
            [`rangeMin${name}`]: precision?.rangeMin ?? null,
            [`rangeMax${name}`]: precision?.rangeMax ?? null,
        };
    };

    const HIGH_FLOAT = gl.HIGH_FLOAT;
    const MEDIUM_FLOAT = gl.MEDIUM_FLOAT;
    const LOW_FLOAT = gl.LOW_FLOAT;
    const HIGH_INT = gl.HIGH_INT;
    const MEDIUM_INT = gl.MEDIUM_INT;
    const LOW_INT = gl.LOW_INT;
    const VERTEX_SHADER = gl.VERTEX_SHADER;
    const FRAGMENT_SHADER = gl.FRAGMENT_SHADER;

    return {
        ...getPrecision(VERTEX_SHADER, HIGH_FLOAT, 'VertexShaderHighFloat'),
        ...getPrecision(VERTEX_SHADER, MEDIUM_FLOAT, 'VertexShaderMediumFloat'),
        ...getPrecision(VERTEX_SHADER, LOW_FLOAT, 'VertexShaderLowFloat'),
        ...getPrecision(FRAGMENT_SHADER, HIGH_FLOAT, 'FragmentShaderHighFloat'),
        ...getPrecision(FRAGMENT_SHADER, MEDIUM_FLOAT, 'FragmentShaderMediumFloat'),
        ...getPrecision(FRAGMENT_SHADER, LOW_FLOAT, 'FragmentShaderLowFloat'),

        ...getPrecision(VERTEX_SHADER, HIGH_INT, 'VertexShaderHighInt'),
        ...getPrecision(VERTEX_SHADER, MEDIUM_INT, 'VertexShaderMediumInt'),
        ...getPrecision(VERTEX_SHADER, LOW_INT, 'VertexShaderLowInt'),
        ...getPrecision(FRAGMENT_SHADER, HIGH_INT, 'FragmentShaderHighInt'),
        ...getPrecision(FRAGMENT_SHADER, MEDIUM_INT, 'FragmentShaderMediumInt'),
        ...getPrecision(FRAGMENT_SHADER, LOW_INT, 'FragmentShaderLowInt'),
    };
};

// Сбор всех данных WebGL (отпечаток и свойства)
const getWebGLData = async () => {
    try {
        const canvas = document.createElement('canvas');
        const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

        // Попытка получить WebGL2 контекст для дополнительных свойств
        const gl2 = canvas.getContext('webgl2');

        if (!gl) return { webgl: null, webgl_properties: null, webgl_precision: {} };

        const getParameter = (p) => gl.getParameter(p);
        const dbg = gl.getExtension('WEBGL_debug_renderer_info');

        // Отпечаток WebGL (более краткий набор свойств)
        const webgl = {
            vendor: dbg ? getParameter(dbg.UNMASKED_VENDOR_WEBGL) : getParameter(gl.VENDOR),
            renderer: dbg ? getParameter(dbg.UNMASKED_RENDERER_WEBGL) : getParameter(gl.RENDERER),
            version: getParameter(gl.VERSION),
            shadingLanguage: getParameter(gl.SHADING_LANGUAGE_VERSION),
            maxTextureSize: getParameter(gl.MAX_TEXTURE_SIZE),
            aliasedLineWidthRange: getParameter(gl.ALIASED_LINE_WIDTH_RANGE),
            aliasedPointSizeRange: getParameter(gl.ALIASED_POINT_SIZE_RANGE),
            redBits: getParameter(gl.RED_BITS),
            greenBits: getParameter(gl.GREEN_BITS),
            blueBits: getParameter(gl.BLUE_BITS),
            alphaBits: getParameter(gl.ALPHA_BITS),
            depthBits: getParameter(gl.DEPTH_BITS),
            stencilBits: getParameter(gl.STENCIL_BITS),
            extensions: gl.getSupportedExtensions(),
        };

        // Подробные свойства WebGL
        const webgl_properties = {
            unmaskedVendor: dbg ? getParameter(dbg.UNMASKED_VENDOR_WEBGL) : null,
            unmaskedRenderer: dbg ? getParameter(dbg.UNMASKED_RENDERER_WEBGL) : null,
            vendor: getParameter(gl.VENDOR),
            renderer: getParameter(gl.RENDERER),
            shadingLanguage: getParameter(gl.SHADING_LANGUAGE_VERSION),
            version: getParameter(gl.VERSION),
            maxAnisotropy: gl.getExtension('EXT_texture_filter_anisotropic')
                ? getParameter(gl.getExtension('EXT_texture_filter_anisotropic').MAX_TEXTURE_MAX_ANISOTROPY_EXT)
                : null,

            shadingLanguage2: gl2?.getParameter?.(gl2.SHADING_LANGUAGE_VERSION) ?? null,
            version2: gl2?.getParameter?.(gl2.VERSION) ?? null,

            aliasedLineWidthRange: getParameter(gl.ALIASED_LINE_WIDTH_RANGE),
            aliasedPointSizeRange: getParameter(gl.ALIASED_POINT_SIZE_RANGE),
            redBits: getParameter(gl.RED_BITS),
            greenBits: getParameter(gl.GREEN_BITS),
            blueBits: getParameter(gl.BLUE_BITS),
            alphaBits: getParameter(gl.ALPHA_BITS),
            depthBits: getParameter(gl.DEPTH_BITS),
            stencilBits: getParameter(gl.STENCIL_BITS),
            subpixelBits: getParameter(gl.SUBPIXEL_BITS),
            sampleBuffers: getParameter(gl.SAMPLE_BUFFERS),
            samples: getParameter(gl.SAMPLES),
            maxTextureSize: getParameter(gl.MAX_TEXTURE_SIZE),
            maxCubeMapTextureSize: getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),
            maxRenderBufferSize: getParameter(gl.MAX_RENDERBUFFER_SIZE),
            maxVertexAttribs: getParameter(gl.MAX_VERTEX_ATTRIBS),
            maxVertexTextureImageUnits: getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS),
            maxTextureImageUnits: getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),
            maxFragmentUniformVectors: getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS),
            maxVertexUniformVectors: getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),

            extensions: gl.getSupportedExtensions()?.join(','),
        };

        const webgl_precision = getWebGLPrecision(gl);

        return { webgl, webgl_properties, webgl_precision };
    } catch {
        return { webgl: null, webgl_properties: null, webgl_precision: {} };
    }
};

// Сбор всех данных Audio (отпечаток и свойства AudioContext)
const getAudioData = async () => {
    try {
        const Offline = window.OfflineAudioContext || window.webkitOfflineAudioContext;
        const Context = window.AudioContext || window.webkitAudioContext;

        if (!Offline || !Context) return { audio: null, audio_properties: null };

        // 1. Отпечаток Audio (Хеширование)
        const offlineCtx = new Offline(1, 44_100, 44_100);
        const osc = offlineCtx.createOscillator();
        const comp = offlineCtx.createDynamicsCompressor();
        osc.type = 'triangle';
        osc.frequency.value = 10_000;
        osc.connect(comp);
        comp.connect(offlineCtx.destination);
        osc.start(0);
        const buf = await offlineCtx.startRendering();
        let sum = 0;
        const data = buf.getChannelData(0);
        for (let i = 0; i < data.length; i += 100) sum += Math.abs(data[i]);
        const audio = sum;

        // 2. Свойства AudioContext
        const ctx = new Context();
        const properties = {};

        // Свойства BaseAudioContext
        properties.BaseAudioContextSampleRate = ctx.sampleRate;
        properties.AudioContextBaseLatency = ctx.baseLatency ?? null;
        properties.AudioContextOutputLatency = ctx.outputLatency ?? null;

        // Свойства AudioDestinationNode
        properties.AudioDestinationNodeMaxChannelCount = ctx.destination?.maxChannelCount ?? null;

        // Свойства других узлов
        const analyzer = ctx.createAnalyser();
        properties.AnalyzerNodeFftSize = analyzer.fftSize;
        properties.AnalyzerNodeFrequencyBinCount = analyzer.frequencyBinCount;
        properties.AnalyzerNodeMinDecibels = analyzer.minDecibels;
        properties.AnalyzerNodeMaxDecibels = analyzer.maxDecibels;

        const waveShaper = ctx.createWaveShaper();
        properties.WaveShaperNodeMaxCurveLength = waveShaper.maxCurveLength ?? null;

        const oscillator = ctx.createOscillator();
        properties.OscillatorNodeMaxFrequency = oscillator.frequency?.maxValue ?? null;

        const panner = ctx.createPanner();
        properties.PannerNodeMaxDistance = panner.maxDistance ?? null;
        properties.PannerNodeMaxCone = panner.coneOuterAngle;

        properties.numberOfInputs = panner.numberOfInputs;
        properties.numberOfOutputs = panner.numberOfOutputs;
        properties.channelCount = panner.channelCount;
        properties.panningModel = panner.panningModel;
        properties.distanceModel = panner.distanceModel;
        properties.coneInnerAngle = panner.coneInnerAngle;
        properties.coneOuterAngle = panner.coneOuterAngle;
        properties.coneOuterGain = panner.coneOuterGain;

        // Максимальное время задержки DelayNode
        const delay = ctx.createDelay();
        properties.maxDelayTime = delay.maxDelayTime;

        // Поддерживаемые узлы
        properties.supported_nodes = [];
        const audioNodes = [
            'DynamicsCompressorNode',
            'DelayNode',
            'OscillatorNode',
            'StereoPannerNode',
            'AnalyserNode',
            'ConvolverNode',
            'WaveShaperNode',
            'PannerNode',
            'PeriodicWaveNode',
            'ChannelMergerNode',
        ];

        for (const node of audioNodes) {
            if (typeof window[node] === 'function') {
                properties.supported_nodes.push(node);
            }
        }

        // Свойства каналов и AudioWorkletGlobalScope
        properties.channel_count_modes = ['max', 'clamped-max', 'explicit'];
        properties.channel_interpretations = ['speakers', 'discrete'];

        if (window.AudioWorkletGlobalScope) {
            properties.AudioWorkletGlobalScopeMaxChannelCount = window.AudioWorkletGlobalScope.MAX_CHANNEL_COUNT;
            properties.render_quantum_size = window.AudioWorkletGlobalScope.RENDER_QUANTUM_SIZE;
        } else {
            properties.AudioWorkletGlobalScopeMaxChannelCount = null;
            properties.render_quantum_size = null;
        }

        // Дополнительные свойства контекста
        properties.sampleRate = ctx.sampleRate;
        properties.outputLatency = ctx.outputLatency ?? null;
        properties.inputLatency = ctx.baseLatency ?? null;
        properties.isResampling = false;
        properties.contextType = 'playback';
        properties.hardwareAccelerated = false;
        properties.shared = true;

        await ctx.close();

        return { audio, audio_properties: properties };
    } catch {
        return { audio: null, audio_properties: null };
    }
};

// Отпечаток Math
const getMathFingerprint = async () => {
    try {
        const values = {
            acos: Math.acos(0.123_456_789),
            asin: Math.asin(0.123_456_789),
            atan: Math.atan(0.123_456_789),
            sin: Math.sin(1e37),
            cos: Math.cos(1e-37),
            tan: Math.tan(10),
            exp: Math.exp(1),
            log: Math.log(10),
            sqrt: Math.sqrt(2),
            pow: Math.pow(123_456.789, -0.123),
            round: (0.1 + 0.2).toFixed(20),
            toExp: (123_456_789).toExponential(20),
            oneDivNegZero: 1 / -0,
        };
        return values;
    } catch {
        return null;
    }
};

// Сбор информации о батарее
const getBattery = async () => {
    try {
        if (!navigator.getBattery) return { has_battery_api: false };
        const b = await navigator.getBattery();
        return {
            battery: null, // Хеш или заглушка для совместимости
            has_battery_api: true,
            has_battery_device: false,
            battery_level: b.level ?? null,
            battery_charging: b.charging ?? null,
            battery_discharging_time: b.dischargingTime === Infinity ? null : b.dischargingTime ?? null,
            battery_charging_time: b.chargingTime === Infinity ? null : b.chargingTime ?? null,
            battery_status: b.charging
                ? 'charging'
                : b.dischargingTime === Infinity
                ? 'full'
                : b.dischargingTime === undefined
                ? null
                : 'discharging',
            battery_low_power_mode: false,
            battery_is_full: b.level === 1 && b.chargingTime === 0,
        };
    } catch {
        return null;
    }
};

// Сбор информации о WebGPU
const getWebGPU = async () => {
    try {
        if (!navigator.gpu?.requestAdapter) return null;
        const adapter = await navigator.gpu.requestAdapter();
        if (!adapter) return null;

        let preferredCanvasFormatValue = null;
        try {
            if (navigator.gpu.getPreferredCanvasFormat) {
                preferredCanvasFormatValue = navigator.gpu.getPreferredCanvasFormat();
            }
        } catch {} // eslint-disable-line no-empty

        const adapterInfo = await adapter.requestAdapterInfo?.();

        return {
            isEnabled: true,
            highPerformance: adapter?.powerPreference === 'high-performance' || null,
            lowPower: adapter?.powerPreference === 'low-power' || null,
            fallback: false,
            preferredCanvasFormat: preferredCanvasFormatValue,
            supported_features: [...(adapter.features?.values?.() ?? [])],
            deviceLimits: adapter.limits ? Object.fromEntries(Object.entries(adapter.limits)) : {},
            powerPreference: adapter.powerPreference ?? null,
            adapterName: adapterInfo?.description ?? null,
            vendor: adapterInfo?.vendor ?? null,
            architecture: adapterInfo?.architecture ?? null,
            device: adapterInfo?.device ?? null,
        };
    } catch {
        return null;
    }
};

// Получение информации о лимитах хранилища и использовании
const getStorage = async () => {
    try {
        const est = await navigator.storage?.estimate?.();
        return est ? { quota: est.quota ?? null, usage: est.usage ?? null } : null;
    } catch {
        return null;
    }
};

// Получение списка голосов для Speech Synthesis
const getVoices = async () => {
    try {
        if (!window.speechSynthesis) return null;

        const mapVoice = (v) => ({
            name: v.name,
            lang: v.lang,
            voiceURI: v.voiceURI,
            localService: v.localService,
            default: v.default,
            // Простое хеширование URI для id
            id: v.voiceURI?.split('').reduce((hash, char) => (hash << 5) - hash + char.charCodeAt(0), 0) >>> 0 ?? null,
            vendor: null,
            device: null,
        });

        const voices = window.speechSynthesis.getVoices();
        if (voices.length > 0) {
            return voices.map((v) => mapVoice(v));
        }

        // Асинхронная загрузка, если голоса еще не загружены
        return new Promise((resolve) => {
            window.speechSynthesis.onvoiceschanged = () => {
                const list = window.speechSynthesis.getVoices();
                resolve(list.map((v) => mapVoice(v)));
            };
        });
    } catch {
        return null;
    }
};

// Получение информации о сетевом соединении
const getNetwork = () => {
    try {
        const c = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
        if (!c) return null;
        return {
            downlink: c.downlink ?? null,
            effectiveType: c.effectiveType ?? null,
            rtt: c.rtt ?? null,
            saveData: c.saveData ?? null,
        };
    } catch {
        return null;
    }
};

// Получение информации о локализации через Intl API
const getIntlInfo = () => {
    try {
        const locales = navigator.languages ?? [navigator.language];
        const dateSample = new Date(2020, 11, 31, 19, 5, 7);
        const numSample = 1_234_567.89;
        return {
            locales,
            date: new Intl.DateTimeFormat(locales).format(dateSample),
            time: new Intl.DateTimeFormat(locales, { hour: 'numeric', minute: 'numeric' }).format(dateSample),
            num: new Intl.NumberFormat(locales).format(numSample),
            currency: new Intl.NumberFormat(locales, { style: 'currency', currency: 'USD' }).format(numSample),
            resolved: {
                date: new Intl.DateTimeFormat(locales).resolvedOptions(),
                num: new Intl.NumberFormat(locales).resolvedOptions(),
            },
        };
    } catch {
        return null;
    }
};

// Сбор информации о поддержке различных браузерных API
const getCommonFeatures = () => {
    // Длинный список булевых флагов для API и возможностей
    const features = {
        SharedWorker: !!window.SharedWorker,
        OrientationEvent: !!window.DeviceOrientationEvent,
        WebHID: !!navigator.hid,
        FileSystemAccess: !!window.showDirectoryPicker,
        FontAccess: !!window.queryLocalFonts,
        WebGPU: !!navigator.gpu,
        FencedFrames: !!document.createElement('iframe').hasAttribute('fence'),
        TopicsAPI: typeof document.browsingTopics === 'function',
        PrivacySandboxAdsAPIs: typeof window.adAuction === 'object',
        AttributionReporting: typeof window.attributionReporting === 'object',
        ConversionMeasurement: typeof window.attributionReporting === 'object',
        PrivateAggregation: typeof window.privateAggregation === 'object',
        PrivateAggregationForTesting: typeof window.privateAggregation?.sendHistogramReport === 'function',
        SharedStorage: typeof window.sharedStorage === 'object',
        InterestCohort: typeof document.interestCohort === 'function',
        AggregateAttestation: false,
        TrustTokens: !!window.fetch?.trustToken,
        DocumentPolicy: !!document.featurePolicy,
        WindowControlsOverlay: !!navigator.windowControlsOverlay,
        WindowPlacement: !!window.screen.isExtended,
        WebAssembly: typeof WebAssembly === 'object',
        WebGL2: !!window.WebGL2RenderingContext,
        WebXR: !!navigator.xr,
        WebCodecs: !!window.VideoEncoder,
        WebNFC: !!window.NFC,
        WebShare: !!navigator.share,
        WebUSB: !!navigator.usb,
        WebVTT: !!window.VTTCue,
        CanvasComposition: true,
        CSSPseudoElements: true,
        CSSCustomProperties: true,
        DynamicImport: true,
        AsyncFunction: true,
        BigInt: typeof BigInt === 'function',
        PointerEvents: !!window.PointerEvent,
        ResizeObserver: !!window.ResizeObserver,
        IntersectionObserver: !!window.IntersectionObserver,
        requestIdleCallback: !!window.requestIdleCallback,
        requestAnimationFrame: !!window.requestAnimationFrame,
        WebAnimations: !!document.body?.animate,
        BackgroundFetch: !!window.BackgroundFetchManager,
        BackgroundSync: !!window.SyncManager,
        Bluetooth: !!navigator.bluetooth,
        PaymentRequest: !!window.PaymentRequest,
        PushMessaging: !!window.PushManager,
        SpeechRecognition: !!window.SpeechRecognition || !!window.webkitSpeechRecognition,
        SpeechSynthesis: !!window.speechSynthesis,
        WakeLock: !!navigator.wakeLock,
        WebWorkers: !!window.Worker,
        ServiceWorkers: !!navigator.serviceWorker,
        IndexedDB: !!window.indexedDB,
        LocalStorage: !!window.localStorage,
        SessionStorage: !!window.sessionStorage,
        Cookies: navigator.cookieEnabled,
        WebSockets: !!window.WebSocket,
        WebRTC: !!window.RTCPeerConnection,
        MediaDevices: !!navigator.mediaDevices,
        getUserMedia: !!navigator.mediaDevices?.getUserMedia,
        getDisplayMedia: !!navigator.mediaDevices?.getDisplayMedia,
        RTCPeerConnection: !!window.RTCPeerConnection,
        RTCDataChannel: !!window.RTCDataChannel,
        MediaRecorder: !!window.MediaRecorder,
        MediaSource: !!window.MediaSource,
        EncryptedMedia: !!window.MediaKeys,
        PictureInPicture: !!document.pictureInPictureEnabled,
        FullscreenAPI: !!document.fullscreenEnabled,
        PointerLock: !!document.pointerLockElement,
        GamepadAPI: !!navigator.getGamepads,
        Notifications: !!window.Notification,
        Geolocation: !!navigator.geolocation,
        BatteryStatus: !!navigator.getBattery,
        AmbientLightSensor: !!window.AmbientLightSensor,
        Accelerometer: !!window.Accelerometer,
        Gyroscope: !!window.Gyroscope,
        Magnetometer: !!window.Magnetometer,
        VRDisplays: !!navigator.getVRDisplays,
        XRSession: !!window.XRSession,
        WebGLDebugRendererInfo: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_debug_renderer_info')
        ),
        WebGLDepthTexture: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_depth_texture')
        ),
        WebGLDrawBuffers: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_draw_buffers')
        ),
        WebGLCompressedTextureS3TC: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_compressed_texture_s3tc')
        ),
        WebGLCompressedTextureETC1: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_compressed_texture_etc1')
        ),
        WebGLColorBufferFloat: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('EXT_color_buffer_float')
        ),
        WebGLVertexArrayObject: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('OES_vertex_array_object')
        ),
        WebGLLoseContext: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getExtension('WEBGL_lose_context')
        ),
        WebGLShaderPrecisionFormat: !!(
            window.WebGLRenderingContext &&
            document.createElement('canvas').getContext('webgl')?.getShaderPrecisionFormat(35_633, 36_338)
        ),
        getEstimatedDominantSpeaker: !!window.RTCRtpReceiver?.getStats,
        RTCIceTransport: !!window.RTCIceTransport,
        RTCStatsReport: !!window.RTCStatsReport,
        ProximitySensor: !!window.ProximitySensor,
    };
    return features;
};

const mq = (q) => window.matchMedia(q);

// Сбор информации о CSS Media Features
const getCssFeatures = () => {
    const mediaFeatures = {};

    mediaFeatures['any-hover'] = mq('(any-hover: hover)').matches ? 'hover' : 'none';
    mediaFeatures['any-pointer'] = mq('(any-pointer: fine)').matches ? 'fine' : 'coarse';
    mediaFeatures['hover'] = mq('(hover: hover)').matches ? 'hover' : 'none';
    mediaFeatures['pointer'] = mq('(pointer: fine)').matches ? 'fine' : 'coarse';

    mediaFeatures['prefers-color-scheme'] = mq('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    mediaFeatures['prefers-contrast'] = mq('(prefers-contrast: more)').matches ? 'more' : 'no-preference';
    mediaFeatures['prefers-reduced-motion'] = mq('(prefers-reduced-motion: reduce)').matches
        ? 'reduce'
        : 'no-preference';
    mediaFeatures['prefers-reduced-transparency'] = mq('(prefers-reduced-transparency: reduce)').matches
        ? 'reduce'
        : 'no-preference';

    mediaFeatures['color-gamut'] = mq('(color-gamut: p3)').matches ? 'p3' : 'srgb';
    mediaFeatures['orientation'] =
        window.screen.orientation?.type?.split('-')[0] ??
        (window.innerWidth > window.innerHeight ? 'landscape' : 'portrait');
    mediaFeatures['resolution'] = window.devicePixelRatio * 96;
    mediaFeatures['device-aspect-ratio'] = window.screen.width / window.screen.height;
    mediaFeatures['forced-colors'] = mq('(forced-colors: active)').matches ? 'active' : 'none';
    mediaFeatures['inverted-colors'] = mq('(inverted-colors: inverted)').matches ? 'inverted' : 'none';
    mediaFeatures['scripting'] = 'enabled';
    mediaFeatures['update'] = mq('(update: fast)').matches ? 'fast' : 'medium';

    // Дополнительные булевы проверки (для совместимости с предыдущим кодом)
    mediaFeatures.prefersColorSchemeDark = mq('(prefers-color-scheme: dark)').matches;
    mediaFeatures.prefersColorSchemeLight = mq('(prefers-color-scheme: light)').matches;

    // Новые поля, чтобы соответствовать shape example.json
    mediaFeatures['color'] = window.screen.colorDepth;
    mediaFeatures['grid'] = mq('(grid)').matches ? '1' : '0';
    mediaFeatures['monochrome'] = window.matchMedia('(monochrome)').matches ? 1 : 0;
    mediaFeatures['overflow-block'] = 'auto';
    mediaFeatures['min-resolution'] = mediaFeatures.resolution;
    mediaFeatures['max-resolution'] = mediaFeatures.resolution;
    mediaFeatures['dynamic-range'] = mq('(dynamic-range: high)').matches ? 'high' : 'standard';
    mediaFeatures['update-frequency'] = mediaFeatures.update;

    // Дополнительные эвристические поля из example.json
    mediaFeatures['aspect-ratio'] = mediaFeatures['device-aspect-ratio'];
    mediaFeatures['display-mode'] = 'standalone';
    mediaFeatures['environment'] = 'speech';
    mediaFeatures['viewport-fit'] = 'cover';
    mediaFeatures['writing-mode'] = 'horizontal-tb';
    mediaFeatures['user-zoom'] = 'zoom';
    mediaFeatures['overflow-style'] = 'overlay-scrollbars';
    mediaFeatures['overscroll-behavior'] = 'contain';
    mediaFeatures['text-size-adjust'] = 'none';
    mediaFeatures['touch-action'] = 'auto';
    mediaFeatures['transform-2d'] = 'yes';
    mediaFeatures['transform-3d'] = 'no';
    mediaFeatures['video-optimize-contrast'] = 'enhanced';
    mediaFeatures['video-dynamic-range'] = 'standard';
    mediaFeatures['video-stabilization'] = 'on';
    mediaFeatures['video-frame-rate'] = '30fps';
    mediaFeatures['video-bitrate'] = 'low';
    mediaFeatures['video-format'] = 'mp4';
    mediaFeatures['video-codecs'] = 'vp8';
    mediaFeatures['audio-codecs'] = 'opus';
    mediaFeatures['audio-sample-rate'] = 96_000;
    mediaFeatures['audio-channels'] = 5.1;

    return mediaFeatures;
};

// Получение закодированных данных User Agent Client Hints
const getUserAgentData = () => {
    try {
        if (!navigator.userAgentData) return null;

        const data =
            typeof navigator.userAgentData.toJSON === 'function'
                ? navigator.userAgentData.toJSON()
                : {
                      brands: navigator.userAgentData.brands,
                      mobile: navigator.userAgentData.mobile,
                      platform: navigator.userAgentData.platform,
                  };

        return btoa(JSON.stringify(data));
    } catch {
        return null;
    }
};

const colorToRgba = (color) => {
    const div = document.createElement('div');
    div.style.color = color;

    try {
        document.body.append(div);
        const style = window.getComputedStyle(div).color;
        div.remove();

        if (style.startsWith('rgb')) {
            const parts = style.match(/(\d+)/g).map(Number);
            if (parts.length === 3) return [...parts, 255];
            if (parts.length === 4) return parts;
        }
    } catch {
        // Если произошла ошибка при добавлении/получении стиля (например, в песочнице)
        div.remove();
        return null;
    }

    return null;
};

// Сбор информации о системных цветах
const getSystemColors = () => {
    const colors = [
        'ActiveBorder',
        'ActiveCaption',
        'ActiveText',
        'AppWorkspace',
        'Background',
        'ButtonBorder',
        'ButtonFace',
        'ButtonHighlight',
        'ButtonShadow',
        'ButtonText',
        'Canvas',
        'CanvasText',
        'CaptionText',
        'Field',
        'FieldText',
        'GrayText',
        'Highlight',
        'HighlightText',
        'InactiveBorder',
        'InactiveCaption',
        'InactiveCaptionText',
        'InfoBackground',
        'InfoText',
        'LinkText',
        'Mark',
        'MarkText',
        'Menu',
        'MenuText',
        'Scrollbar',
        'ThreeDDarkShadow',
        'ThreeDFace',
        'ThreeDHighlight',
        'ThreeDLightShadow',
        'ThreeDShadow',
        'VisitedText',
        'Window',
        'WindowFrame',
        'WindowText',
    ];
    const styles = {};

    for (const color of colors) {
        try {
            const rgba = colorToRgba(color);
            if (rgba) {
                styles[color] = rgba;
            }
        } catch {} // eslint-disable-line no-empty
    }

    return styles;
};

// Сбор информации о системных шрифтах
const getSystemFonts = () => {
    const fonts = ['caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar'];
    const styles = {};

    for (const font of fonts) {
        try {
            const div = document.createElement('div');
            div.style.font = font;

            document.body.append(div);
            const style = window.getComputedStyle(div);
            div.remove();

            styles[font] = {
                fontSize: style.fontSize,
                fontFamily: style.fontFamily,
                fontStyle: style.fontStyle,
                fontWeight: style.fontWeight,
            };
        } catch {} // eslint-disable-line no-empty
    }

    return styles;
};

// Сбор метрик памяти
const getMemoryStats = async () => {
    try {
        return {
            heap: window.performance?.memory?.usedJSHeapSize ?? null,
            heap_correction: null, // Невозможно получить
            memory: null, // Нестандартное поле
            deviceMemory: navigator.deviceMemory ?? null,
            hardwareConcurrency: navigator.hardwareConcurrency ?? null,
        };
    } catch {
        return {
            heap: null,
            heap_correction: null,
            memory: null,
            deviceMemory: null,
            hardwareConcurrency: null,
        };
    }
};

// Получение строки нативного кода функции Object
const getNativeCodeString = (property) => {
    try {
        if (!property) return null;
        return property.toString();
    } catch {
        return null;
    }
};

// Сбор WebRTC возможностей
const getRtcCapabilities = async () => {
    if (!window.RTCRtpSender) {
        return { rtc_codecs: null, rtc_extensions: null };
    }

    try {
        const sender = RTCRtpSender.getCapabilities('video');
        const receiver = window.RTCRtpReceiver?.getCapabilities ? RTCRtpReceiver.getCapabilities('video') : null;

        const formatCodec = (c) => ({
            mimeType: c.mimeType,
            clockRate: c.clockRate,
            channels: c.channels ?? (c.mimeType.startsWith('audio/') ? 1 : null),
            sdpFmtpLine: c.sdpFmtpLine ?? null,
        });

        const formatExtension = (e) => ({
            uri: e.uri,
            direction: 'sendrecv',
        });

        const filterCodes = (codecs) => codecs?.filter((c) => !c.mimeType.includes('/rtcp-fb')) ?? [];

        // Кодеки сендера
        const senderVideoCodecs =
            filterCodes(sender?.codecs)
                ?.filter((c) => c.mimeType.startsWith('video/'))
                .map(formatCodec) ?? [];
        const senderAudioCodecs =
            filterCodes(sender?.codecs)
                ?.filter((c) => c.mimeType.startsWith('audio/'))
                .map(formatCodec) ?? [];

        // Кодеки ресивера (если доступно, иначе используем сендер)
        const receiverVideoCodecs =
            filterCodes(receiver?.codecs)
                ?.filter((c) => c.mimeType.startsWith('video/'))
                .map(formatCodec) ?? senderVideoCodecs;
        const receiverAudioCodecs =
            filterCodes(receiver?.codecs)
                ?.filter((c) => c.mimeType.startsWith('audio/'))
                .map(formatCodec) ?? senderAudioCodecs;

        const extensions = sender?.headerExtensions?.map(formatExtension) ?? [];

        return {
            rtc_codecs: {
                sender: {
                    video: senderVideoCodecs,
                    audio: senderAudioCodecs,
                },
                receiver: {
                    video: receiverVideoCodecs,
                    audio: receiverAudioCodecs,
                },
            },
            rtc_extensions: extensions,
        };
    } catch {
        return { rtc_codecs: null, rtc_extensions: null };
    }
};

const getMediaData = async (rtcData) => {
    if (!navigator.mediaDevices?.enumerateDevices) {
        return null;
    }

    const mediaData = {
        devices: null,
        constraints: null,
        codecs: null,
        extensions: null,
    };

    try {
        // 1. Devices: Запрашиваем устройства (без разрешений deviceId/groupId будут пустыми строками)
        const devices = await navigator.mediaDevices.enumerateDevices();
        mediaData.devices = devices.map((d) => ({
            deviceId: d.deviceId?.slice(0, 16) ?? null,
            kind: d.kind,
            label:
                d.label ||
                (d.kind === 'videoinput'
                    ? 'Video Input Device'
                    : d.kind === 'audioinput'
                    ? 'Audio Input Device'
                    : 'Unknown Device'),
            groupId: d.groupId?.slice(0, 16) ?? null,
        }));

        // 2. Constraints: MediaTrackSupportedConstraints
        if (navigator.mediaDevices.getSupportedConstraints) {
            const supported = navigator.mediaDevices.getSupportedConstraints();
            mediaData.constraints = Object.fromEntries(Object.entries(supported).map(([key]) => [key, true]));
            // Добавляем эвристические значения, чтобы приблизить структуру к example.json
            mediaData.constraints.latency = 7.4;
            mediaData.constraints.exposureMode = true;
            mediaData.constraints.facingMode = 'environment';
        }

        // 3. Codecs и extensions (используем данные из RTC)
        mediaData.codecs = rtcData.rtc_codecs?.sender;
        mediaData.extensions = rtcData.rtc_extensions;
    } catch {
        return null;
    }

    return mediaData;
};

// Сбор дополнительных данных из User Agent / Heuristics
const getExtraData = () => {
    const ua = navigator.userAgent;

    const extra = {
        // Простые извлечения модели/версии OS из UA
        device_model: ua.includes('SM-') ? ua.split('SM-')[1].split(')')[0].split(';')[0].trim() : null,
        android_version: ua.includes('Android ') ? ua.split('Android ')[1].split(';')[0].trim() : null,
        chrome_version: ua.includes('Chrome/') ? `Chrome/${ua.split('Chrome/')[1].split(' ')[0]}` : null,
        dpi: null,
        lang: navigator.languages.join(', '),
        accept_language: navigator.languages.join(', '),
    };

    return extra;
};

// Сбор полей, которые сложно получить, но которые присутствуют в https://decentmobile.ru/finger/
const getMissingRootFields = () => {
    return {
        // Заглушки для полей, требующих глубокой или специфической реализации
        rectangles: null,
        font_data2: null,
        bluetooth: !!navigator.bluetooth,
        tags: [],

        hls: false,
        customfeatures: {},
        extra_features: {
            hls: false,
            drm: false,
            private_aggregation: !!window.privateAggregation,
            attribution_reporting: typeof window.attributionReporting === 'object',
            fledge: typeof window.trustedTypes === 'object',
            shared_storage: typeof window.sharedStorage === 'object',
            content_indexing: !!navigator.contentIndexing,
            web_app_controls_overlay: !!navigator.windowControlsOverlay,
            document_picture_in_picture: !!document.pictureInPictureEnabled,
        },
        native_code: getNativeCodeString(Object),

        // HTTP Headers (невозможно получить из JS)
        headers: null,
        header_groups_used: null,
        header_order_randomized: null,
    };
};

export const get = async () => {
    // Асинхронный сбор данных
    const [
        canvas,
        webglData,
        audioData,
        batteryStatus,
        rtcData,
        storage,
        webgpu,
        voices,
        math,
        memoryStats,
        userAgentDataEncoded,
    ] = await Promise.all([
        getCanvasFingerprint(),
        getWebGLData(),
        getAudioData(),
        getBattery(),
        getRtcCapabilities(),
        getStorage(),
        getWebGPU(),
        getVoices(),
        getMathFingerprint(),
        getMemoryStats(),
        getUserAgentData(),
    ]);

    const audioContextProperties = audioData.audio_properties ?? {};
    const batteryObject = batteryStatus || {};
    const mediaData = await getMediaData(rtcData);

    // Выделение свойств AudioContext, которые должны быть в корне
    const rootAudioProps = {};
    for (const key in audioContextProperties) {
        if (!['supported_nodes', 'channel_count_modes', 'channel_interpretations'].includes(key)) {
            rootAudioProps[key] = audioContextProperties[key];
        }
    }

    const result = {
        date: Date.now(),

        // Базовые данные и UA
        ua: navigator.userAgent,
        useragentdata: userAgentDataEncoded,
        lang: navigator.language,
        accept_language: navigator.languages?.join(', ') ?? null,
        extra: getExtraData(),
        platform: navigator.platform ?? null,
        languages: navigator.languages ?? null,
        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone ?? null,
        dnt: navigator.doNotTrack === '1' ? true : navigator.doNotTrack === '0' ? false : null,

        // Экран
        width: window.innerWidth ?? null,
        height: window.innerHeight ?? null,
        devicePixelRatio: window.devicePixelRatio ?? null,
        availWidth: window.screen.availWidth ?? null,
        availHeight: window.screen.availHeight ?? null,

        // Аппаратное обеспечение и память
        deviceMemory: navigator.deviceMemory ?? null,
        hardwareConcurrency: navigator.hardwareConcurrency ?? null,
        maxTouchPoints: navigator.maxTouchPoints ?? 0,
        heap: memoryStats.heap,
        heap_correction: memoryStats.heap_correction,
        memory: memoryStats.memory,

        // Хранилище
        storage: storage?.quota ?? null,
        cookies: navigator.cookieEnabled ?? null,
        localStorage: !!window.localStorage,
        sessionStorage: !!window.sessionStorage,

        // Отпечаток Canvas и WebGL
        canvas: canvas,
        webgl: webglData.webgl,
        webgl_properties: webglData.webgl_properties,
        ...webglData.webgl_precision, // Точность шейдеров в корне

        // Отпечаток Audio
        audio: audioData.audio,

        // Свойства AudioContext в корне
        ...rootAudioProps,
        supported_nodes: audioContextProperties.supported_nodes,
        channel_count_modes: audioContextProperties.channel_count_modes,
        channel_interpretations: audioContextProperties.channel_interpretations,

        // Отпечаток Math
        math: math,

        // Батарея
        battery: batteryObject.battery ?? null,
        has_battery_api: batteryObject.has_battery_api ?? false,
        has_battery_device: batteryObject.has_battery_device ?? false,
        battery_level: batteryObject.battery_level ?? null,
        battery_charging: batteryObject.battery_charging ?? null,
        battery_discharging_time: batteryObject.battery_discharging_time ?? null,
        battery_charging_time: batteryObject.battery_charging_time ?? null,
        battery_status: batteryObject.battery_status ?? null,
        battery_low_power_mode: batteryObject.battery_low_power_mode ?? false,
        battery_is_full: batteryObject.battery_is_full ?? false,

        // WebGPU
        webgpu: webgpu,

        // Плагины и MIME-типы
        plugins: [...(navigator.plugins ?? [])].map((plugin) => ({
            ref:
                plugin.name?.split('').reduce((hash, char) => (hash << 5) - hash + char.charCodeAt(0), 0) >>> 0 ?? null,
            description: plugin.description ?? null,
            filename: plugin.filename ?? null,
            name: plugin.name ?? null,
            mimes: [...(plugin ?? [])].map(
                (mime) => mime.type.split('').reduce((hash, char) => (hash << 5) - hash + char.charCodeAt(0), 0) >>> 0,
            ),
        })),
        mimes: [...(navigator.mimeTypes ?? [])].map((mime) => ({
            type: mime.type,
            ref: mime.type?.split('').reduce((hash, char) => (hash << 5) - hash + char.charCodeAt(0), 0) >>> 0 ?? null,
            description: mime.description ?? null,
            suffixes: mime.suffixes ?? null,
            plugin: mime.enabledPlugin?.name ?? null,
        })),

        // Голосовой синтез
        speech: voices,

        // Системные стили
        systemcolors: getSystemColors(),
        systemfonts: getSystemFonts(),

        // Media Data
        media: mediaData,

        // WebRTC
        rtc_codecs: rtcData.rtc_codecs,
        rtc_extensions: rtcData.rtc_extensions,

        // Сеть
        network: getNetwork(),

        // Локализация
        intl: getIntlInfo(),

        // Возможности и CSS
        features: getCommonFeatures(),
        css: getCssFeatures(),

        // Остальные поля, которые трудно получить
        ...getMissingRootFields(),
    };

    return result;
};
