// Define types for the dataset structure
interface Dataset {
    meta: {
        languages: string[];
        generated: string;
    };
    fault_codes: {
        [code: string]: {
            internal?: string;
            [lang: string]: {
                title: string;
                summary?: string;
            } | string | undefined;
        };
    };
    translations: {
        [lang: string]: {
            [key: string]: string;
        };
    };
    general_translations?: {
        [lang: string]: {
            [key: string]: string;
        };
    };
    attribute_mappings?: Record<string, string>;
    status_map?: Record<string, string>;
}

export class RoborockLocales {
	private dataset: Dataset;
	private attributeMap: Record<string, string> = {};

	constructor(dataset: Dataset) {
		this.dataset = dataset;

		// Merge attribute mappings from dataset if available
		if (this.dataset.attribute_mappings) {
			this.attributeMap = {
				...this.attributeMap,
				...this.dataset.attribute_mappings
			};
		}
	}

	private isDatasetValid(): boolean {
		return !!this.dataset;
	}

	/**
     * Get translated text for a generic key.
     * @param key The translation key (e.g., "setting_consumable_mop")
     * @param language The language code (e.g., "en", "de")
     */
	public getText(key: string, language: string = "en"): string {
		if (!this.isDatasetValid()) return key;
		// Normalize ioBroker language codes to dataset keys
		let normalizedLang = language.toLowerCase();
		if (normalizedLang === "zh-cn") normalizedLang = "zh";
		if (normalizedLang === "zh-tw") normalizedLang = "zh-hant";

		// Try translations first
		// Look up using a case-insensitive check to handle zh-Hant vs zh-hant
		const datasetLangKey = Object.keys(this.dataset.translations || {}).find(
			(k) => k.toLowerCase() === normalizedLang.toLowerCase()
		);
		const langData = datasetLangKey ? this.dataset.translations?.[datasetLangKey] : null;

		if (langData && langData[key]) {
			return langData[key];
		}
		// Try general_translations
		const genData = this.dataset.general_translations?.[language];
		if (genData && genData[key]) {
			return genData[key];
		}
		// Fallback to English translations
		const enData = this.dataset.translations?.["en"];
		if (enData && enData[key]) {
			return enData[key];
		}
		// Fallback to English general_translations
		const enGenData = this.dataset.general_translations?.["en"];
		if (enGenData && enGenData[key]) {
			return enGenData[key];
		}
		return key; // Return key if not found
	}

	/**
	 * Get all available translations for a key.
	 */
	public getTranslations(key: string): Record<string, string> {
		const translations: Record<string, string> = {};
		if (!this.isDatasetValid()) return translations;

		// Collect from regular translations
		if (this.dataset.translations) {
			for (const lang in this.dataset.translations) {
				const langDict = this.dataset.translations[lang];
				if (langDict && langDict[key]) {
					translations[lang] = langDict[key];
				}
			}
		}

		// Collect from general translations if missing in specific language
		if (this.dataset.general_translations) {
			for (const lang in this.dataset.general_translations) {
				const langDict = this.dataset.general_translations[lang];
				if (langDict && langDict[key] && !translations[lang]) {
					translations[lang] = langDict[key];
				}
			}
		}

		return translations;
	}

	/**
     * Get translated error text.
     * @param errorCode The error code
     * @param language The language code
     */
	public getErrorText(errorCode: string | number, language: string = "en"): string {
		let normalizedLang = language.toLowerCase();
		if (normalizedLang === "zh-cn") normalizedLang = "zh";
		if (normalizedLang === "zh-tw") normalizedLang = "zh-hant";

		const codeStr = errorCode.toString();
		const fault = this.dataset.fault_codes?.[codeStr];
		if (fault) {
			const getTitle = (entry: any) =>
				(typeof entry === "object" && entry !== null) ? entry.title : entry;

			const langTitle = getTitle(fault[normalizedLang]);
			if (langTitle && typeof langTitle === "string") return langTitle;

			const enTitle = getTitle(fault["en"]);
			if (enTitle && typeof enTitle === "string") return enTitle;
		}
		return `Error ${errorCode}`;
	}

	/**
	 * Get the translation key for a given device attribute.
	 * Returns undefined if no mapping is found.
	 */
	public getAttributeKey(attribute: string): string | undefined {
		if (!this.isDatasetValid()) return undefined;
		if (this.dataset.attribute_mappings) {
			return this.dataset.attribute_mappings[attribute];
		}
		return undefined;
	}

	/**
	 * Get the translation key for a status code.
	 */
	public getStatusKey(statusCode: string | number): string | undefined {
		if (this.dataset.status_map) {
			return this.dataset.status_map[statusCode.toString()];
		}
		return undefined;
	}

	public getErrorCodes(): number[] {
		if (!this.dataset.fault_codes) return [];
		return Object.keys(this.dataset.fault_codes).map(k => parseInt(k)).filter(k => !isNaN(k) && k !== 0);
	}

	/**
     * Get the mapped value for Cloth State (Mop Mount).
     * Mirrors logic: if not 0, show warning.
     * @param value The cloth_state value
     * @param language The language code
     */
	public getClothStateText(value: number, language: string = "en"): string {
		if (value === 0) {
			return this.getText("setting_consumable_mop", language) + ": " + this.getText("common_on", language);
		} else if (value === 1) {
			// Removed / Not Installed
			return this.getText("setting_consumable_change_tips7", language);
		} else {
			// 2 or other = Dirty / abnormal? Default to generic warning
			return this.getText("setting_consumable_change_tips7", language) + ` (${value})`;
		}
	}

	public getWaterBoxModeText(value: number, language: string = "en"): string {
		switch (value) {
			case 200: return this.getText("home_clean_water_close", language);
			case 201: return this.getText("home_clean_water_low", language);
			case 202: return this.getText("home_clean_water_medium", language);
			case 203: return this.getText("home_clean_water_high", language);
			default: return this.getText("unknown", language) || `Water ${value}`;
		}
	}

	public getFanPowerText(value: number, language: string = "en"): string {
		switch (value) {
			case 101: return this.getText("home_clean_wind_silence", language);
			case 102: return this.getText("home_clean_wind_standard", language);
			case 103: return this.getText("home_clean_wind_strong", language);
			case 104: return this.getText("home_clean_wind_super_strong", language);
			case 105: return this.getText("home_clean_wind_max", language);
			default: return this.getText("unknown", language) || `Fan ${value}`;
		}
	}

	public getMopModeText(value: number, language: string = "en"): string {
		switch (value) {
			case 300: return this.getText("home_clean_route_standard", language);
			case 301: return this.getText("home_clean_route_carefully", language);
			case 302: return this.getText("home_clean_route_deep_plus", language);
			case 303: return this.getText("home_clean_route_custom", language);
			default: return this.getText("unknown", language) || `MopMode ${value}`;
		}
	}

	public getName(attribute: string, language: string = "en"): string | undefined {
		const key = this.attributeMap[attribute];
		if (key) {
			return this.getText(key, language);
		}
		return undefined;
	}

	/**
	 * Get all translations for an attribute name (for common.name object)
	 */
	public getNameAll(attribute: string): ioBroker.StringOrTranslated {
		const key = this.attributeMap[attribute];
		if (key) {
			const trans = this.getTranslations(key);
			if (Object.keys(trans).length > 0) {
				trans["en"] = trans["en"] || key;
				return trans as ioBroker.StringOrTranslated;
			}
		}

		// If translation missing, return key (if mapped) or raw attribute name as fallback string.
		// This ensures we can use keys like 'water_tank' that are defined in words.js but not in the dataset.
		return key || attribute;
	}
}
