import { Family } from '../Definitions/Global/Families.js';
import type { ISY } from '../ISY.js';
import { ISYDevice } from '../ISYDevice.js';
//@ts-ignore
import { NodeInfo, cleanNodeInfo } from '../Model/NodeInfo.js';
import { InsteonDeviceFactory } from './Insteon/InsteonDeviceFactory.js';
import { NodeFactory } from './NodeFactory.js';
import { ZigBeeDeviceFactory } from './ZigBee/ZigBeeDeviceFactory.js';

export class DeviceFactory {
	static async createAll(isy: ISY, ...nodeInfos: NodeInfo[]) {
		const devices = await Promise.all(
			nodeInfos.map(async node => {
				return await this.create(isy, node);
			})
		);
		for (const device of devices) {
			if (ISYDevice.isComposite(device)) {
				for (const child of this.nodeMap.get(device.node.address) ?? []) {
					device.addNode(child, isy);
				}
			}
		}
		return devices;
	}

	static nodeMap = new Map<string, NodeInfo<Family>[]>();

	static async addChildNodes(isy: ISY) {
		isy.logger.debug('Adding child nodes. Composite Device Count: ' + DeviceFactory.nodeMap.size);

		for (const [address, nodeInfos] of DeviceFactory.nodeMap) {
			isy.logger.debug('Adding child nodes for: ' + address + '. Child Count: ' + nodeInfos.length);
			const parent = isy.getDevice(address);
			if (parent && ISYDevice.isComposite(parent)) {
				for (const nodeInfo of nodeInfos) {
					await parent.addNode(nodeInfo, isy);
				}
				await parent.applyNodeDefs();
			} else {
				isy.logger.debug('Parent not found for: ' + address);

				for (const nodeInfo of nodeInfos) {
					try {
						await NodeFactory.create(nodeInfo, isy);
					} catch {}
				}
			}
		}
	}

	static async create<T extends Family>(isy: ISY, nodeInfo: NodeInfo<T>): Promise<ISYDevice<T, any, any, any>> {
		const cinfo = cleanNodeInfo(nodeInfo);

		if (cinfo.pnode != cinfo.address) {
			let nodeInfos = DeviceFactory.nodeMap.get(cinfo.pnode) ?? [];
			nodeInfos.push(cinfo);
			DeviceFactory.nodeMap.set(nodeInfo.pnode, nodeInfos);
			return null;
		}

		let device = null as ISYDevice<T, any, any, any>;
		switch (cinfo.family) {
			case Family.Insteon:
				//@ts-ignore
				device = await InsteonDeviceFactory.create(isy, cinfo);
				break;
			case Family.ZigBee:
				device = (await ZigBeeDeviceFactory.create(isy, cinfo)) as any;
				break;
			default:
				device = null;
				break;
		}
		if (!device) {
			device = (await NodeFactory.create(cinfo, isy)) as any;
		}
		/*if (!device) {
			return new GenericNode(isy, nodeInfo) as any;
		}*/
		return device;
	}
}

export namespace DeviceFactory {
	export const Insteon = InsteonDeviceFactory;
	export type ZWave = {};
	export type ZigBee = ZigBeeDeviceFactory;

	export const ZigBee = ZigBeeDeviceFactory;

	export type Insteon = typeof InsteonDeviceFactory;
}
