import datetime
from time import sleep

import allure
import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
from frostfs_testlib.steps.cli.object import neo_go_query_height
from frostfs_testlib.storage.controllers import ClusterStateController
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
from frostfs_testlib.utils import datetime_utils


@pytest.mark.order(20)
@pytest.mark.failover
class TestTime(ClusterTestBase):
    @reporter.step("Neo-go should continue to release blocks")
    def check_nodes_block(self, cluster_state_controller: ClusterStateController):
        count_blocks = {}
        with reporter.step("Get current block id"):
            for cluster_node in self.cluster.cluster_nodes:
                cluster_state_controller.get_node_date(cluster_node)
                count_blocks[cluster_node] = neo_go_query_height(
                    shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint()
                )["Latest block"]
        with reporter.step("Wait for 3 blocks"):
            sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME) * 3)
        with reporter.step("Current block id should be higher than before"):
            for cluster_node in self.cluster.cluster_nodes:
                shell = cluster_node.host.get_shell()
                now_block = neo_go_query_height(shell=shell, endpoint=cluster_node.morph_chain.get_http_endpoint())[
                    "Latest block"
                ]
                assert count_blocks[cluster_node] < now_block

    @pytest.fixture()
    def node_time_synchronizer(self, cluster_state_controller: ClusterStateController) -> None:
        cluster_state_controller.set_sync_date_all_nodes(status="inactive")
        yield
        cluster_state_controller.set_sync_date_all_nodes(status="active")

    @allure.title("Changing hardware and system time")
    def test_system_time(self, cluster_state_controller: ClusterStateController, node_time_synchronizer: None):
        cluster_nodes = self.cluster.cluster_nodes
        timezone_utc = datetime.timezone.utc
        node_1, node_2, node_3 = cluster_nodes[0:3]

        with reporter.step("On node 1, move the system time forward by 5 days"):
            cluster_state_controller.change_node_date(
                node_1, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=5))
            )

        self.check_nodes_block(cluster_state_controller)

        with reporter.step("On node 2, move the system time back 5 days."):
            cluster_state_controller.change_node_date(
                node_2, (datetime.datetime.now(timezone_utc) - datetime.timedelta(days=5))
            )

        self.check_nodes_block(cluster_state_controller)

        with reporter.step("On node 3, move the system time forward by 10 days"):
            cluster_state_controller.change_node_date(
                node_3, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=10))
            )

        self.check_nodes_block(cluster_state_controller)

        with reporter.step("Return the time on all nodes to the current one"):
            for cluster_node in self.cluster.cluster_nodes:
                cluster_state_controller.restore_node_date(cluster_node)

        self.check_nodes_block(cluster_state_controller)

        with reporter.step("Reboot all nodes"):
            cluster_state_controller.shutdown_cluster(mode="soft")
            cluster_state_controller.start_stopped_hosts()

        self.check_nodes_block(cluster_state_controller)