import { DeviceInfo } from '../Definitions/Global/Categories.js';
import { Family, type Category } from '../Definitions/index.js';

import type { DriverState } from './DriverState.js';

// #region Type aliases (1)

export type NodeInfo<T extends Family = Family> = (T extends Family.ZMatter | Family.ZigBee
	? DynamicNodeInfo<T>
	: T extends Family.ZWave
		? ZWaveNodeInfo
		: BaseNodeInfo<T>) & { state?: { [x: string]: DriverState } };

// #endregion Type aliases (1)

// #region Interfaces (7)

interface Custom {
	// #region Properties (2)

	flags: number | string;
	val1: number | string;

	// #endregion Properties (2)
}

interface TypeInfo {
	t: T[];
}

interface T {
	id: 'ioxType' | 'mfgCode' | 'model';
	val: number | string;
}

interface Devtype {
	// #region Properties (3)

	cat: number;
	gen: `${number}.${number}.${number}`;
	mfg: string;

	// #endregion Properties (3)
}

export interface DynamicNodeInfo<F extends Family.ZigBee | Family.ZMatter> extends BaseNodeInfo<F> {
	typeInfo: TypeInfo;

	nodeTypeId: string;

	rpnode: string;
}

export interface ZWaveNodeInfo extends BaseNodeInfo<Family.ZWave> {
	// #region Properties (20)

	custom: Custom;
	devtype: Devtype;
	rpnode: string;
	sgid: string;

	nodeTypeId: string;
	// #endregion Properties (20)
}

interface Node {
	// #region Properties (15)

	address: string;
	dcPeriod: string;
	deviceClass: string;
	enabled: string;
	endDelay: string;
	family: string;
	flag: string;
	hint: string;
	name: string;
	nodeDefId: string;
	parent: Parent;
	pnode: string;
	startDelay: string;
	type: string;
	wattage: string;

	// #endregion Properties (15)
}

interface Parent {
	// #region Properties (2)

	_: string;
	type: string;

	// #endregion Properties (2)
}

interface RootObject {
	// #region Properties (1)

	node: Node;

	// #endregion Properties (1)
}

export interface BaseNodeInfo<F extends Family> {
	// #region Properties (16)

	address: any;
	dcPeriod: number;
	deviceClass?: any;
	enabled?: boolean;
	endDelay: number;
	family?: F extends Family.Poly ? { _: Family.Poly; instance: number } : F;
	flag?: any;
	hint: string;
	name: string;
	nodeDefId?: string;
	parent?: Parent;
	pnode: any;
	property?: DriverState[] | DriverState;
	startDelay: number;
	type?: string;
	wattage: number;

	// #endregion Properties (16)
}

// #endregion Interfaces (7)

// #region Functions (3)

export function isFamily<T extends Family>(nodeInfo: NodeInfo<any>, family: T): nodeInfo is NodeInfo<T> {
	return nodeInfo.family ?? 1 === family;
}

export function parseDeviceInfo<F extends Family>(
	nodeInfo: NodeInfo<F>
): NodeInfo<F> extends BaseNodeInfo<Family.Insteon>
	? { category: Category.Insteon; model: number; firmwareVersion: string; firmwareRevision: string }
	: DeviceInfo<any, any, any> {
	const type = nodeInfo.type === undefined || nodeInfo.type === '0.0.0.0' ? nodeInfo.hint : nodeInfo.type;
	const family = (nodeInfo.family ?? nodeInfo.family == 0) ? Family.Insteon : nodeInfo.family;
	if (isFamily(nodeInfo, Family.Insteon)) {
		const s = type.split('.');
		return {
			category: Number(s[0]),
			model: Number(s[1]),
			firmwareVersion: Number(Number(s[2]).toString(16)),
			firmwareRevision: Number(Number(s[3]).toString(16)),
		} as any;
	} else {
		const s = type.split('.');
		return { domain: Number(s[0]), category: Number(s[1]), subcategory: Number(s[1]), model: Number(s[2]) } as any;
	}
}

export function cleanNodeInfo<T extends Family>(nodeInfo: NodeInfo<T>) {
	if (isDynamic(nodeInfo)) {
		nodeInfo.nodeTypeId = `${nodeInfo.typeInfo.t.find(p => p.id == 'ioxType').val}`;
	} else if (isZWave(nodeInfo)) {
		nodeInfo.nodeTypeId = nodeInfo.sgid;
	}
	if (!nodeInfo.property) {
		nodeInfo.property = [];
	}
	nodeInfo.property = Array.isArray(nodeInfo.property) ? nodeInfo.property : [nodeInfo.property];
	nodeInfo.state = nodeInfo.property.reduce((acc, p) => {
		if (p && p?.id) {
			p.name = p.name == '' ? undefined : p.name;
			acc[p.id] = p;
		}
		return acc;
	}, {});
	if (!nodeInfo.family || nodeInfo.family == -1) {
		nodeInfo.family = Family.Insteon;
	}
	return nodeInfo;
}

export function isPlugin(nodeInfo: NodeInfo<any>): nodeInfo is NodeInfo<Family.Poly> {
	if (typeof nodeInfo.family == 'object') {
		return true;
	}
	return nodeInfo.family === Family.Poly;
}

export function isDynamic(nodeInfo: NodeInfo<any>): nodeInfo is DynamicNodeInfo<any> {
	return [Family.ZMatter, Family.ZigBee].includes(nodeInfo.family);
}

export function isZWave(nodeInfo: NodeInfo<any>): nodeInfo is ZWaveNodeInfo {
	return nodeInfo.family == Family.ZWave;
}

export function isStatic(nodeInfo: NodeInfo<any>): nodeInfo is BaseNodeInfo<any> {
	return isDynamic(nodeInfo) || isZWave(nodeInfo) ? false : true;
}

// #endregion Functions (3)
