#!/usr/bin/env python3

import configparser
import logging
import time
import schedule
import sys
from pathlib import Path

FORMAT = '%(asctime)-15s %(message)s'
logging.basicConfig(format=FORMAT)

logger = logging.getLogger()

from pygoodwe import SingleInverter
from pvoutput import PVOutput
from pvoutput.parameters import ADDSTATUS_PARAMETERS

default_config = {
    'default' : {
        'logging_level' : 'INFO',
        'sleep_timer' : '10',
    },
    'goodwe' : {

    },
    'pvoutput' : {
        'donation_made' : 'False',
        'schedule_time' : '10', # minutes, the default for Goodwe - PVOutput allows 5-15
        'soc_enable' : 'False',
        'soc_field' : 'None',
    }
}


configfiles = ['./goodwe2pvoutput.conf', '/etc/goodwe2pvoutput.conf', f"{str(Path.home())}/.goodwe2pvoutput.conf"]

CONFIG = configparser.ConfigParser()
CONFIG.read_dict(default_config)
CONFIG.read(filenames=configfiles)

logging_level = CONFIG.get('default', 'logging_level')
if logging_level not in ['CRITICAL','ERROR','WARNING','INFO','DEBUG','NOTSET']:
    exit(f"Invalid logging level: {logging_level}")
logger.setLevel(eval(f'logging.{logging_level}'))

if CONFIG.getboolean('pvoutput', 'soc_enable'):
    if not CONFIG.getboolean('pvoutput', 'donation_made'):
        logger.error('Cannot log State of Charge if you have not donated')
        CONFIG.set('pvoutput', 'soc_enable', False)
    if CONFIG.get('pvoutput', 'soc_field') not in ADDSTATUS_PARAMETERS.keys():
        # setting an invalid field name
        logger.error('Cannot log State of Charge to field "%s" - field does not exist', CONFIG.get('pvoutput', 'soc_field'))
        CONFIG.set('pvoutput', 'soc_enable', False)

def add_soc(gw, pvo, pvdata):
    """ adds the state of charge field if you've donated and set it to """
    if not CONFIG.getboolean('pvoutput', 'soc_enable'):
        return pvdata
    if not CONFIG.getboolean('pvoutput', 'donation_made'):
        return pvdata
    pvdata[CONFIG.get('pvoutput', 'soc_field')] = gw.get_battery_soc()
    
    # this'll throw errors if it's not right
    try:
        pvo.validate_data(pvdata, ADDSTATUS_PARAMETERS)
    except Exception as e:
        logger.error("PVOutput.validate_data(%s) failed with an error: %s", pvdata, e)
        sys.exit()
    return pvdata    

# check if the config has the needful
required_config = (
    ('pvoutput', 'apikey'),
    ('pvoutput', 'systemid'),
    ('goodwe', 'account'),
    ('goodwe', 'password'),
    ('goodwe', 'systemid')
)
failout = False
for section, key in required_config:
    if not CONFIG.get(section, key, fallback=None):
        logging.error("Need to specify %s %s in config", section, key)
        failout = True
if failout:
    sys.exit()
# end config check


def do_the_thing():
    logger.debug("Starting do_the_thing()")

    logger.debug("Instantiating PVOutput API Object")
    pvo = PVOutput(apikey=CONFIG.get('pvoutput', 'apikey'), 
                    systemid=CONFIG.getint('pvoutput', 'systemid'), 
                    donation_made=CONFIG.get('pvoutput', 'donation_made'),
                    )

    logger.debug("Connecting to Goodwe API")
    gw = SingleInverter(account=CONFIG.get('goodwe', 'account'),
                        system_id=CONFIG.get('goodwe', 'systemid'), 
                        password=CONFIG.get('goodwe', 'password')
                        )
    # update the data
    pvdata = gw.getDataPvoutput()
    # add the state of charge data
    pvdata = add_soc(gw, pvo, pvdata)
    logger.debug("Grabbing the PVOutput-ready data: %s", pvdata)

    response = pvo.addstatus(data=pvdata)
    logger.debug("Called the PVOutput addstatus endpoint: %s", response.text)

# simple scheduler, run do_the_thing() every x minutes
logger.debug("Scheduling update every %s minutes", CONFIG.getint('pvoutput', 'schedule_time'))
schedule.every(CONFIG.getint('pvoutput', 'schedule_time')).minutes.do(do_the_thing)

logger.debug("Doing initial run...")
do_the_thing()

while True:
    logger.debug("Running scheduler...")
    schedule.run_pending()
    logger.debug("Done, sleeping for %s seconds", CONFIG.getint('default', 'sleep_timer'))
    time.sleep(CONFIG.getint('default', 'sleep_timer'))