import TradingPair from "../../apiwrapper/assets/TradingPair.js";
import Transaction from "../../apiwrapper/transaction/Transaction.js";
import Logger from "../../logging/Logger.js";
import Serializable from "../../util/Serializable.js";
import SerializableHelper from "../../util/SerializableHelper.js";
import AssetData from "../data/asset/AssetData.js";
import AbstractStrategy from "../strategy/AbstractStrategy.js";

export default class AbstractWorker extends Serializable {

    /**
     * @type {string} unique id of the worker (generated at create time)
     */
    #id = null;

    /**
     * @type {string} worker name
     */
    #name = "";

    /**
     * @type {TradingPair} tradingpair
     */
    #tradingpair = null;

    /**
     * @type {AbstractStrategy} worker strategy
     */
    #strategy = null;

    /**
     * @type {Logger}
     */
    #logger = null;

    /**
     * @param {string} name 
     * @param {TradingPair} tradingPair 
     * @param {AbstractStrategy} strategy 
     */
    constructor(name, tradingPair, strategy){
        super();
        this.#name = name;
        this.#tradingpair = tradingPair;
        this.#strategy = strategy;
        this.#id = this.#generateId();
    }

    /**
     * @param {Logger} logger 
     * @param {string} dataPath 
     */
    init(logger, dataPath){
        this.#logger = logger;
        if(this.#strategy != null){
            this.#strategy.init(logger);
        }
        this.onInit(dataPath);
    }

    /**
     * @returns {string}
     */
    getName(){
        return this.#name;
    }

    /**
     * @param {string} name 
     */
    setName(name){
        this.#name = name;
    }

    /**
     * @returns {string}
     */
    getId(){
        return this.#id;
    }

    /**
     * @returns {TradingPair}
     */
    getTradingPair(){
        return this.#tradingpair;
    }

    /**
     * @param {TradingPair} tradingPair 
     */
    setTradingPair(tradingPair){
        this.#tradingpair = tradingPair;
    }

    /**
     * @returns {AbstractStrategy}
     */
    getStrategy(){
        return this.#strategy;
    }

    /**
     * @param {AbstractStrategy} strategy 
     */
    setStrategy(strategy){
        this.#strategy = strategy;
    }

    /**
     * @returns {Logger}
     */
    getLogger(){
        return this.#logger;
    }

    /**
     * @param {string} dataPath path to data directory
     */
    onInit(dataPath){
        // TODO implement in specific worker
    }

    /**
     * @param {AssetData} assetData 
     * @param {Transaction[]} transactions 
     */
    onUpdate(assetData, transactions){
        // TODO implement update
    }

    /**
     * Primary used for custom workers to return custom properties.
     * For example this is used by the StatisticWorker to return statistics.
     * @param {string} attributeName 
     * @returns {any} attribute value or null
     * @throws {Error} if the requested attribute is not supported by the worker instance
     */
    getAttribute(attributeName){
        // implement in worker
        return {};
    }

    #generateId(){
        return "W-" + this.#name + "-" + Date.now() + "-" + Math.floor(Math.random() * 8999999 + 1000000);
    }

    /**
     * @returns {string} string representation of this object
     */
    toJson() {
        const obj = JSON.parse(super.toJson());
        obj.id = this.#id;
        obj.name = this.#name;
        obj.tradingPair = SerializableHelper.serialize(this.#tradingpair);
        obj.strategy = SerializableHelper.serialize(this.#strategy);
        
        return JSON.stringify(obj);
    }

    /**
     * @param {string} objString 
     * @param {AbstractWorker} proptotype
     * @returns {AbstractWorker}
     */
    static fromJson(objString, proptotype = null) {
        const obj = JSON.parse(objString);
        const worker = proptotype === null ? new AbstractWorker() : proptotype;

        worker.#id = obj.id;
        worker.#name = obj.name;
        worker.#tradingpair = SerializableHelper.deserialize(obj.tradingPair);
        worker.#strategy = SerializableHelper.deserialize(obj.strategy);

        return worker;
    }
}