# -*- coding: utf-8 -*-
import os

from threading import Lock
from typing import Tuple
from requests import Session
from io import IOBase
from queue import Queue
import time

from requests.models import Response

# region Wrappers
def JSONResponse(classfunc) -> dict:
    """Decodes `Response`s content to JSON dict"""

    def wrapper(session: Session, *args, **kwargs):
        response: Response = classfunc(session, *args, **kwargs)
        return response.json()

    return wrapper


# endregion

# common models
class LoginException(Exception):
    def __init__(self, resp: Response, desc="") -> None:
        self.resp = resp
        self.desc = desc
        super().__init__(
            "登陆失败：%s\n<Response %s>:%s" % (desc, resp.status_code, resp.text)
        )


class ReprExDict(dict):
    """fancy __repr__-ed dict"""

    def __repr__(self) -> str:
        return "(%s)" % " ".join(["%s/%s" % (k, v) for k, v in self.items()])


class FileManager(dict):
    """single-instance threadsafe file IO manager"""

    CHUNK_SIZE = 2**16

    def __init__(self) -> None:
        super().__init__()
        self.lock = Lock()

    def open(self, path):
        self.lock.acquire()  # preventing multipule instances from accessing all at once
        if not path in self or self[path]["stream"].closed:
            self[path] = {
                "stream": open(path, "rb"),
                "read": 0,
                "length": os.stat(path).st_size,
            }
        self.lock.release()

    def close(self, path):
        stream: IOBase = self[path]["stream"]
        stream.close()
        del self[path]

    def read(self, path, start, end):
        if not path in self:
            self.open(path)  # open for new IO handler
        stream: IOBase = self[path]["stream"]
        self.lock.acquire()  # same as open
        stream.seek(start)
        length = end - start
        self[path]["read"] += length
        self.lock.release()
        return stream.read(length)


class FileIterator:
    """__iter__ impl for `FileManager` with i/o usage monitoring"""

    tell = None

    def __init__(self, path, start, end) -> None:
        self.path, self.start, self.end = path, start, end

    def __getattr__(self, name):  # defining fallback
        return {}

    def __iter__(self):
        start = self.start
        for start in range(self.start, self.end, FileManager.CHUNK_SIZE):
            yield file_manager.read(
                self.path, start, min(self.end, start + FileManager.CHUNK_SIZE)
            )
        if start + FileManager.CHUNK_SIZE < self.end:
            yield file_manager.read(self.path, start, self.end)

    def __len__(self):
        return self.end - self.start

    def to_bytes(self):
        buffer = bytearray()
        for chunk in self:
            buffer.extend(chunk)
        return buffer


def get_timestamp() -> int:
    """returns current time in ms"""
    return int(time.time() * 1000)


def check_file(path) -> Tuple[str, str, int]:
    """checks if targeted path is a file then returns its Full Path, Basename, Size (in Bytes)"""
    assert os.path.isfile(path), "%s 不存在，检查视频源配置！（如：yt-dlp配置了时间窗口）" % path
    size = os.stat(path).st_size
    return path, os.path.basename(path), size


chunk_queue = Queue()
file_manager = FileManager()
