from rgtracker.common import *
import json
from dataclasses import dataclass, field
from datetime import datetime
from typing import ClassVar
from redisgears import executeCommand as execute
from redisgears import log


@dataclass
class Website:
    id: str
    name: str = None
    last_visited: str = None
    dimension_key: field(init=False) = None
    dimension_ts_key: field(init=False) = None
    metric_pageviews_key: field(init=False) = None
    metric_unique_device_key: field(init=False) = None
    cms_width: ClassVar[int] = 2000
    cms_depth: ClassVar[int] = 5

    def __post_init__(self):
        if len(self.last_visited.split('_')) > 1:
            self.dimension_key = f'{Type.JSON.value}::{Dimension.WEBSITE.value}:{self.id}::'
            self.dimension_ts_key = f'::{Dimension.WEBSITE.value}:{self.id}:{self.last_visited}:'
            self.metric_pageviews_key = f'{Type.CMS.value}::{Dimension.WEBSITE.value}::{self.last_visited}:{Metric.PAGEVIEWS.value}'
            self.metric_unique_device_key = f'{Type.HLL.value}::{Dimension.WEBSITE.value}:{self.id}:{self.last_visited}:{Metric.UNIQUE_DEVICES.value}'
            # log(f'__post_init__ len(last_visited) > 1  \n{self.dimension_key}\n{self.metric_pageviews_key}\n{self.metric_unique_device_key}')
        else:
            dt = datetime.fromtimestamp(int(self.last_visited) / 1000) if self.last_visited is not None else None
            rounded_last_visited = int(round_time(dt).timestamp() * 1000)
            # tracker_log(f'Rounded time: {dt} to {rounded_last_visited}', f'Website - ')
            self.dimension_key = f'{Type.JSON.value}::{Dimension.WEBSITE.value}:{self.id}::'
            self.dimension_ts_key = f'::{Dimension.WEBSITE.value}:{self.id}:{rounded_last_visited}:'
            self.metric_pageviews_key = f'{Type.CMS.value}::{Dimension.WEBSITE.value}::{rounded_last_visited}:{Metric.PAGEVIEWS.value}'
            self.metric_unique_device_key = f'{Type.HLL.value}::{Dimension.WEBSITE.value}:{self.id}:{rounded_last_visited}:{Metric.UNIQUE_DEVICES.value}'
            # log(f'__post_init__ len(last_visited) > 1  \n{self.dimension_key}\n{self.metric_pageviews_key}\n{self.metric_unique_device_key}')

    def create(self):
        execute('SADD', Dimension.WEBSITE.value, f'{self.id}:{self.name}')
        # log(f'SADD websites {self.name}:{self.id}')
        execute('JSON.SET', self.dimension_key, '.', json.dumps({
            'id': self.id,
            'name': self.name,
            'last_visited': self.last_visited,
            'sections': [],
            'pages': []
        }))
        # log(f'JSON.SET {self.dimension_key}')

    def create_new_metrics(self):
        try:
            execute('CMS.INITBYDIM', self.metric_pageviews_key, self.cms_width, self.cms_depth)
            tracker_log(f'CMS.INITBYDIM {self.metric_pageviews_key}', f'Website - create_new_metrics - ')
        except Exception as e:
            # tracker_log(f'{e} - {self.metric_pageviews_key}', 'Website - ')
            pass

        try:
            execute('PFADD', self.metric_unique_device_key)
            tracker_log(f'PFADD {self.metric_unique_device_key}', f'Website - create_new_metrics - ')
        except Exception as e:
            # tracker_log(f'{e} - {self.metric_pageviews_key}', 'Website - ')
            pass

    def create_metrics(self):
        try:
            execute('CMS.INITBYDIM', self.metric_pageviews_key, self.cms_width, self.cms_depth)
            tracker_log(f'CMS.INITBYDIM {self.metric_pageviews_key}', f'Website - create_metrics - ')
        except Exception as e:
            # tracker_log(f'{e} - {self.metric_pageviews_key}', 'Website - ')
            pass

        try:
            execute('PFADD', self.metric_unique_device_key)
            tracker_log(f'PFADD {self.metric_unique_device_key}', f'Website - create_metrics - ')
        except Exception as e:
            # tracker_log(f'{e} - {self.metric_pageviews_key}', 'Website - ')
            pass

        # parsed_key = parse_key_name(self.dimension_ts_key)
        #
        # current_ts = int(parsed_key.get("ts"))
        # prev_unix_ts = int((datetime.fromtimestamp(current_ts / 1000) - timedelta(minutes=1)).timestamp() * 1000)
        #
        # ids_raw = execute('FT.SEARCH', 'I::W:::', f'@last_visited:[{prev_unix_ts} {current_ts}]', 'RETURN', '1', 'id')
        # ids = []
        # for d in ids_raw:
        #     if type(d) is list:
        #         ids.append(d[-1])
        #
        # for id in ids:
        #     ud_key = create_key_name(Type.HLL.value, '', Dimension.WEBSITE.value, id, parsed_key.get('ts'),
        #                              Metric.UNIQUE_DEVICES.value)
        #     try:
        #         execute('PFADD', ud_key)
        #         tracker_log(f'PFADD {ud_key}', f'Website - create_metrics - ')
        #     except Exception as e:
        #         # tracker_log(f'{e} - {self.metric_pageviews_key}', 'Website - ')
        #         pass

    def incr_new_metrics(self, page_id, device_id):
        execute('CMS.INCRBY', self.metric_pageviews_key, self.id, 1)
        tracker_log(f'CMS.INCRBY {self.metric_pageviews_key} {self.id} 1', f'Website - incr_new_metrics - ')
        execute('PFADD', self.metric_unique_device_key, device_id)
        tracker_log(f'PFADD {self.metric_unique_device_key} {device_id}', f'Website - incr_new_metrics - ')

    # def incr_metrics(self, page_id, device_id):
    #     parsed_key = parse_key_name(self.dimension_ts_key)
    #
    #     current_ts = int(parsed_key.get("ts"))
    #     prev_unix_ts = int((datetime.fromtimestamp(current_ts / 1000) - timedelta(minutes=1)).timestamp() * 1000)
    #
    #     ids_raw = execute('FT.SEARCH', 'I::W:::', f'@last_visited:[{prev_unix_ts} {current_ts}]', 'RETURN', '1', 'id')
    #     ids = []
    #     for d in ids_raw:
    #         if type(d) is list:
    #             ids.append(d[-1])
    #
    #     for id in ids:
    #         execute('CMS.INCRBY', self.metric_pageviews_key, id, 1)
    #         tracker_log(f'CMS.INCRBY {self.metric_pageviews_key} {id} 1', f'Website - incr_metrics - ')
    #
    #         ud_key = create_key_name(Type.HLL.value, '', Dimension.WEBSITE.value, id, parsed_key.get('ts'),
    #                                  Metric.UNIQUE_DEVICES.value)
    #         execute('PFADD', ud_key, device_id)
    #         tracker_log(f'PFADD {ud_key} {device_id} 1', f'Website - incr_metrics - ')

    def incr_new_minute_metrics(self, page_id, device_id):
        execute('CMS.INCRBY', self.metric_pageviews_key, self.id, 1)
        tracker_log(f'CMS.INCRBY {self.metric_pageviews_key} {self.id} 1', f'Website - incr_new_minute_metrics - ')
        execute('PFADD', self.metric_unique_device_key, device_id)
        tracker_log(f'PFADD {self.metric_unique_device_key} {device_id}', f'Website - incr_new_minute_metrics - ')

    def incr_new_record_metrics(self, page_id, device_id):
        execute('CMS.INCRBY', self.metric_pageviews_key, self.id, 1)
        tracker_log(f'CMS.INCRBY {self.metric_pageviews_key} {self.id} 1', f'Website - incr_new_record_metrics - ')
        execute('PFADD', self.metric_unique_device_key, device_id)
        tracker_log(f'PFADD {self.metric_unique_device_key} {device_id}', f'Website - incr_new_record_metrics - ')

    def expire_new_metrics(self):
        # Check if CMS and HLL keys have already expired time fixed
        ttl_pageviews = execute('TTL', self.metric_pageviews_key)
        # tracker_log(f'TTL {self.metric_pageviews_key} {ttl_pageviews}', 'Website - expire new - ')
        if ttl_pageviews == -1:
            expire_pg = execute('EXPIRE', self.metric_pageviews_key, 3600)
            tracker_log(f'Expire key {self.metric_pageviews_key} = {expire_pg}', f'Website - expire_new - ')

        ttl_unique_devices = execute('TTL', self.metric_unique_device_key)
        # tracker_log(f'TTL {self.metric_unique_device_key} {ttl_unique_devices}', 'Website - expire new ')
        if ttl_unique_devices == -1:
            expire_ud = execute('EXPIRE', self.metric_unique_device_key, 3600)
            tracker_log(f'Expire key {self.metric_unique_device_key} = {expire_ud}', f'Website - expire_new - ')

    # Todo: put expire time in method params
    def expire_metrics(self):
        expire_pg = execute('EXPIRE', self.metric_pageviews_key, 3600)
        tracker_log(f'Expire key {self.metric_pageviews_key} = {expire_pg}', f'Website - expire - ')

        expire_ud = execute('EXPIRE', self.metric_unique_device_key, 3600)
        tracker_log(f'Expire key {self.metric_unique_device_key} = {expire_ud}', f'Website - expire - ')

        # parsed_key = parse_key_name(self.dimension_ts_key)
        # # tracker_log(f'{parsed_key}', 'Website - 0 - ')
        # #
        # current_ts = int(parsed_key.get("ts"))
        # prev_unix_ts = int((datetime.fromtimestamp(current_ts / 1000) - timedelta(minutes=1)).timestamp() * 1000)
        # # # Todo: create index_name with method instead hard coding it
        # # # FixMe: IDS maybe not present in website json/referential when it's new website and when we try to expire it?
        # ids_raw = execute('FT.SEARCH', 'I::W:::', f'@last_visited:[{prev_unix_ts} {current_ts}]', 'RETURN', '1', 'id')
        # ids = []
        # for d in ids_raw:
        #     if type(d) is list:
        #         ids.append(d[-1])
        # # tracker_log(f'{prev_unix_ts} {current_ts} - {ids}', 'Website - 1 - ')
        #
        # for id in ids:
        #     ud_key = create_key_name(Type.HLL.value, '', Dimension.WEBSITE.value, id, parsed_key.get('ts'),
        #                              Metric.UNIQUE_DEVICES.value)
        #     expire_ud = execute('EXPIRE', ud_key, 3600)
        #     tracker_log(f'Expire key({execute("EXISTS", ud_key)}) {ud_key} = {expire_ud}', 'Website - expire - ')
        #     # if expire_ud != 1:
        #     #     tracker_log(f'Expire key {ud_key} = {expire_ud}', f'Website - Warning - ', 'warning')

    def check_if_need_to_expire(self):
        # Check if CMS and HLL keys have already expired time fixed
        ttl_pageviews = execute('TTL', self.metric_pageviews_key)
        # tracker_log(f'TTL {self.metric_pageviews_key} {ttl_pageviews}', 'Website - expire new - ')
        if ttl_pageviews == -1:
            expire_pg = execute('EXPIRE', self.metric_pageviews_key, 3600)
            tracker_log(f'Expire key {self.metric_pageviews_key} = {expire_pg}',
                        f'Website - check_if_need_to_expire - ')

        ttl_unique_devices = execute('TTL', self.metric_unique_device_key)
        # tracker_log(f'TTL {self.metric_unique_device_key} {ttl_unique_devices}', 'Website - expire new ')
        if ttl_unique_devices == -1:
            expire_ud = execute('EXPIRE', self.metric_unique_device_key, 3600)
            tracker_log(f'Expire key {self.metric_unique_device_key} = {expire_ud}',
                        f'Website - check_if_need_to_expire - ')

    def is_exists(self):
        x = execute('EXISTS', self.dimension_key)
        # log(f'EXISTS {self.dimension_key} => {x}')
        return x

    def has_metrics(self):
        x = execute('EXISTS', self.metric_pageviews_key)
        # log(f'EXISTS {self.metric_pageviews_key} => {x}')
        z = execute('EXISTS', self.metric_unique_device_key)
        # log(f'EXISTS {self.metric_unique_device_key} => {z}')
        # return x + z
        return x

    def update_last_visited(self):
        execute('JSON.SET', self.dimension_key, '.last_visited', self.last_visited)
        # log(f'update_last_visited: JSON.SET {self.dimension_key} .last_visited {self.last_visited}')

    def notification_of_new_website(self, stream_names):
        parsed_key = parse_key_name(self.dimension_ts_key)
        tracker_log(f'{parsed_key.get("ts")} new website {self.id}', f'Website - notification_of_new_website - ')
        execute('XADD', stream_names.get('website_pageviews'), 'MAXLEN', '8640', '*',
                'ts', parsed_key.get("ts"),
                'status', 'new',
                'ids', self.id)
        execute('XADD', stream_names.get('website_unique_devices'), 'MAXLEN', '8640', '*',
                'ts', parsed_key.get("ts"),
                'status', 'new',
                'ids', self.id)

    # Fixme: change method name
    def notification_of_active_websites(self, stream_names):
        parsed_key = parse_key_name(self.dimension_ts_key)

        current_ts = int(parsed_key.get("ts"))
        prev_unix_ts = int((datetime.fromtimestamp(current_ts / 1000) - timedelta(minutes=1)).timestamp() * 1000)

        # Todo: create index_name with method instead hard coding it
        ids_raw = execute('FT.SEARCH', 'I::W:::', f'@last_visited:[{prev_unix_ts} {current_ts}]', 'RETURN', '1', 'id')
        ids = []
        for d in ids_raw:
            if type(d) is list:
                ids.append(d[-1])

        tracker_log(f'{prev_unix_ts} {current_ts} active websites {ids}',
                    f'Website - notification_of_active_websites - ')
        execute('XADD', stream_names.get('website_pageviews'), 'MAXLEN', '8640', '*',
                'ts', parsed_key.get("ts"),
                'status', 'active',
                'ids', json.dumps(ids))
        execute('XADD', stream_names.get('website_unique_devices'), 'MAXLEN', '8640', '*',
                'ts', parsed_key.get("ts"),
                'status', 'active',
                'ids', json.dumps(ids))

    def send_to_enrich(self):
        execute('XADD', 'ST:ENRICH:W:::', 'MAXLEN', '10', '*', 'key', self.dimension_key)

    def log_new_website(self, device_id):
        tracker_log(
            f'NEW WEBSITE! {self.id}_{self.last_visited}_{device_id} at {parse_key_name(self.dimension_ts_key).get("ts")}',
            f'Website - ')

    def log_new_minute(self, device_id):
        tracker_log(
            f'CLAP! {self.id}_{self.last_visited}_{device_id} at {parse_key_name(self.dimension_ts_key).get("ts")}',
            f'Website - ')

    def log_new_records(self, device_id):
        tracker_log(
            f'NEW RECORD! {self.id}_{self.last_visited}_{device_id} at {parse_key_name(self.dimension_ts_key).get("ts")}',
            f'Website - ')


def load_website(website, section, page, device, output_stream_names):
    if website.is_exists() != 1:
        website.log_new_website(device.id)
        website.create()
        website.create_new_metrics()
        website.incr_new_metrics(page.id, device.id)
        website.expire_new_metrics()
        website.notification_of_new_website(stream_names=output_stream_names)
        website.send_to_enrich()
    else:
        if website.has_metrics() < 1:
            website.log_new_minute(device.id)
            website.create_metrics()
            website.incr_new_minute_metrics(page.id, device.id)
            website.expire_metrics()
            website.notification_of_active_websites(stream_names=output_stream_names)
            website.update_last_visited()
        else:
            website.log_new_records(device.id)
            website.incr_new_record_metrics(page.id, device.id)
            website.check_if_need_to_expire()
            website.update_last_visited()
