from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any

from .....utils import DictUtils, Crypto, get_object_model_class, init_class_kwargs
from ....base.firebase_collection import FirebaseCollectionBase, getTimestamp
from .audit_log_fields import AuditLogFields, AuditLogFieldsProps, STANDARD_FIELDS, AUDIT_LOG_COLLECTION
from .....utils.response import ApiResponse, Error, ResponseStatus, StatusCode


@dataclass(init=False)
class AuditLog(FirebaseCollectionBase):
    id: str
    event_type: str
    user_uid: str
    client_id: str
    ip_address: str
    user_agent: str
    metadata: dict
    created_at: str

    def __init__(self, log=None, **kwargs):
        if type(log) is str:
            log = {"event_type": log}
        (cls_object, keys, data_args) = init_class_kwargs(self, log, STANDARD_FIELDS, AuditLogFieldsProps, AUDIT_LOG_COLLECTION, ['id'], **kwargs)
        super().__init__(**data_args)
        for key in keys:
            setattr(self, key, cls_object[key])

    @staticmethod
    def generate_model(_key_="default_value"):
        model = {}
        props = AuditLogFieldsProps
        for k in DictUtils.get_keys(props):
            model[k] = props[k][_key_]
        return model

    @staticmethod
    def from_dict(log: Any = None, db=None, collection_name=None) -> 'AuditLog':
        cls_object, keys = get_object_model_class(log, AuditLog, AuditLogFieldsProps)
        return AuditLog(cls_object, db=db, collection_name=collection_name)

    def query(self, query_params: list, first=False, limit=None):
        try:
            result = []
            query_result = self.custom_query(query_params, first=first, limit=limit)
            if bool(query_result):
                if first:
                    return AuditLog.from_dict(log=query_result, db=self.db, collection_name=self.collection_name)
                else:
                    for doc in query_result:
                        result.append(AuditLog.from_dict(log=doc, db=self.db, collection_name=self.collection_name))
                    return result
            return None if first else []
        except Exception as e:
            print('Exception while querying AuditLog', e)
            return None if first else []

    def to_dict(self):
        return {
            "id": self.id,
            "event_type": self.event_type,
            "user_uid": self.user_uid,
            "client_id": self.client_id,
            "ip_address": self.ip_address,
            "user_agent": self.user_agent,
            "metadata": self.metadata,
            "created_at": self.created_at,
        }

    def to_json(self, _fields=None):
        return self.to_dict()

    def list_by_user(self, uid: str, limit: int = 50):
        return self.query([{"key": AuditLogFields.user_uid, "comp": "==", "value": uid}], first=False, limit=limit)

    @classmethod
    def log(cls, event_type: str, user_uid: str = "", client_id: str = "",
            ip_address: str = "", user_agent: str = "", metadata: dict = None):
        now = datetime.now(timezone.utc).isoformat()
        model = {
            "event_type": event_type,
            "user_uid": user_uid,
            "client_id": client_id,
            "ip_address": ip_address,
            "user_agent": user_agent,
            "metadata": metadata or {},
            "created_at": now,
        }
        obj = cls.from_dict(model)
        obj.id = Crypto().generate_id(event_type + "~" + user_uid + "~" + now)
        doc_ref = AuditLog(obj.to_json()).document_reference()
        doc_ref.set({**obj.to_json(), "createdAt": getTimestamp()}, merge=True)
        return obj
