import {Action} from "redux";
import {IVirtualStoreConstructor} from "./virtual-store";

export type IAData = any;

export interface IActionData<IData extends IAData> {
	payload?: IData;
	virtualStorage?: string;
}

export interface IAction<IData extends IAData> extends IActionData<IData>, Action {
}

export interface IReducer<IData extends IAData, ValueInterface> {
	(state: ValueInterface, action: ReduxAction<IData, ValueInterface>): ValueInterface|void;
}

export interface ISingleReducer<ValueInterface, IData extends IAData> {
	(state: ValueInterface, action: IData, rawAction: IAction<IData>): ValueInterface|void;
	
	displayName?: string;
}

export interface IReduxActionConstructor<IData extends IAData, ValueInterface = void> {
	new(payload: IData, virtualStore?: IVirtualStoreConstructor<any>): ReduxAction<IData, ValueInterface>;
	
	// (data: IData): IAction<IData>;
}

export abstract class ReduxAction<IData extends IAData, VI = void> implements IAction<IData> {
	readonly type: string;
	readonly payload: IData;
	readonly virtualStorage?: string;
	
	constructor(payload: IData, VirtualStore?: IVirtualStoreConstructor<VI>|'*') {
		this.type = this.constructor.name;
		this.payload = payload || null;
		if (VirtualStore) {
			if (typeof VirtualStore === 'string') {
				this.virtualStorage = VirtualStore;
			} else {
				this.virtualStorage = VirtualStore.name;
			}
			if (!this.virtualStorage) {
				throw new TypeError('Action `' + this.type + '`: invalid virtualStorage');
			}
		} else {
			this.virtualStorage = null;
		}
	}
	
	static is(act: IAction<any>) {
		return act.type === this.name;
	}
	
	static compare<VI>(act: IAction<any>,
	                   Action: IReduxActionConstructor<any, VI>,
	                   VirtualStore?: IVirtualStoreConstructor<VI>) {
		if (VirtualStore) {
			return act && act.virtualStorage
			       && act.type === Action.name
			       && act.virtualStorage === VirtualStore.name;
		} else {
			return act && act.type === Action.name;
		}
	}
	
	toJSON(): IAction<IData> {
		return {
			type: this.type,
			virtualStorage: this.virtualStorage,
			payload: this.payload,
		}
	}
}
