# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Manage persistent time skew."""

import os
import time
import logging
import datetime
from sigma.util import dance

logger = logging.getLogger("util.timeskew")

class TimeSkew(object):

    # XXX Should this be a singleton?

    """Persistent time skew manager.

    The time skew is a float which can be added to time.time() to get
    the server time.

    The time skew is stored in $AVDATA/timeskew.txt.

    The scheduler occasionally updates the value; other code should
    only read it.

    """

    def __init__(self, options):
        """Constructor.  Requires a config.Options arguments."""
        self.options = options
        self.filename = options.datapath("timeskew.txt")
        self.skew = 0.0
        self.load()

    def get_skew(self):
        """Returns the current time skew."""
        return self.skew

    def set_skew(self, skew):
        """Set the current time skew.

        The scheduler computes the time skew by observing the server
        time stamp in ACK messages and calls this method to persist
        it.

        """
        if not isinstance(skew, (int, float)):
            raise TypeError("invalid time skew type %s" % type(skew))
        self.skew = float(skew)
        self.store()

    # Internal methods

    def load(self):
        if not os.path.exists(self.filename):
            return
        try:
            f = open(self.filename)
        except EnvironmentError as err:
            logger.error("Can't open %s: %s", self.filename, err)
            return
        try:
            line = f.readline()
        finally:
            f.close()
        try:
            self.skew = float(line.strip())
        except ValueError as err:
            logger.error("Can't parse time skew %r read from %s: %s",
                         line.strip(), self.filename, err)
            return

    def store(self):
        fn = self.filename
        fnn = fn + ".new"
        line = "%.6f\n" % self.skew
        try:
            f = open(fnn, "w")
        except EnvironmentError as err:
            logger.error("Can't create %s: %s", self.filename, err)
            return
        try:
            f.write(line)
        finally:
            f.close()
        err = dance.rename(fnn, fn)
        if err:
            self.error("Can't move %s to %s: %s", fnn, fn, err)

# Higher level APIs

def get_skew(options):
    """Returns the current time skew.  Requires a config.Options argument."""
    return TimeSkew(options).get_skew()

def get_skewed_time(options):
    """Returns the current server time.  Requires a config.Options argument."""
    return time.time() + get_skew(options)
