
import hashlib
from datetime import datetime
from email.utils import parsedate_to_datetime
from mimetypes import guess_type

import asyncio

from ducts import utils
from ifconf import configure_module, config_callback

import logging
logger = logging.getLogger(__name__)
        
@config_callback
def config(loader):
    pass

conf = configure_module(config)

GroupMetadata = utils.nameddict(
    'GroupMetadata',
    (
        'gid'
        , 'group_key'
        , 'group_key_text'
        , 'group_name'
        , 'content_type'
    ))

ContentMetadata = utils.nameddict(
    'ContentMetadata',
    (
        'cid'
        , 'content_key'
        , 'content_key_name'
#        , 'content_name'
        , 'content_type'
        , 'content_length'
        , 'last_modified'
        , 'encoding'
#        , 'md5'
        , 'order'
        , 'revert_to'
    ))

def is_compatible_with_sha1_key(key):
    try:
        return len(key) == 40 and int(key, 16) != 0
    except ValueError:
        return False

def key_for(key):
    if key:
        if is_compatible_with_sha1_key(key):
            return key
        else:
            return hashlib.sha1(key.encode('UTF-8')).hexdigest()
    else:
        raise KeyError('key is empty')

def scan_key_blobs():
    return 'BLOBS/*'

def scan_key_blobs_gid(group_id):
    return f'BLOBS/GID={group_id}*'

def incr_key_for_group_id():
    return f'BLOBS/GROUP_ID'

def zset_key_for_group_names():
    return f'BLOBS/GROUP_NAMES'

def stream_key_for_group_metadata(group_key):
    return f'BLOBS/GKEY={group_key}/METADATA'


def incr_key_for_content_id(group_id):
    return f'BLOBS/GID={group_id}/CONTENT_ID'

def zset_key_for_content_keys(group_id):
    return f'BLOBS/GID={group_id}/CONTENT_KEYS'

#def stream_key_for_contents_metadata(group_id):
#    return f'BLOBS/GID={group_id}/CONTENTS_METADATA'

#def list_key_for_versions(group_id, content_key):
#    return f'BLOBS/GID={group_id}/CKEY={content_key}/VERSIONS'

def stream_key_for_contents_metadata(group_id, content_key):
    return f'BLOBS/GID={group_id}/CKEY={content_key}/METADATA'

def obj_key_for_content(group_id, content_id):
    return f'BLOBS/GID={group_id}/CID={content_id}/OBJ'

def stream_key_for_object_buffer():
    return 'BLOBS/BUFFER'

def obj_key_for_object_buffer(session_id, buffer_id):
    return f'BLOBS/BUFFER/{session_id}/{buffer_id}'



async def get_group_metadata_with_version(redis, group_key):
    key = key_for(group_key)
    stream_key = stream_key_for_group_metadata(key)
    stream_id, kv = await redis.xlast_str_with_id(stream_key)
    if not stream_id:
        raise KeyError('group_key[{}] not found.'.format(group_key))
    return (GroupMetadata(kv), stream_id)

async def get_group_metadata(redis, group_key):
    key = key_for(group_key)
    stream_key = stream_key_for_group_metadata(key)
    kv = await redis.xlast_str(stream_key)
    if not kv:
        raise KeyError('group_key[{}] not found.'.format(group_key))
    return GroupMetadata(kv)

async def get_group_content_metadata_with_version(redis, group_key, content_key):
    group = await get_group_metadata(redis, group_key)

    key = key_for(content_key)
    stream_key = stream_key_for_contents_metadata(group.gid, key)
    stream_id, kv = await redis.xlast_str_with_id(stream_key)
    if not stream_id:
        raise KeyError('content_key [{}/{}] not found.'.format(group_key, content_key))
    return (group, ContentMetadata(kv), stream_id)
    
async def get_group_content_metadata_for(redis, group_key, content_key, version):
    group = await get_group_metadata(redis, group_key)

    key = key_for(content_key)
    stream_key = stream_key_for_contents_metadata(group.gid, key)
    ret_version, content_dict = await redis.xget_str_with_id(stream_key, version)
    content = ContentMetadata(content_dict)
    if not content or content.content_key != key:
        raise KeyError('version[{}] not found in content_key [{}/{}]'.format(version, group_key, content_key))
    return (group, content, ret_version)

def determine_last_modified(last_modified_in_request):
    if last_modified_in_request:
        try:
            dt = datetime.fromtimestamp(int(last_modified_in_request))
        except ValueError:
            dt = parsedate_to_datetime(last_modified_in_request)
    else:
       dt = datetime.now()
    return int(dt.timestamp())

async def get_content_metadata(params : dict
                               , content_key : str
#                               , content_name : str
                               , content_type : str
                               , encoding : str
                               , last_modified : str):

    metadata = ContentMetadata(params)
    if content_key:
        if is_compatible_with_sha1_key(content_key):
            metadata.content_key = content_key
#            metadata.content_name = content_name
        else:
            metadata.content_key = hashlib.sha1(content_key.encode('UTF-8')).hexdigest()
            metadata.content_key_name = content_key
#            metadata.content_name = content_name if content_name else content_key
#    metadata.content_type = content_type if content_type else guess_type(content_name)[0]
    metadata.content_type = content_type if content_type else guess_type(content_key)[0]
    metadata.last_modified = determine_last_modified(last_modified)
    return metadata

