import { Cache } from "react-native-cache";
import { LIBRETRANSLATE_BASE_URL } from "@/constants/api";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Settings } from "../lib/settings";

type language_t = string;

const cache = new Cache({
    namespace: "translation_terrace",
    policy: {
        maxEntries: 50000, // if unspecified, it can have unlimited entries
        stdTTL: 0 // the standard ttl as number in seconds, default: 0 (unlimited)
    },
    backend: AsyncStorage
});

export type language_matrix_entry = {
    code: string,
    name: string,
    targets: string []
}

export type language_matrix = {
    [key:string] : language_matrix_entry
}

export async function fetchWithTimeout(url : string, options : RequestInit, timeout = 5000) : Promise<Response> {
    return Promise.race([
        fetch(url, options),
        new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
    ]);
}

export class LanguageServer {
    constructor(public baseUrl : string) {}

    async fetchLanguages(timeout = 500) : Promise<language_matrix> {
        let data  = {};
        const res = await fetchWithTimeout(this.baseUrl + "/languages", {
            headers: {
                "Content-Type": "application/json"
            }
        }, timeout);
        try {
            data = await res.json();
        } catch (e) {
            throw new Error(`Parsing data from ${await res.text()}: ${e}`)
        }
        try {
            return Object.fromEntries(
                Object.values(data as language_matrix_entry []).map((obj : language_matrix_entry) => {
                    return [
                        obj["code"],
                        obj,
                    ]
                })
            )
        } catch(e) {
            throw new Error(`Can't extract values from data: ${JSON.stringify(data)}`)
        }
    }

    static async getDefault() {
        const settings = await Settings.getDefault();
        return new LanguageServer(await settings.getLibretranslateBaseUrl() || LIBRETRANSLATE_BASE_URL);
    }
}

export class Translator {
    constructor(public source : language_t, public defaultTarget : string = "en", private _languageServer : LanguageServer) {
    }

    get languageServer() {
        return this._languageServer;
    }

    async translate(text : string, target : string|undefined = undefined) {
        const url = this._languageServer.baseUrl + `/translate`;
        const res = await fetch(url, {
            method: "POST",
            body: JSON.stringify({
                q: text,
                source: this.source,
                target: target || this.defaultTarget,
                format: "text",
                alternatives: 3,
                api_key: ""
            }),
            headers: { "Content-Type": "application/json" }
        });

        
        const data = await res.json();
        console.log(data)
        return data.translatedText
    }

    static async getDefault(defaultTarget: string | undefined = undefined) {
        const settings = await Settings.getDefault();
        const source = await settings.getHostLanguage();
        return new Translator(source, defaultTarget, await LanguageServer.getDefault())
    }
}

export class CachedTranslator extends Translator {
    async translate (text : string, target : string|undefined = undefined) {
        const targetKey = target || this.defaultTarget;
        // console.debug(`Translating from ${this.source} -> ${targetKey}`)
        const key1 = `${this.source}::${targetKey}::${text}`
        const tr1 = await cache.get(key1);
        if (tr1) return tr1;
        const tr2 = await super.translate(text, target);
        const key2 = `${this.source}::${targetKey}::${text}`
        await cache.set(key2, tr2);
        return tr2;
    }

    static async getDefault(defaultTarget: string | undefined = undefined) {
        const settings = await Settings.getDefault();
        const source = await settings.getHostLanguage();
        return new CachedTranslator(source, defaultTarget, await LanguageServer.getDefault())
    }
}