import ska_control_model as scm
import ska_tango_base as stb
import tango
import tango.server
import release

import os
import pwd
import grp
import time
import stat
import logging
import typing


class FileStats(stb.base.BaseInterface):
    """Tango device that exposes file statistics as attributes."""

    FilePath = tango.server.device_property(
        dtype="DevString", default_value="dummy", doc="Path of file to monitor"
    )

    def init_device(self) -> None:
        """Initialise the device."""
        super().init_device()

        self._version_id = release.version
        self._build_state = f"{release.name} {release.version}: {release.description}"
        self.metadata = Metadata(self.FilePath, self.logger)

        self.init_completed()

    def change_control_level(self, control_level: stb.base.ControlLevel) -> None:
        """Change how the device is interacting with the system under control."""
        if control_level == stb.base.ControlLevel.NO_CONTACT:
            self.metadata.reset()
            self.component_disconnected()
        elif control_level == stb.base.ControlLevel.FULL_CONTROL:
            self.component_on()
        else:
            raise ValueError(f"Unknown control_level {control_level}")

    def read_attr_hardware(self, attr_list: list[int]) -> None:
        """Prepare for attribute read."""
        _ = attr_list
        if self.get_state() != tango.DevState.DISABLE:
            self.metadata.refresh()

    @tango.server.attribute(dtype=int)
    def size(self) -> stb.type_hints.ReadAttrType[int]:
        """Read the size of the file in bytes."""
        return self._to_read_attr_type(self.metadata.size, 0)

    @tango.server.attribute(dtype=str)
    def lastModifiedTime(self) -> stb.type_hints.ReadAttrType[str]:
        """Return the time the file was last modified."""
        return self._to_read_attr_type(self.metadata.last_modified_time, "")

    @tango.server.attribute(dtype=str)
    def owner(self) -> stb.type_hints.ReadAttrType[str]:
        """Return the owner of the file."""
        return self._to_read_attr_type(self.metadata.owner, "")

    @tango.server.attribute(dtype=str)
    def mode(self) -> stb.type_hints.ReadAttrType[str]:
        """Return the mode (permissions) of the file."""
        return self._to_read_attr_type(self.metadata.mode, "")

    def _to_read_attr_type(
        self, value: typing.Any, default: typing.Any
    ) -> stb.type_hints.ReadAttrType[typing.Any]:
        if value is None:
            return default, time.time(), tango.AttrQuality.ATTR_INVALID

        return value, self.metadata.timestamp, tango.AttrQuality.ATTR_VALID


class Metadata:
    """File metadata."""

    def __init__(self, path: str, logger: logging.Logger) -> None:
        self.path = os.path.abspath(path)
        self.logger = logger
        self.data: os.stat_result | None = None
        self.timestamp: float | None = None

    def reset(self) -> None:
        """Reset metadata to None."""
        self.data = None
        self.timestamp = None

    def refresh(self) -> None:
        """Refresh metadata."""
        self.data = os.stat(self.path)
        self.timestamp = time.time()

    @property
    def size(self) -> int | None:
        """Return the size of the file in bytes."""
        if self.data is not None:
            return self.data.st_size
        return None

    @property
    def mode(self) -> str | None:
        """
        Return the mode (permission) of the file.

        :return: mode in the same format as ``ls -l``.
        """
        if self.data is not None:
            return stat.filemode(self.data.st_mode)
        return None

    @property
    def owner(self) -> str | None:
        """
        Return the owner of the file.

        :return: has the format "<user>:<group>"
        """
        if self.data is not None:
            user = pwd.getpwuid(self.data.st_uid)
            group = grp.getgrgid(self.data.st_gid)
            return f"{user.pw_name}:{group.gr_name}"
        return None

    @property
    def last_modified_time(self) -> str | None:
        """
        Return the time the file was last modified.

        :return: last modified time in the ``ctime`` format
        """
        if self.data is not None:
            return time.ctime(self.data.st_mtime)
        return None


if __name__ == "__main__":
    tango.server.run((FileStats,))
