import logging
from logging.handlers import NTEventLogHandler
from time import sleep
import os
from os.path import join
import servicemanager
import socket
import sys
import winerror
import win32event
import win32service
import win32serviceutil

from utils import setup_logging
from windows.winutils import getRegistryVal, get_windows_version, servicePostUpdate

if getattr(sys, "frozen", False):
    logger = setup_logging(logfile=r"C:\Circle\watchdog.log") 
else: 
    logger = setup_logging() 
fh = NTEventLogHandler("CIRCLE Watchdog")
formatter = logging.Formatter(
    "%(asctime)s - %(name)s [%(levelname)s] %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)
level = getRegistryVal(
    r"SYSTEM\\CurrentControlSet\\Services\\CIRCLE-Agent\\Parameters",
    "LogLevel",
    "INFO"
)
logger.setLevel(level)
logger.info("%s loaded", __file__)


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "circle-watchdog"
    _svc_display_name_ = "CIRCLE Watchdog"
    _svc_description_ = "Watchdog for CIRCLE Agent"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(60)
        self._stopped = False
        
    def watch(self, checked_service):
        logger.debug("watch...")
        def check_service(checked_service):
            return win32serviceutil.QueryServiceStatus(checked_service)[1] == 4

        def start_service():
            win32serviceutil.StartService(checked_service)

        timo_base = 20
        timo = timo_base
        sleep(6*timo)  # boot process may have triggered the agent, so we are patient
        while not self._stopped:
            logger.debug("checking....(timo: %d", timo) 
            if not check_service(checked_service):
                logger.info("Service %s is not running.", checked_service)
                try:
                    start_service()
                    timo = timo_base
                    logger.info("Service %s restarted.", checked_service)
                except Exception:
                    timo = min(timo * 2, 15 * 60)  # max 15 perc
                    logger.exception("Cant start service %s new timo: %s" % (checked_service, timo))
            sleep(timo)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self._stopped = True
        logger.info("%s stopped", __file__)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ''))
        logger.info("%s starting", __file__)
        working_dir = r"C:\circle" 
        exe = "circle-watchdog.exe"
        exe_path = join(working_dir, exe)
        logger.debug("hahooo %s %s", self._svc_name_, exe_path)
        if servicePostUpdate(self._svc_name_, exe_path): 
            # Service updated, Restart needed
            logger.debug("update....")
            self.ReportServiceStatus(
                win32service.SERVICE_STOPPED,
                win32ExitCode=winerror.ERROR_SERVICE_SPECIFIC_ERROR,  # 1066
                svcExitCode=int(1)
            )
            return
        self.watch("circle-agent")
        # normal stop
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)


def main():
    logger.info("Started: %s", sys.argv)
 
    if len(sys.argv) == 1:
        # service must be starting...
        # for the sake of debugging etc, we use win32traceutil to see
        # any unhandled exceptions and print statements.
        import win32traceutil  # noqa
        logger.info("service is starting...")

        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(AppServerSvc)
        # Now ask the service manager to fire things up for us...
        servicemanager.StartServiceCtrlDispatcher()
        logger.info("service done!")
    else:
        win32serviceutil.HandleCommandLine(AppServerSvc)


if __name__ == '__main__':
    try:
        main()
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception:
        logger.exception("Exception:")
