import os
import shutil
import io
import zipfile
from io import BytesIO
from typing import Tuple, Union, List, Optional, Iterator
from werkzeug.datastructures import FileStorage


# Checks if the file size exceeds 50MB limit
def check_size(file: FileStorage) -> bool:
    try:
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)
        return file_size > (50 * 1024 * 1024)  # 50MB
    except Exception as e:
        raise ValueError(f"Greška pri proveri veličine fajla: {str(e)}")


# Streams a large file in chunks (default 1MB per chunk)
def stream_large_file(
        file_buffer: io.BytesIO,
        chunk_size: int = 1024 *
        1024) -> Iterator[bytes]:
    try:
        while True:
            chunk = file_buffer.read(chunk_size)
            if not chunk:
                break
            yield chunk
    except Exception as e:
        raise RuntimeError(f"Greška pri strimovanju fajla: {str(e)}")


# Creates a ZIP archive in memory containing only full-size watermark
# images (excludes thumbnails)
def create_zip_from_directory(directory_path: str) -> BytesIO:
    try:
        zip_buffer = BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, _, files in os.walk(directory_path):
                for file in files:
                    if "thumbnail" in file:
                        continue

                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, directory_path)
                    zipf.write(file_path, arcname)

        zip_buffer.seek(0)
        return zip_buffer
    except Exception as e:
        raise RuntimeError(
            f"Greška pri kreiranju ZIP arhive iz foldera '{directory_path}': {str(e)}")


# Returns the next available numeric filename based on existing image numbers
def get_next_available_filename_number(existing_filenames: list[int]) -> int:
    if not existing_filenames:
        return 1
    return max(existing_filenames) + 1


# Splits the filename into name and extension
def split_filename(filename: str) -> Tuple[str, str]:
    return os.path.splitext(filename)


# Checks if the given file or directory path exists on the filesystem
def file_exists(path: str) -> bool:
    return os.path.exists(path)


# Joins folder path and filename into a full file path
def build_file_path(folder_path: str, filename: str) -> str:
    return os.path.join(folder_path, filename)


# Builds a full folder path by combining the base directory with the given
# directory name
def build_folder_path(base_dir: str, dir_name: Union[str, int]) -> str:
    return os.path.join(base_dir, str(dir_name))


# Attempts to delete all files in the given list of paths; returns error
# message if any fail
def remove_files(paths: List[str]) -> Optional[str]:
    for path in paths:
        if not os.path.exists(path) or not os.access(path, os.W_OK):
            return f"Fajl nije moguće obrisati: {path}"
        try:
            os.remove(path)
        except Exception as e:
            return f"Greška pri brisanju fajla: {path} – {str(e)}"
    return None


# Recursively deletes the specified folder and all its contents if it exists.
def remove_folder(folder_path: str) -> Optional[str]:
    if not os.path.exists(folder_path) or not os.access(folder_path, os.W_OK):
        return f"Folder nije moguće obrisati: {folder_path}"
    try:
        shutil.rmtree(folder_path)
    except Exception as e:
        return f"Greška pri brisanju foldera: {folder_path} – {str(e)}"
    return None


# Ensures the folder exists and returns its path along with sorted numeric
# filenames already in it
def prepare_folder(base_dir: str, id: int) -> Tuple[str, List[int]]:
    try:
        folder_path = os.path.join(base_dir, str(id))
        os.makedirs(folder_path, exist_ok=True)

        existing_files = sorted([
            int(f.split('.')[0])
            for f in os.listdir(folder_path)
            if f.endswith(".jpg") and f.split('.')[0].isdigit()
        ])

        return folder_path, existing_files
    except Exception as e:
        raise ValueError(f"Greška pri pripremi foldera: {str(e)}")
