import hashlib
import json
from dataclasses import dataclass
from typing import Any, List

import pydash
from adafri.cache.lib import CacheClass
from adafri.utils import DateUtils
from firebase_admin import auth as admin_auth
from firebase_admin.firestore import firestore

from ....utils import (
    BaseClass,
    Crypto,
    DictUtils,
    get_object_model_class,
    get_request_fields,
    init_class_kwargs,
    integer,
)
from ....utils.response import ApiResponse, Error, ResponseStatus, StatusCode
from ...base.firebase_collection import FirebaseCollectionBase, getTimestamp
from .file_fields import FILE_FIELDS, FileFieldProps, FileFields


def filter_file_request_fields(fields, default_fields = FILE_FIELDS):
    request_fields = get_request_fields(fields, default_fields, [default_fields[0]])
    if request_fields is None or bool(request_fields) is False:
        request_fields = [default_fields[0]]
    return request_fields

@dataclass(init=False)
class File(FirebaseCollectionBase):
    _id: str
    id: str
    accountId: str
    blob_id: str
    blob_name: str
    bucket_name: str
    colors: str
    content_type: str
    created_time: any
    directory_path: str
    extension: str
    file_source: str
    file_url: str
    filename: str
    height: int
    mime_type: str
    mode: str
    size: int
    width: int
    type: str
    url: str
    crop: dict
    data: str
    hash: str
    crop_state: str

    def __init__(self, file=None, **kwargs):
        value = file;
        if type(file) is str:
            value = {"id": file}
        (cls_object, keys, data_args) = init_class_kwargs(self, value, FILE_FIELDS, FileFieldProps, None, ['id','uid'], **kwargs)

        super().__init__(**data_args);
        for key in keys:
            if key in cls_object:
                if key == "created_time":
                    setattr(self, key, DateUtils.convert_firestore_timestamp_to_str(cls_object[key]))
                elif key == "width":
                    setattr(self, key, integer(cls_object[key], 0, True))
                elif key == "height":
                    setattr(self, key, integer(cls_object[key], 0, True))
                elif key == "size":
                    setattr(self, key, integer(cls_object[key], 0, True))
                else:
                    # print('set attr for', key)
                    setattr(self, key, cls_object[key]);
            else:
                setattr(self, key, None)
        if bool(self.blob_name) is True:
            col = self.blob_name.split('/');
            self.collection_name = col[0]
        if bool(self.hash) is False:
            self.hash = self.id;
        self.cache = CacheClass("file", File);
                
    


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

    @staticmethod
    def from_dict(file: Any=None, db=None, collection_name=None) -> 'File':
        cls_object, keys = get_object_model_class(file, File, FileFieldProps);
            
        _file = File(cls_object, db=db, collection_name=collection_name)
        return _file
    
    def to_dict(self, fields=None):
        values = {
            "id": self.id,
            "accountId": self.accountId,
            "blob_id": self.blob_id,
            "blob_name": self.blob_name,
            "bucket_name": self.bucket_name,
            "colors": self.colors,
            "content_type": self.content_type,
            "created_time": self.created_time,
            "directory_path": self.directory_path,
            "extension": self.extension,
            "file_source": self.file_source,
            "file_url": self.file_url,
            "filename": self.filename,
            "height": self.height,
            "mime_type": self.mime_type,
            "mode": self.mode,
            "size": self.size,
            "hash": self.hash,
            "width": self.width,
            "type": self.type,
            "url": self.url,
            "crop": self.crop,
            "crop_state": self.crop_state
        };
        return DictUtils.remove_none_values(values)
    def to_json(self, _fields=None):
        return self.to_dict(_fields)

    def getFiles(self, aacid, fields=None):
        files = []
        files_ = self.query(query_params=[{"key": "accountId", "comp": "==", "value": aacid}])
        request_fields = filter_file_request_fields(fields)
        # print('account query', account)
        for file in files_:
            files.append(file)
            # campaigns.append(D(campaign.to_dict(request_fields)))

        return files
    def get(self):
        file_cache: File | None = self.cache.get(self.id)
        if bool(file_cache) is True:
            return File.from_dict(file=file_cache.to_dict(), db=self.db, collection_name=self.collection_name)
        doc = self.document_reference().get();
        if doc.exists is False:
            print('file not found', self.id)
            return None;
        fields = FILE_FIELDS
        # d = pydash.pick(doc.to_dict(), fields)
        new_file = File.from_dict(file=doc.to_dict(), db=self.db, collection_name=self.collection_name)
        ttl = 225
        if new_file.crop_state == "done":
            ttl = 3600
        self.cache.set(self.id, new_file.to_dict(), ttl)
        return new_file;

    def query(self, query_params: list, first=False, limit=None):
        hash = f"{hashlib.sha1(str(str(json.dumps(query_params))).encode()).hexdigest()}"
        cache_get = self.cache.get(hash, "file_query")
        result = [];
        if bool(cache_get) is True:
            print('from cache')
            if first:
                if isinstance(cache_get, list):
                    return File.from_dict(file=cache_get[0], db=self.db, collection_name=self.collection_name)
                return File.from_dict(file=cache_get, db=self.db, collection_name=self.collection_name)
            for doc in cache_get:
                print('doc', doc)
                result.append(File.from_dict(file=doc, db=self.db, collection_name=self.collection_name))
            return result
        query_result = self.custom_query(query_params, first=first, limit=limit, collection_name=self.collection_name)
        # print('query result', query_result)
        if bool(query_result):
            if first:
                value = File.from_dict(file=query_result, db=self.db, collection_name=self.collection_name)
                self.cache.set(hash, value.to_dict(), 3600, "file_query")
                return value
            else:
                for doc in query_result:
                    result.append(File.from_dict(file=doc, db=self.db, collection_name=self.collection_name))
                self.cache.set(hash, File().toListDict(result, None), 3600, "file_query")
                return result
        if first:
                return None
        return [];

    def create(self, **kwargs):
        testMode = kwargs.get('testMode', False)
        data_dict = DictUtils.pick_fields(kwargs, FileFields(fields_props=FileFieldProps).filtered_keys('mutable', True));
        # print('file dict', DictUtils.merge_dict(data_dict, File.generate_model()))
        file_model = File().from_dict(DictUtils.merge_dict(data_dict, File.generate_model()));
        # print('file model', file_model)
        if bool(file_model.to_dict()) is False:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error("Empty request","INVALID_REQUEST", 1))
        document_id = file_model._id;
        doc_ref = self.collection().document(document_id);
        create = {**file_model.to_json(), 'createdAt': firestore.SERVER_TIMESTAMP}
        try:
            if bool(testMode) is False:
                doc_ref.set(create, True);
            data_file = File.from_dict(create)
            return ApiResponse(ResponseStatus.OK, StatusCode.status_200, data_file, None);
        
        except Exception as e:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(str(e),"INVALID_REQUEST", 1));
    
    def update(self, data):
        try:
            last_value = self.to_dict();
            filtered_value = pydash.pick(data, FileFields(fields_props=FileFieldProps).filtered_keys('editable', True));
            new_value = DictUtils.merge_dict(filtered_value, self.to_dict());
            changed_fields = DictUtils.get_changed_field(last_value, new_value);
            # print('last', last_value)
            # print('new', new_value)
            # print('changed', changed_fields)
            # print('data', data)
            data_update = DictUtils.dict_from_keys(filtered_value, changed_fields);
            if bool(data_update) is False:
                return None;
            self.document_reference().set(data_update, merge=True)
            new_file = self.get()
            self.cache.set(new_file.id, new_file)
            return DictUtils.dict_from_keys(new_file.to_dict(), changed_fields);
        except Exception as e:
            print(e)
            return None;

    def remove(self, only_mark_as_removed=True):
        try:
            if self.id is None:
                return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(f"Cannot identify File with id {self.id}","INVALID_REQUEST", 1));
            if only_mark_as_removed:
                self.document_reference().set({"is_removed": True})
            else:
                self.document_reference().delete();
            return ApiResponse(ResponseStatus.OK, StatusCode.status_200, {"message": f"File {self.id} deleted"}, None);
        except:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(f"An error occurated while removing file with id {self.id}","INVALID_REQUEST", 1));
  


