# coding=utf8

from __future__ import annotations

import base64
import requests

from auto_keyworder_modules import util, datamodel


class AssetInfoFailedException(Exception):

    def __init__(self, detail):
        self.detail = detail
        self.message = f'asset info could not be retrieved from object: detail'
        super(Exception, self).__init__(self.message)


def get_asset_info(
    obj: dict,
    objecttype: str,
    asset_field: str,
    asset_version: str,
    url_required: bool,
) -> tuple[int, str, str, str]:
    assets = util.get_json_value(obj, f'{objecttype}.{asset_field}')
    if not isinstance(assets, list) or len(assets) < 1:
        raise AssetInfoFailedException(f'{objecttype}.{asset_field}')

    eas_id = util.get_json_value(assets[0], '_id')
    if eas_id is None:
        raise AssetInfoFailedException(
            f'versions.{asset_version}._id not found in asset field'
        )

    eas_url = None
    if url_required:
        eas_url = util.get_json_value(assets[0], f'versions.{asset_version}.url')
        if eas_url is None:
            raise AssetInfoFailedException(
                f'versions.{asset_version}.url not found in asset field'
            )

    img_type = util.get_json_value(assets[0], f'versions.{asset_version}.extension')
    if img_type is None:
        raise AssetInfoFailedException(
            f'versions.{asset_version}.extension not found in asset field'
        )

    original_filepath = util.get_json_value(assets[0], 'original_filepath')

    return eas_id, eas_url, img_type, original_filepath


class AssetStatus(object):

    easydb_obj: dict

    # asset information
    eas_id: int = None
    img_url: str = None
    img_type: str = None
    base64_data: str = None
    original_filepath: str = None

    # status of asset/object
    uploaded: bool = False
    finished: bool = False
    failed: bool = False
    record_updated: bool = False
    failure_reason: str = None
    diff: str[str] = set()

    # ai service results
    ai_job_id: str = None
    ai_data_added_to_object: bool = False
    ai_data: dict = {}

    def __init__(self, easydb_obj):
        self.easydb_obj = easydb_obj

    def easydb_obj_objecttype(self) -> str:
        return self.easydb_obj.get('_objecttype')

    def easydb_obj_sys_id(self) -> int:
        return self.easydb_obj.get('_system_object_id')

    def easydb_obj_id(self) -> int:
        ot = self.easydb_obj_objecttype()
        if not ot:
            return None
        sub_obj = self.easydb_obj.get(ot)
        if not sub_obj:
            return None
        return sub_obj.get('_id')

    def easydb_obj_version(self) -> int:
        ot = self.easydb_obj_objecttype()
        if not ot:
            return None
        sub_obj = self.easydb_obj.get(ot)
        if not sub_obj:
            return None
        return sub_obj.get('_version')

    def to_dict(self):
        d = {}
        data = {
            'easydb_obj': self.easydb_obj,
            'eas_id': self.eas_id,
            'img_type': self.img_type,
            'original_filepath': self.original_filepath,
            'uploaded': self.uploaded,
            'finished': self.finished,
            'failed': self.failed,
            'reason': self.failure_reason,
            'ai_data': self.ai_data,
        }

        for k in data:
            if data[k] is None:
                continue
            d[k] = data[k]

        return d

    def copy(self) -> AssetStatus:
        new = AssetStatus(self.easydb_obj)
        new.eas_id = self.eas_id
        new.img_url = self.img_url
        new.img_type = self.img_type
        new.base64_data = self.base64_data
        new.original_filepath = self.original_filepath
        new.uploaded = self.uploaded
        new.finished = self.finished
        new.failed = self.failed
        new.record_updated = self.record_updated
        new.failure_reason = self.failure_reason
        new.ai_job_id = self.ai_job_id
        new.ai_data_added_to_object = self.ai_data_added_to_object
        new.ai_data = self.ai_data
        return new

    def add_to_diff(
        self,
        field_info: datamodel.FieldInfo,
        field_value: str,
    ) -> None:
        ot = self.easydb_obj_objecttype()

        field_name = ''
        field_type = ''

        if field_info.is_linked():
            field_name = f'{field_info.link_name}.{field_info.name}'
            field_type = f'link: {field_info.linked_ot}'
        else:
            field_name = field_info.name
            field_type = field_info.typ

        if not field_info.is_in_nested():
            self.diff.add(
                f'set field {ot}.{field_name} (type: "{field_type}", value: "{field_value}")'
            )
            return

        self.diff.add(
            f'set field {field_name} (type: "{field_type}", value: "{field_value}") in {field_info.nested}'
        )


def map_assets(
    asset_map: dict[int, AssetStatus],
    objecttype: str,
    asset_field: str,
    asset_version: str,
    use_base64: bool,
) -> tuple[set[int], list[str]]:
    pending_objects: set[int] = set()
    collectect_errors: list[str] = []

    for sys_id in asset_map:
        asset = asset_map[sys_id]
        if asset.eas_id is not None:
            continue
        if asset.easydb_obj is None:
            continue

        try:
            eas_id, eas_url, img_type, original_filepath = get_asset_info(
                asset.easydb_obj,
                objecttype,
                asset_field,
                asset_version,
                True,
            )
            asset_map[sys_id].img_url = eas_url
            asset_map[sys_id].eas_id = eas_id
            asset_map[sys_id].img_type = img_type
            asset_map[sys_id].original_filepath = original_filepath

            if use_base64:
                base64_data = base64_from_url(eas_url)
                if base64_data is None:
                    collectect_errors.append(
                        f'object #{sys_id}: could not convert asset data to base64'
                    )
                    continue
                asset_map[sys_id].base64_data = base64_data

            pending_objects.add(sys_id)
        except Exception as e:
            collectect_errors.append(
                f'object #{sys_id}: could not get asset information: {e}'
            )

    return pending_objects, collectect_errors


def base64_from_url(fileurl: str) -> str:
    response = requests.get(fileurl)
    if response.status_code != 200:
        return None

    return base64.b64encode(response.content).decode('utf-8')


class AssetSkippedException(Exception):

    def __init__(self, reason):
        self.reason = reason
        self.message = f'asset skipped, reason: {reason}'
        super(Exception, self).__init__(self.message)
