// *****************************************************************************
// Copyright (C) 2021 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import * as assert from 'assert';
import { DeeplParameters, DeeplResponse } from './deepl-api';
import { LocalizationManager, LocalizationOptions } from './localization-manager';

describe('localization-manager#translateLanguage', () => {

    async function mockLocalization(parameters: DeeplParameters): Promise<DeeplResponse> {
        return {
            translations: parameters.text.map(value => ({
                detected_source_language: '',
                text: `[${value}]`
            }))
        };
    }

    const manager = new LocalizationManager(mockLocalization);
    const defaultOptions: LocalizationOptions = {
        authKey: '',
        freeApi: false,
        sourceFile: '',
        targetLanguages: ['EN']
    };

    it('should translate a single value', async () => {
        const input = {
            key: 'value'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[value]'
        });
    });

    it('should translate nested values', async () => {
        const input = {
            a: {
                b: 'b'
            },
            c: 'c'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            a: {
                b: '[b]'
            },
            c: '[c]'
        });
    });

    it('should not override existing targets', async () => {
        const input = {
            a: 'a'
        };
        const target = {
            a: 'b'
        };
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            a: 'b'
        });
    });

    it('should keep placeholders intact', async () => {
        const input = {
            key: '{1} {0}'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[{1} {0}]'
        });
    });

    it('should preserve angle brackets in text', async () => {
        const input = {
            key: 'Use <b>bold</b> text'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[Use <b>bold</b> text]'
        });
    });

    it('should preserve ampersands in text', async () => {
        const input = {
            key: 'foo & bar'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[foo & bar]'
        });
    });

    it('should preserve angle brackets combined with placeholders', async () => {
        const input = {
            key: '{0} is <b>greater</b> than {1}'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[{0} is <b>greater</b> than {1}]'
        });
    });

    it('should not wrap empty braces as placeholders', async () => {
        const input = {
            key: 'empty {} braces'
        };
        const target = {};
        await manager.translateLanguage(input, target, 'EN', defaultOptions);
        assert.deepStrictEqual(target, {
            key: '[empty {} braces]'
        });
    });

    it('should re-translate forced keys even if they exist in target', async () => {
        const input = { a: 'new-a' };
        const target = { a: 'old-translation' };
        await manager.translateLanguage(input, target, 'EN', {
            ...defaultOptions,
            forceKeys: new Set(['a'])
        });
        assert.deepStrictEqual(target, { a: '[new-a]' });
    });

    it('should re-translate nested forced keys', async () => {
        const input = { a: { b: 'new-b' } };
        const target = { a: { b: 'old-translation' } };
        await manager.translateLanguage(input, target, 'EN', {
            ...defaultOptions,
            forceKeys: new Set(['a/b'])
        });
        assert.deepStrictEqual(target, { a: { b: '[new-b]' } });
    });

    it('should not re-translate keys not in force set', async () => {
        const input = { a: 'new-a', b: 'new-b' };
        const target = { a: 'old-a', b: 'old-b' };
        await manager.translateLanguage(input, target, 'EN', {
            ...defaultOptions,
            forceKeys: new Set(['a'])
        });
        assert.deepStrictEqual(target, { a: '[new-a]', b: 'old-b' });
    });

    it('should pass context to the localization function', async () => {
        let receivedContext: string | undefined;
        const contextCapture = new LocalizationManager(async (parameters: DeeplParameters) => {
            receivedContext = parameters.context;
            return mockLocalization(parameters);
        });
        const input = { key: 'value' };
        const target = {};
        await contextCapture.translateLanguage(input, target, 'EN', defaultOptions);
        assert.ok(receivedContext, 'context should be defined');
        assert.ok(receivedContext!.includes('IDE'), 'context should mention IDE');
    });
});
