import { formatNumber } from "@angular/common";
import { TranslateService } from "@ngx-translate/core";
import { Currency, CurrentData, EdgeConfig, GridMode, Limiter14aRestriction, RippleControlReceiverRestrictionLevel, Utils, } from "../../shared";

import { EnabledDisabledState } from "../../type/general";
import { Language } from "../../type/language";
import { NumberUtils } from "../../utils/number/number-utils";
import { TimeUtils } from "../../utils/time/timeutils";
import { Formatter } from "./formatter";

export type Converter = (value: number | string | null) => string;

export namespace Converter {
    /**
     * Converts a number or string value to its string representation.
     *
     * @param value The value
     * @returns The value or empty string for null
     */
    export const TO_STRING: Converter = (value): string => {
        if (value === null) {
            return "";
        }
        return "" + value;
    };

    /**
     * Appends a percent sign to a number or string value.
     *
     * Returns an empty string when the input is null.
     *
     * @param value The value to format.
     * @returns The value with a ' %' suffix, or an empty string for null.
     */
    export const TO_PERCENT: Converter = (value): string => {
        if (value === null) {
            return "";
        }
        return "" + value + " %";
    };

    export const IF_NUMBER = (value: number | string | null, callback: (number: number) => string) => {
        if (typeof value === "number") {
            return callback(value);
        }
        return "-"; // null or string
    };

    export const IF_STRING = (value: number | string | null, callback: (text: string) => string) => {
        if (typeof value === "string") {
            return callback(value);
        }
        return "-"; // null or number
    };

    export const IF_NUMBER_OR_STRING = (
        value: number | string | null,
        callback: (value: number | string) => string,
    ) => {
        if (typeof value === "number" || typeof value === "string") {
            return callback(value);
        }
        return "-"; // null or string
    };

    /**
     * Converter for Grid-Buy-Power.
     *
     * @param value The ActivePower value (positive, negative or null)
     * @returns Formatted positive value; zero for negative; '-' for null
     */
    export const GRID_BUY_POWER_OR_ZERO: Converter = (raw): string => {
        return IF_NUMBER(raw, (value) => (value >= 0 ? Formatter.FORMAT_WATT(value) : Formatter.FORMAT_WATT(0)));
    };

    /**
     * Converter for Grid-Sell-Power.
     *
     * @param value The ActivePower value (positive, negative or null)
     * @returns Formatted inverted negative value; zero for positive; '-' for null
     */
    export const GRID_SELL_POWER_OR_ZERO: Converter = (raw): string => {
        return IF_NUMBER(raw, (value) =>
            value <= 0 ? Formatter.FORMAT_WATT(Math.abs(value)) : Formatter.FORMAT_WATT(0),
        );
    };

    /**
     * Converter for ActivePower; always returns the formatted positive value in [W]
     *
     * @param value The ActivePower value (positive, negative or null)
     * @returns Formatted absolute value; '-' for null
     */
    export const POSITIVE_POWER_IN_W: Converter = (raw): string => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_WATT(Math.abs(value)));
    };

    /**
     * Converter for ActivePower; always returns the formatted positive value converted to [kW]
     *
     * @param value The ActivePower value (positive, negative or null)
     * @returns Formatted absolute value; '-' for null
     */
    export const POSITIVE_POWER_IN_KILO_WATT: Converter = (raw): string => {
        return IF_NUMBER(raw, (value) => Converter.POWER_IN_KILO_WATT(Math.abs(value)));
    };

    /**
     * Formats a Power value as Watt [W].
     *
     * Value 1000 -> "1.000 W". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const POWER_IN_WATT: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_WATT(value));
    };

    /**
     * Formats a apparent power value as Volt-Ampere [VA].
     *
     * Value 1000 -> "1.000 VA". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const POWER_IN_VOLT_AMPERE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_VOLT_AMPERE(value));
    };

    /**
     * Formats a apparent power value as Volt-Ampere [VA].
     *
     * Value 1000 -> "1.000 VA". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const POWER_IN_VOLT_AMPERE_REACTIVE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_VOLT_AMPERE_REACTIVE(value));
    };

    /**
     * Formats a Power value as Watt [kW].
     *
     * Value 1000 -> "1 kW". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const POWER_IN_KILO_WATT: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => {
            const dividedNumber = NumberUtils.divideSafely(value, 1000);
            if (dividedNumber == null) {
                return "-";
            }
            return Formatter.FORMAT_KILO_WATT(dividedNumber);
        });
    };

    /**
     * Formats a Power value as Kilowatt [kW].
     *
     * Value 1 -> "1 kW". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const POWER_IN_KILO_WATT_AS_KW: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_KILO_WATT(value));
    };

    /**
     * Formats a Energy value as Kilo watt hours [kWh].
     *
     * Value 1000 -> "1,00 kWh". Value null -> "-".
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const WATT_HOURS_IN_KILO_WATT_HOURS: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_KILO_WATT_HOURS(value / 1000));
    };

    /**
     * Formats a Energy value as Watt hours [Wh].
     *
     * Value 1000 -> "1000 Wh". Value null -> "-".
     *
     * @param value The energy value
     * @returns Formatted value; '-' for null
     */
    export const TO_WATT_HOURS: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_WATT_HOURS(value));
    };

    /**
     * Formats a Energy value as Kilo watt hours [kWh].
     *
     * Value 1000 -> "1000 kWh". Value null -> "-".
     *
     * @param value The energy value
     * @returns Formatted value; '-' for null
     */
    export const TO_KILO_WATT_HOURS: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_KILO_WATT_HOURS(value));
    };

    export const STATE_IN_PERCENT: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_PERCENT(value));
    };

    export const TEMPERATURE_IN_DEGREES: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_CELSIUS(value));
    };

    export const DEZIDEGREE_CELSIUS_TO_DEGREE_CELSIUS: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => {
            const dividedValue = NumberUtils.divideSafely(value, 10);

            if (dividedValue == null) {
                return value + " °C";
            }

            return Formatter.FORMAT_CELSIUS(dividedValue);
        });
    };

    /**
     * Formats a Voltage value as Volt [V].
     *
     * Value 1000 -> "1.000 V". Value null -> "-".
     *
     * @param value The voltage value
     * @returns Formatted value; '-' for null
     */
    export const VOLTAGE_IN_MILLIVOLT_TO_VOLT: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_VOLT(value / 1000));
    };

    export const VOLTAGE_TO_VOLT: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_VOLT(value));
    };

    /**
     * Formats a Current value as Ampere [A].
     *
     * Value 1000 -> "1.000 A". Value null -> "-".
     *
     * @param value The current value
     * @returns Formatted value; '-' for null
     */
    export const CURRENT_IN_MILLIAMPERE_TO_AMPERE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_AMPERE(value / 1000));
    };

    /**
     * Converts a formatted current value to the absolute value.
     *
     * Value -1000 -> "1.000 A". Value 1000 -> "1.000 A". Value null -> "-".
     *
     * @param value The current value
     * @returns Formatted value; '-' for null
     */
    export const CURRENT_IN_MILLIAMPERE_TO_ABSOLUTE_AMPERE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_AMPERE(Math.abs(value) / 1000));
    };

    export const ONLY_POSITIVE_POWER_AND_NEGATIVE_AS_ZERO: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => (value <= 0 ? Formatter.FORMAT_WATT(0) : Formatter.FORMAT_WATT(value)));
    };

    export const CURRENT_TO_AMPERE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_AMPERE(value));
    };

    /**
     * Formats a monetary value (e.g. price or cost in currency per MWh) as currency per kWh.
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const CURRENCY_PER_MWH_TO_KWH = (currency: Currency.Label): Converter => {
        return (raw): string => {
            return IF_NUMBER(raw, (value) => {
                if (value == null) {
                    return "-";
                }
                const locale: string = Language.getCurrentLanguage().i18nLocaleKey;
                return formatNumber(value / 10, locale, "1.0-2") + " " + Currency.getCurrencyLabelByCurrency(currency);
            });
        };
    };

    /**
     * Formats a monetary value (e.g. price or cost in currency per kWh) as currency per kWh.
     *
     * @param value The power value
     * @returns Formatted value; '-' for null
     */
    export const CURRENCY_PER_KWH_TO_KWH = (currency: Currency.Label): Converter => {
        return (raw): string => {
            return IF_NUMBER(raw, (value) => {
                if (value == null) {
                    return "-";
                }
                const locale: string = Language.getCurrentLanguage().i18nLocaleKey;
                return formatNumber(value, locale, "1.0-2") + " " + Currency.getCurrencyLabelByCurrency(currency);
            });
        };
    };

    export const CONVERT_TO_GRID_MODE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => {
            return GridMode[value].toLowerCase();
        });
    };

    export const CONVERT_TO_EXTERNAL_RECEIVER_LIMITATION: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => {
            const limitation = () => {
                switch (value) {
                    case 1:
                        return "0";
                    case 2:
                        return "30";
                    case 4:
                        return "60";
                    case 8:
                        return "100";
                    default:
                        return null;
                }
            };

            if (limitation() == null) {
                return "-";
            }

            return Utils.CONVERT_TO_PERCENT(limitation());
        });
    };

    /**
     * Hides the actual value, always returns empty string.
     *
     * @param value The value
     * @returns Always ""
     */
    export const HIDE_VALUE: Converter = (ignore): string => {
        return "";
    };

    /**
     * Converts the grid mode value into a translated label.
     *
     * @param translate The current language to be translated to
     * @returns Converted value
     */
    export const GRID_MODE_OFF_GRID_OR_GENERATOR = (translate: TranslateService): Converter => {
        return (value: any): string => {
            switch (value) {
                case 2:
                    return translate.instant("GENERAL.OFF_GRID");
                case 3:
                    return translate.instant("GENERAL.GENERATOR_ON");
                case -1:
                default:
                    return translate.instant("");
            }
        };
    };

    /**
     * Calculates the otherPower: the power, that can't be assigned to a consumer
     *
     * @param evcss The evcss
     * @param consumptionMeters The "CONSUMPTION_METERED" meters
     * @param currentData The currentData
     * @returns The otherPower
     */
    export const CALCULATE_CONSUMPTION_OTHER_POWER = (
        evcss: EdgeConfig.Component[],
        consumptionMeters: EdgeConfig.Component[],
        currentData: CurrentData,
    ): number => {
        const activePowerTotal = currentData.allComponents["_sum/ConsumptionActivePower"] ?? null;
        const evcsChargePowerTotal =
            evcss
                ?.map((evcs) => currentData.allComponents[evcs.id + "/ChargePower"])
                ?.reduce((prev, curr) => Utils.addSafely(prev, curr), 0) ?? null;
        const consumptionMeterActivePowerTotal =
            consumptionMeters
                ?.map((meter) => currentData.allComponents[meter.id + "/ActivePower"])
                ?.reduce((prev, curr) => Utils.addSafely(prev, curr), 0) ?? null;

        const otherPower = NumberUtils.subtractSafely(
            activePowerTotal,
            Utils.addSafely(evcsChargePowerTotal, consumptionMeterActivePowerTotal),
        );

        return otherPower ?? 0;
    };

    export const GRID_STATE_TO_MESSAGE = (translate: TranslateService, currentData: CurrentData): string => {
        const gridMode = currentData.allComponents["_sum/GridMode"];
        const restrictionMode14a =
            currentData.allComponents["ctrlEssLimiter14a0/RestrictionMode"] ?? Limiter14aRestriction.NO_RESTRICTION;
        const restrictionModeRcr =
            currentData.allComponents["ctrlEssRippleControlReceiver0/RestrictionMode"] ??
            RippleControlReceiverRestrictionLevel.NO_RESTRICTION;

        if (gridMode === GridMode.GENERATOR) {
            return translate.instant("GENERAL.GENERATOR_SUPPLY");
        }

        if (gridMode === GridMode.OFF_GRID) {
            return translate.instant("GRID_STATES.OFF_GRID");
        }

        if (restrictionMode14a) {
            return translate.instant(
                restrictionModeRcr !== RippleControlReceiverRestrictionLevel.NO_RESTRICTION
                    ? "GRID_STATES.GRID_LIMITATION"
                    : "GRID_STATES.CONSUMPTION_LIMITATION",
            );
        }

        if (restrictionModeRcr !== RippleControlReceiverRestrictionLevel.NO_RESTRICTION) {
            return translate.instant("GRID_STATES.FEED_IN_LIMITATION");
        }

        return translate.instant("GRID_STATES.NO_EXTERNAL_LIMITATION");
    };

    export const RCR_RESTRICTION_LEVEL_TO_MESSAGE = (currentData: CurrentData): string => {
        return `${currentData.allComponents["ctrlEssRippleControlReceiver0/RestrictionMode"]} %`;
    };

    export const ON_OFF = (translate: TranslateService) => {
        return (raw: number | string | null): string => {
            return translate.instant(raw == 1 ? "GENERAL.ON" : "GENERAL.OFF");
        };
    };

    export const HEAT_PUMP_STATES = (translate: TranslateService) => {
        return (raw: number | null): string => {
            switch (raw) {
                case -1:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.UNDEFINED");
                case 0:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.LOCK");
                case 1:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.NORMAL_OPERATION_SHORT");
                case 2:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.SWITCH_ON_REC_SHORT");
                case 3:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.SWITCH_ON_COM_SHORT");
                default:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEAT_PUMP.UNDEFINED");
            }
        };
    };

    export const FORMAT_SECONDS_TO_DURATION: any = (locale: string) => {
        return (raw: number): any => {
            return IF_NUMBER(raw, (value) => {
                return TimeUtils.formatSecondsToDuration(value, locale);
            });
        };
    };

    /**
     * Converts Industrial-State
     *
     * @param translate The current language to be translated to
     * @returns Converted value
     */
    export const CONVERT_INDUSTRIAL_STATE = (translate: TranslateService) => {
        return (value: any): string => {
            switch (value) {
                case 10:
                    return translate.instant("GENERAL.STATE_MACHINE.GO_RUNNING");
                case 11:
                    return translate.instant("GENERAL.STATE_MACHINE.RUNNING");
                case 20:
                    return translate.instant("GENERAL.STATE_MACHINE.GO_STOPPED");
                case 21:
                    return translate.instant("GENERAL.STATE_MACHINE.STOPPED");
                case 30:
                    return translate.instant("GENERAL.STATE_MACHINE.ERROR");
                case -1:
                default:
                    return translate.instant("GENERAL.STATE_MACHINE.UNDEFINED");
            }
        };
    };

    /**
     * Converts the runState of the heating element to the tranlsated state
     *
     * @param translate The current language to be translated to
     * @returns Converted value
     */
    export const CONVERT_HEATING_ELEMENT_RUNSTATE = (translate: TranslateService) => {
        return (value: any): string => {
            switch (value) {
                case 0:
                    return translate.instant("GENERAL.INACTIVE");
                case 1:
                    return translate.instant("GENERAL.ACTIVE");
                case 2:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEATINGELEMENT.ACTIVE_FORCED");
                case 3:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEATINGELEMENT.ACTIVED_FORCED_LIMIT");
                case 4:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEATINGELEMENT.DONE");
                case 5:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEATINGELEMENT.UNREACHABLE");
                case 6:
                    return translate.instant("EDGE.INDEX.WIDGETS.HEATINGELEMENT.CALIBRATION");
                default:
                    return "";
            }
        };
    };

    export const CONVERT_TO_BAR: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_BAR(value));
    };

    export const CONVERT_TO_ENABLED_DISABLED_STATE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => EnabledDisabledState[value]);
    };

    export const CONVERT_TO_HEATING_STATE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => EnabledDisabledState[value]);
    };

    export const CONVERT_TO_HOUR: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_HOUR(value));
    };

    export const CONVERT_MINUTE_TO_TIME_OF_DAY = (translate: TranslateService, locale: string): Converter => {
        const converter = TimeUtils.CONVERT_MINUTE_TO_TIME_OF_DAY(translate, locale);

        return (value: number | string | null): string => {
            if (typeof value !== "number") {
                return "";
            }

            return converter(value);
        };
    };

    export const CONTROLLER_PROPERTY_MODES = (translate: TranslateService): Converter => {
        return (raw): string => {
            return IF_STRING(raw, (value) => {
                switch (value) {
                    case "AUTOMATIC":
                        return translate.instant("GENERAL.AUTOMATIC");
                    case "MANUAL":
                        return translate.instant("GENERAL.MANUALLY");
                    case "MANUAL_ON":
                    case "ON":
                        return translate.instant("GENERAL.ON");
                    case "MANUAL_OFF":
                    case "OFF":
                        return translate.instant("GENERAL.OFF");
                    case "CHARGE_ONCE":
                        return translate.instant("GENERAL.CHARGE_ONCE");
                    case "DISCHARGE_ONCE":
                        return translate.instant("GENERAL.DISCHARGE_ONCE");
                }
            });
        };
    };

    /**
     * Converts a value to WattHours [Wh]
     *
     * @param value The value from passed value in html
     * @returns Converted value
     */
    export const CONVERT_TO_WATTHOURS = (locale?: string): Converter => {
        return (raw) =>
            IF_NUMBER(raw, (value) => {
                const usedLocale =
                    locale ?? (Language.getByKey(localStorage.LANGUAGE) ?? Language.DEFAULT).i18nLocaleKey;

                return formatNumber(value, usedLocale, "1.0-1") + " Wh";
            });
    };

    /**
     * Converts a value in Volt Ampere Reactive [var] to Kilo Volt Ampere Reactive [kvar].
     *
     * @param value The value from passed value in html
     * @returns Converted value
     */
    export const CONVERT_VAR_TO_KILO_VOLT_AMPERE_REACTIVE: Converter = (raw) => {
        return IF_NUMBER(raw, (value) => Formatter.FORMAT_KILO_VOLT_AMPERE_REACTIVE(Math.max(value)));
    };
}

export enum State {
    ON = 0,
    OFF = 1,
    NO_DISCHARGE = 2,
    CHARGE_FROM_GRID = 3,
    DISCONNECTED = 4,
    CONNECTED = 5,
}
