#  Copyright (C) 2023
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Vasiliev Ivan <i.vasiliev@technokert.ru>


import logging
import warnings
from dataclasses import dataclass
from io import BytesIO
from typing import Optional

import yarl
from aiohttp import FormData
from http_tools import AbmServiceConnector
from http_tools.mime_types import ContentType
from init_helpers import dict_to_dataclass


logger = logging.getLogger(__name__)


@dataclass
class FileInfo:
    key: str
    md5: str
    sha512: str
    size: int


@dataclass
class FileLink:
    id: str
    filename: str
    extension: str | None
    file_info: FileInfo

    @property
    def size(self) -> int:
        warnings.warn("is deprecated, replace it with 'file_info.size'", DeprecationWarning)
        return self.file_info.size

    @property
    def full_file_name(self) -> str:
        warnings.warn("is deprecated, replace it with 'filename'", DeprecationWarning)
        return self.filename

    @property
    def file_extension(self) -> str | None:
        warnings.warn("is deprecated, replace it with 'extension'", DeprecationWarning)
        return self.extension

    @property
    def quoted_full_file_name(self) -> str:
        warnings.warn("is deprecated, was removed", DeprecationWarning)
        return self.filename

    @property
    def content_type(self) -> str:
        warnings.warn("is deprecated, was removed", DeprecationWarning)
        return ''

    @property
    def thumbnail(self) -> None:
        warnings.warn("is deprecated, was removed", DeprecationWarning)
        return None


FileDescriptor = FileLink  # backward compatibility


class FileServerConnector:
    Config = AbmServiceConnector.Config
    Context = AbmServiceConnector.Context

    def __init__(self, config: Config, context: Context):
        self.config = config
        self._connector = AbmServiceConnector(config, context)

    def get_file_url(self, file_id: str) -> yarl.URL:
        return (yarl.URL(self.config.url) / 'file/get').with_query({'id': file_id})

    async def get_file(self, file_id: str, range_: Optional[str] = None) -> bytes:
        args = {"id": file_id}
        headers = self._construct_headers()
        if range_ is not None:
            headers["Range"] = range_
        content = await self._connector.get("/file/get", args, headers=headers)
        assert isinstance(content, bytes)
        return content

    async def get_file_info(self, file_id: str) -> FileLink | None:
        args = {"id": file_id}
        if answer := await self._connector.get("/file/info", args, headers=self._construct_headers()):
            return dict_to_dataclass(answer, FileLink)

    async def get_thumbnail(self, file_id: str) -> bytes:
        return await self._connector.get("file/preview", {"id": file_id}, headers=self._construct_headers())

    async def upload_file(
            self, filename: str, content: bytes, is_thumbnail_required: bool = False
    ) -> tuple[FileLink, FileLink | None]:
        form_data = FormData()
        is_thumbnail_required = str(is_thumbnail_required).lower()
        form_data.add_field("is_thumbnail_required", is_thumbnail_required, content_type=ContentType.Text.value)
        form_data.add_field("file", BytesIO(content), filename=filename)
        answer = await self._connector.post("/file/add", payload=form_data, headers=self._construct_headers())
        original = answer.get("original")
        if not original:
            raise ValueError(f'Wrong answer: {answer}')
        original = dict_to_dataclass(original, FileLink)
        if thumbnail := answer.get("thumbnail"):
            thumbnail = dict_to_dataclass(thumbnail, FileLink)
        return original, thumbnail

    async def upload_from(self, file_url: str | yarl.URL) -> FileLink:
        answer = await self._connector.post(
            "/file/add_from", payload={"file_url": str(file_url)}, headers=self._construct_headers()
        )
        return dict_to_dataclass(answer, FileLink)

    async def add_file(self, file: bytes, filename: str, *args, **kwargs) -> FileLink:
        warnings.warn("is deprecated, replace it with 'upload_file'", DeprecationWarning)
        return (await self.upload_file(filename, file))[0]

    async def delete_file(self, file_id: str) -> FileLink:
        args = {"id": file_id}
        answer = await self._connector.post("/file/delete", payload=None,
                                            url_query=args, headers=self._construct_headers())
        return dict_to_dataclass(answer, FileLink)

    def _construct_headers(self) -> dict[str, str]:
        headers = {"Connection": "Keep-Alive"}
        return headers
