// @ts-strict-ignore
import { Edge } from "../components/edge/edge";
import { EdgeConfig } from "../components/edge/edgeconfig";
import { TEnumKeys } from "./utility";

export enum WidgetClass {
    Energymonitor,
    Common_Autarchy,
    Common_Selfconsumption,
    Storage,
    Grid,
    Common_Production,
    Consumption,
    Controller_ChannelThreshold,
    Controller_Io_Digital_Outputs,
}

export enum WidgetNature {
    "io.openems.edge.evcs.api.Evcs",
    "io.openems.edge.heat.api.ManagedHeatElement",
    "io.openems.impl.controller.channelthreshold.ChannelThresholdController", // TODO deprecated
    "io.openems.edge.io.api.DigitalInput",
}

export enum WidgetFactory {
    "Evse.Controller.Single",
    "Evse.Controller.Cluster",
    "Controller.Api.ModbusTcp.ReadWrite",
    "Controller.Asymmetric.PeakShaving",
    "Controller.BraiinsOS.Single",
    "Controller.ChannelThreshold",
    "Controller.CHP.SoC",
    "Controller.Clever-PV",
    "Controller.Ess.DelayedSellToGrid",
    "Controller.Ess.FixActivePower",
    "Controller.Ess.GridOptimizedCharge",
    "Controller.Ess.Time-Of-Use-Tariff.Discharge",
    "Controller.Ess.Time-Of-Use-Tariff",
    "Controller.IO.ChannelSingleThreshold",
    "Controller.Io.FixDigitalOutput",
    "Controller.IO.HeatingElement",
    "Controller.IO.Heating.Room",
    "Controller.Io.HeatPump.SgReady",
    "Controller.Heat.Heatingelement",
    "Controller.Symmetric.PeakShaving",
    "Controller.Symmetric.Balancing",
    "Controller.TimeslotPeakshaving",
    "Evcs.Cluster.PeakShaving",
    "Evcs.Cluster.SelfConsumption",
    "Heat.Askoma",
    "Heat.MyPv",
    "Heat.MyPv.AcThor9s",
    "System.Fenecon.Industrial.Xl",
    "System.Fenecon.Industrial.L",
    "System.Fenecon.Industrial.M",
    "System.Fenecon.Industrial.S",
    "Weather.OpenMeteo",
    "Scheduler.JSCalendar",
}

export type Icon = {
    color: string;
    size: string;
    name: string;
};

export type ImageIcon = {
    src: string;
    large: boolean;
};

export class Widget {
    public name: TEnumKeys<typeof WidgetNature | typeof WidgetFactory> | string;
    public componentId: string;
    public alias: string;
}

export class Widgets {
    /** Names of Widgets. */
    public readonly names: string[] = [];

    private constructor(
        /** List of all Widgets. */
        public readonly list: Widget[],
        /** List of Widget-Classes. */
        public readonly classes: string[],
    ) {
        // fill names
        for (const widget of list) {
            const name: string = widget.name.toString();
            if (!this.names.includes(name)) {
                this.names.push(name);
            }
        }
    }

    public static parseWidgets(edge: Edge, config: EdgeConfig): Widgets {
        const classes: string[] = Object.values(WidgetClass) //
            .filter((v) => typeof v === "string")
            .filter((clazz) => {
                switch (clazz) {
                    case "Common_Autarchy":
                    case "Grid":
                        return config.hasMeter();
                    case "Energymonitor":
                    case "Consumption":
                        if (config.hasMeter() == true || config.hasProducer() == true || config.hasStorage() == true) {
                            return true;
                        } else {
                            return false;
                        }
                    case "Storage":
                        return config.hasStorage();
                    case "Common_Production":
                    case "Common_Selfconsumption":
                        return config.hasProducer();
                    case "Controller_ChannelThreshold":
                        return config.getComponentIdsByFactory("Controller.ChannelThreshold")?.length > 0;
                    case "Controller_Io_Digital_Outputs":
                        return (
                            config.getComponentIdsByFactories(
                                "Controller.Io.FixDigitalOutput",
                                "Controller.IO.ChannelSingleThreshold",
                            )?.length > 0
                        );
                    case "Controller.Api.ModbusTcp.ReadWrite":
                        return true;
                    default:
                        return false;
                }
            })
            .map((clazz) => clazz.toString());
        const list: Widget[] = [];

        for (const nature of Object.values(WidgetNature).filter((v) => typeof v === "string")) {
            for (const componentId of config.getComponentIdsImplementingNature(nature.toString())) {
                if (
                    nature === "io.openems.edge.io.api.DigitalInput" &&
                    list.some((e) => e.name === "io.openems.edge.io.api.DigitalInput")
                ) {
                    continue;
                }
                const component = config.getComponent(componentId);
                if (component.isEnabled) {
                    list.push({
                        name: nature,
                        componentId: componentId,
                        alias: component.alias,
                    });
                }
            }
        }
        for (const factory of Object.values(WidgetFactory).filter((v) => typeof v === "string")) {
            for (const componentId of config.getComponentIdsByFactory(factory.toString())) {
                const component = config.getComponent(componentId);
                if (factory === "Controller.Clever-PV") {
                    // Clever-PV Widget should be shown only if readOnly property is explicitely set to false
                    const readOnly = config.getPropertyFromComponent<boolean>(component, "readOnly");
                    if (readOnly !== false) {
                        continue;
                    }
                }
                if (component.isEnabled) {
                    list.push({
                        name: factory,
                        componentId: componentId,
                        alias: component.alias,
                    });
                }
            }
        }

        // explicitely sort ChannelThresholdControllers by their outputChannelAddress
        list.sort((w1, w2) => {
            if (
                w1.name === "Controller.IO.ChannelSingleThreshold" &&
                w2.name === "Controller.IO.ChannelSingleThreshold"
            ) {
                let outputChannelAddress1: string | string[] = config.getComponentProperties(w1.componentId)[
                    "outputChannelAddress"
                ];
                if (typeof outputChannelAddress1 !== "string") {
                    // Takes only the first output for simplicity reasons
                    outputChannelAddress1 = outputChannelAddress1[0];
                }
                let outputChannelAddress2: string | string[] = config.getComponentProperties(w2.componentId)[
                    "outputChannelAddress"
                ];
                if (typeof outputChannelAddress2 !== "string") {
                    // Takes only the first output for simplicity reasons
                    outputChannelAddress2 = outputChannelAddress2[0];
                }
                if (outputChannelAddress1 && outputChannelAddress2) {
                    return outputChannelAddress1.localeCompare(outputChannelAddress2);
                }
            }

            return w1.componentId.localeCompare(w1.componentId);
        });
        return new Widgets(list, classes);
    }
}

export enum ProductType {}
