# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

import logging
from datetime import datetime
from zoneinfo import ZoneInfo

from deye_events import DeyeEventList, DeyeEventProcessor
from deye_modbus import DeyeModbus
from deye_config import DeyeLoggerConfig
from deye_sensor import Sensor, DateTimeSensor


class DeyeSetTimeProcessor(DeyeEventProcessor):
    """
    Set logger time when the logger becomes available online.
    """

    def __init__(
        self, logger_config: DeyeLoggerConfig, interval: int, timezone: str, sensors: list[Sensor], modbus: DeyeModbus
    ):
        self.__log = logger_config.logger_adapter(logging.getLogger(DeyeSetTimeProcessor.__name__))
        self.__interval = interval
        self.__modbus = modbus
        self.__last_status = False
        self.__last_update_ts = datetime.min
        self.__sensors = []
        self.__timezone = timezone
        for sensor in sensors:
            if isinstance(sensor, DateTimeSensor):
                self.__sensors.append(sensor)

    def get_id(self):
        return "set_time"

    def get_description(self):
        return "Set inverter time once online"

    @property
    def last_status(self):
        return self.__last_status

    def process(self, events: DeyeEventList):
        logger_status = events.get_status()
        if logger_status is None:
            return
        # Get current time in specified timezone,
        # ZoneInfo uses the "Europe/Berlin" style strings from your config
        # for windows dev or run (does not have default zoneinfo db) need to install tzdata, like "uv add tzdata"
        tz = ZoneInfo(self.__timezone)
        now = datetime.now(tz).replace(tzinfo=None)
        delta = now - self.__last_update_ts
        if delta.total_seconds() < self.__interval:
            return
        if logger_status:
            self.__last_status = self.__set_time(now)
        else:
            self.__last_status = False
            self.__last_update_ts = datetime.min

    def __set_time(self, now) -> bool:
        self.__last_update_ts = now
        write_status = False
        if len(self.__sensors) == 1:
            reg_map = self.__sensors[0].write_value(now)
            if not reg_map:
                self.__log.error("Sensor returned no register mapping for current time")
                return False
            reg_addr, reg_value = reg_map.popitem()
            write_status = self.__modbus.write_registers(reg_addr, reg_value)
        else:
            if len(self.__sensors) == 0:
                self.__log.warning(
                    "Couldn't determine the DateTimeSensor object. Using registers 22-24. "
                    "If setting of the time fails, please ensure there's a DateTimeSensor defined for your inverter "
                    "in the metric groups."
                )
                write_status = self.__modbus.write_registers_uint(
                    22,
                    [
                        # year and month
                        256 * (now.year % 100) + now.month,
                        # day and hour
                        256 * now.day + now.hour,
                        # minute and seconds
                        256 * now.minute + now.second,
                    ],
                )
            else:
                self.__log.warning(
                    "More than one DateTimeSensor object defined. Time won't be updated. "
                    "Please verify the configuration - DEYE_METRIC_GROUPS."
                )
        if write_status:
            self.__log.info(f"Logger time set to {now}")
        else:
            self.__log.warning("Failed to set logger time")
        return write_status
