import os
import posixpath
from pathlib import Path
from typing import Tuple

from ytdl_sub.config.defaults import MAX_FILE_NAME_BYTES
from ytdl_sub.utils.file_handler import get_file_extension


class FilePathTruncater:
    _EXTENSION_BYTES = len("-thumb.jpg".encode("utf-8")) + 8
    _DEFAULT_MAX_BASE_FILE_NAME_BYTES: int = MAX_FILE_NAME_BYTES - _EXTENSION_BYTES

    _MAX_BASE_FILE_NAME_BYTES: int = _DEFAULT_MAX_BASE_FILE_NAME_BYTES

    @classmethod
    def set_max_file_name_bytes(cls, max_file_name_bytes: int) -> None:
        """Actually sets the max _base_ file name in bytes (excludes extension)"""
        max_base_file_name_bytes = max_file_name_bytes - cls._EXTENSION_BYTES

        # bound between (extension_bytes + 20, MAX_FILE_NAME_BYTES)
        max_base_file_name_bytes = max(max_base_file_name_bytes, 16)
        max_base_file_name_bytes = min(
            max_base_file_name_bytes, MAX_FILE_NAME_BYTES - cls._EXTENSION_BYTES
        )

        cls._MAX_BASE_FILE_NAME_BYTES = max_base_file_name_bytes

    @classmethod
    def _is_file_name_too_long(cls, file_name: str) -> bool:
        return len(file_name.encode("utf-8")) > cls._MAX_BASE_FILE_NAME_BYTES

    @classmethod
    def _get_extension_split(cls, file_name: str) -> Tuple[str, str, str]:
        if file_name.endswith("-thumb.jpg"):
            ext = "-thumb.jpg"
            delimiter = ""
        else:
            ext = get_file_extension(file_name)
            delimiter = "."

        return file_name[: -len(ext)], ext, delimiter

    @classmethod
    def _truncate_file_name(cls, file_name: str) -> str:
        file_sub_name, file_ext, delimiter = cls._get_extension_split(file_name)

        desired_size = cls._MAX_BASE_FILE_NAME_BYTES - len(file_ext.encode("utf-8")) - 1
        while len(file_sub_name.encode("utf-8")) > desired_size:
            file_sub_name = file_sub_name[:-1]

        return f"{file_sub_name}{delimiter}{file_ext}"

    @classmethod
    def maybe_truncate_file_path(cls, file_path: str) -> str:
        """Turn into a Path, then a string, to get correct directory separators"""
        file_directory, file_name = os.path.split(Path(file_path))

        if cls._is_file_name_too_long(file_name):
            return str(Path(file_directory) / cls._truncate_file_name(file_name))

        return str(file_path)

    @classmethod
    def to_native_filepath(cls, file_path: str) -> str:
        """Ensures file paths use the correct separator"""
        return os.path.expanduser(file_path.replace(posixpath.sep, os.sep))
