import { Room } from './room';
import { Device } from './device';
import { Switch, TempValue, Mode } from './types';
import { secondsToTime, getCurrentDate } from './utils';
import { mqttWorker } from './services/mqttWorker';

import axios from 'axios';

export class Heating
{
    protected wattsUsage:number = 1800;
    protected priceKWh:number = 0.165345;

    protected room:Room;
    protected device:Device;

    protected todayOnTimer:number = 0;
    protected yesterdayOnTimer:number = 0;
    protected sumaOnTimer:number = 0;

    protected roomTemperature:TempValue = 0;
    protected roomTempTolerance:number = 0.3;
    protected wantedTemperature:number = 21.5;

    protected mode:Mode = Mode.AUTO;
    protected heating:Switch = Switch.OFF;
    protected power:any;
    protected currentDate:number;
    protected lastStatusOn:number = 0;
    protected mqttWorker:mqttWorker;

    constructor(mqttWorker:mqttWorker)
    {
        let setupTemperature:number = parseFloat(process.argv.slice(2).toString());
        
        this.mqttWorker = mqttWorker;
        
        this.room = new Room();
        this.device = new Device(mqttWorker);
        this.currentDate = getCurrentDate();

        if (process.env.PRICE_KWH) {
            this.priceKWh = parseFloat(process.env.PRICE_KWH);
        }

        if (process.env.WATTS_USAGE) {
            this.wattsUsage = parseInt(process.env.WATTS_USAGE);
        }
                
        if (setupTemperature > 0) {
            this.wantedTemperature = setupTemperature;
        }

        this.init();
    }

    public async init() : Promise<void>
    {
        this.heating = await this.getStatus();
        this.heating = this.mqttWorker.getCurrentState();
        this.power = await this.device.getPowerUsage();
        this.roomTemperature = await this.getTemperature();
        this.todayOnTimer = 0;
        this.yesterdayOnTimer = 0;
        this.sumaOnTimer = 0;
        this.update();
        this.storePowerUsage();

        setInterval(() =>  { 
            if (this.heating == Switch.ON) {
                this.todayOnTimer++;
                this.sumaOnTimer++;
            }

            // Check if we have a new day
            const currentDate = getCurrentDate();
            if ( getCurrentDate() !== this.currentDate) {
                this.yesterdayOnTimer = this.todayOnTimer;
                this.todayOnTimer = 0;
                this.currentDate = currentDate;
            }
        }, 1000);

        setInterval(() =>  { 
            const state:Switch = this.mqttWorker.getCurrentState();
            if (state !== this.heating) {
                this.heating = state;
                this.device.setState(state);
                this.showInfo();
            }
        }, 10000);

        setInterval(async () => {
            this.power = await this.device.getPowerUsage();
            this.storePowerUsage();
        }, 1000 * 60 * 10) // 10 Minutes
    }

    public async update() : Promise<void>
    {
        let temp:number|null = this.mqttWorker.getRoomTemperature();
        if (temp === null) {
            temp = await this.getTemperature();
        }
        this.roomTemperature = temp;
        this.heating = await this.getStatus();
        this.checkRoomHeatingStatus();

        // Debug Info
        this.showInfo();
    }
    
    public setTemperature(newTemp:number) : void
    {
        this.wantedTemperature = newTemp;
        this.update();
    }

    public setMode(newMode:Mode) : void
    {
        this.mode = newMode;
        this.update();
    }

    public raiseTemperature(change:number = 0.1) : void
    {
        this.wantedTemperature += change;
    }

    public lowerTemperature(change:number = 0.1) : void
    {
        this.wantedTemperature -= change;
    }

    public getStats() : object
    {        
        return {
            status: this.heating,
            mode: this.mode,
            temp: {
                room: this.roomTemperature,
                set: this.wantedTemperature,
            },
            heating: {
                today: {
                    time: (this.todayOnTimer).toFixed(0),
                    price: this.getPrice(),
                },
                suma: {
                    time: (this.sumaOnTimer).toFixed(0),
                    price: this.getPriceSuma(),                    
                }
            },
            power: this.power, 
        };
    }
    
    protected async storePowerUsage() : Promise<void>
    {
        let today = 0;
        let yesterday = 0;

        if (this.power.daily !== undefined) {
            if (this.power.daily[0] !== undefined) {
                today = this.power.daily[0].usage;
            }

            if (this.power.daily[1] !== undefined) {
                yesterday = this.power.daily[1].usage;
            }
        }
        
        try {
            const res:any = await axios.post('https://homedata.top/api/power_usage', {
                today: today,
                yesterday: yesterday,
                todayOnTimer: this.todayOnTimer,
                yesterdayOnTimer: this.yesterdayOnTimer,
            });
            
        } catch (err:any) {
            console.log("POST POWER USAGE", err.message);
        }
    }

    protected checkRoomHeatingStatus() : void
    {
        if (this.roomTemperature === null) {
            return;
        }

        // Check if we are in AUTO mode, otherwise do nothing...
        if (this.mode === Mode.MANUAL) {
            return;
        }
        
        // Check if we need to turn ON heating
        if (this.roomTemperature < this.wantedTemperature) {
            if (this.heating === Switch.OFF) {
                let now:number = new Date().getTime();
                let razlika:number = now - this.lastStatusOn;

                if (razlika > 600) {
                    this.setState(Switch.ON);
                } else {
                    console.log("SECURITY: TO CLOSE TO TURN ON");
                }
            }
        }

        // Check if we need to turn OFF heating
        //const neededTemp:number = this.roomTemperature + this.roomTempTolerance;
        if (this.roomTemperature > this.wantedTemperature) {
            if (this.heating === Switch.ON) {
                this.setState(Switch.OFF);
                this.lastStatusOn = new Date().getTime();
            }
        }
    }

    protected async getStatus() : Promise<Switch>
    {
        return this.device.getStatus();
    }
    
    public async getTemperature() : Promise<TempValue>
    {
        return await this.room.getTemperature();
    }
    
    public async setState(state:Switch) : Promise<void> 
    {
        if (state === this.heating) {
            return;
        }
        this.heating = await this.device.setState(state);
        this.update();
    }
    
    protected getPriceSuma(toPrecision:number = 4) : string
    {
        const watts:number = this.wattsUsage / 1000;
        const price:number = watts * this.priceKWh / 3600 * this.sumaOnTimer;
        
        return price.toFixed(toPrecision);
    }

    protected getPrice(toPrecision:number = 4) : string
    {
        const watts:number = this.wattsUsage / 1000;
        const price:number = watts * this.priceKWh / 3600 * this.todayOnTimer;
        
        return price.toFixed(toPrecision);
    }

    protected showInfo() : void
    {
        let output:string = '';

        if (this.mode == Mode.AUTO) {
            output = "\x1b[35mAUTO   \x1b[0m";
        } else {
            output = "\x1b[36mMANUAL \x1b[0m";
        }

        if (this.heating == Switch.ON) {
            output += "\x1b[32mON \x1b[0m";
        } else {
            output += "\x1b[31mOFF \x1b[0m";
        }

        let wTemp:unknown = this.wantedTemperature;
        if(!isNaN(this.wantedTemperature)) {
            wTemp = this.wantedTemperature.toPrecision(4);
        }

        output += " 🏠 \x1b[33m" + this.roomTemperature + " °C\x1b[0m";
        output += " 🌡️ \x1b[34m" + wTemp + " °C\x1b[0m";
        output += " ⌚️ " + secondsToTime(this.todayOnTimer) + "";
        output += " 💶 " + this.getPrice() + " €";
        output += " ⌚️ " + secondsToTime(this.sumaOnTimer) + "";
        output += " 💶 " + this.getPriceSuma() + " €";

        console.log(output);
    }
}