from PIL import Image, ImageOps
from typing import Union, Tuple, List, Optional, Dict, Any
from app.core.config import UPLOAD_DIR
from app.core.database.operations import add_item_to_database
from app.utils.images import resize_image, add_watermark, prepare_for_saving_as_jpg
from app.utils.file_manager import (
    check_size,
    get_next_available_filename_number,
    build_file_path,
    split_filename,
    remove_files,
    prepare_folder,
    remove_folder,
    build_folder_path
)
from werkzeug.datastructures.file_storage import FileStorage


ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "HEIC"}
MAX_SIZE = (1920, 1080)
THUMBNAIL_SIZE = (500, 500)


# Checks if the file has an allowed extension for upload
def allowed_file(filename: str) -> bool:
    return '.' in filename and filename.rsplit(
        '.', 1)[1].lower() in ALLOWED_EXTENSIONS


# Processes and saves uploaded image files (original and thumbnail) for a
# given property
def process_and_store_uploaded_images(
    uploaded_files: List[FileStorage],
    id: int
) -> Union[List[dict[str, str]], str]:
    errors = []

    try:
        folder_path, existing_filenames = prepare_image_folder(item_id=id)
    except ValueError as e:
        return f'Greška pri pripremi foldera: {str(e)}'

    next_filename = get_next_available_filename_number(
        existing_filenames=existing_filenames)

    for file in uploaded_files:
        if file and allowed_file(filename=file.filename):
            if check_size(file):
                errors.append(
                    {'file': file.filename, 'error': 'Fajl je prevelik'})
                continue

            filename = f"{next_filename}.jpg"
            next_filename += 1

            save_path = build_file_path(
                folder_path=folder_path, filename=filename)
            thumbnail_path = save_path.replace(".jpg", "_thumbnail.jpg")
            watermark_path = save_path.replace(".jpg", "_watermark.jpg")
            thumbnail_watermark_image_path = save_path.replace(
                ".jpg", "_thumbnail_watermark.jpg")
            image = Image.open(file)
            image = prepare_for_saving_as_jpg(image=image)
            image = ImageOps.exif_transpose(image)

            try:
                downsized_image = resize_image(image=image, max_size=MAX_SIZE)
                downsized_image.save(save_path)
                thumbnail_image = resize_image(
                    image=image, max_size=THUMBNAIL_SIZE)
                thumbnail_image.save(thumbnail_path)
                watermark_image = add_watermark(image=downsized_image)
                watermark_image.save(watermark_path)

                thumbnail_watermark_image = add_watermark(
                    image=thumbnail_image)
                thumbnail_watermark_image.save(thumbnail_watermark_image_path)

                image_data = {
                    "filename": filename,
                    "item_id": id,
                }

                response = add_item_to_database(
                    table_name="images", item_dict=image_data)
                if "error" in response:
                    errors.append(
                        {'file': file.filename, 'error': response["error"]})

            except Exception as e:
                errors.append({'file': file.filename, 'error': str(e)})

        else:
            errors.append(
                {'file': file.filename, 'error': 'Nedozvoljen tip fajla'})

    return errors


def build_image_urls(
        image_id: int, include_watermark: bool = False) -> Dict[str, Any]:
    if include_watermark:
        urls = {
            "thumbnail": f"/get_public_image/{image_id}?thumbnail=true",
            "full_size": f"/get_public_image/{image_id}"
        }
    else:
        urls = {
            "thumbnail": f"/get_image/{image_id}?thumbnail=true",
            "full_size": f"/get_image/{image_id}"
        }

    return urls


# Builds the full file path for an image, optionally adding watermark and
# thumbnail suffixes
def get_image_path(
    id: int,
    filename: str,
    watermark: bool = False,
    thumbnail: bool = False
) -> str:
    base_name, ext = split_filename(filename=filename)

    if thumbnail:
        base_name += "_thumbnail"
    if watermark:
        base_name += "_watermark"

    filename = f"{base_name}{ext}"
    folder_path = build_folder_path(base_dir=UPLOAD_DIR, dir_name=str(id))

    return build_file_path(folder_path=folder_path, filename=filename)


# Generates full file paths for all image variants (original, thumbnail,
# watermark, etc.)
def get_image_path_variants(id: Union[int, str], filename: str) -> List[str]:
    folder = build_folder_path(base_dir=UPLOAD_DIR, dir_name=str(id))
    base_name, ext = split_filename(filename=filename)

    variants = [
        f"{base_name}{ext}",
        f"{base_name}_watermark{ext}",
        f"{base_name}_thumbnail{ext}",
        f"{base_name}_thumbnail_watermark{ext}"
    ]

    return [build_file_path(folder_path=folder, filename=name)
            for name in variants]


# Returns the image folder path for the given property ID
def get_image_directory(item_id: Union[int, str]) -> str:
    return build_folder_path(base_dir=UPLOAD_DIR, dir_name=str(item_id))


# Removes all versions (original and thumbnail) of a given image from the
# filesystem
def remove_image(id: Union[str, int], filename: str) -> Optional[str]:
    paths = get_image_path_variants(id=id, filename=filename)
    return remove_files(paths=paths)


# Deletes the entire image folder for the given property ID
def remove_image_folder(item_id: Union[str, int]) -> Optional[str]:
    folder_path = build_folder_path(base_dir=UPLOAD_DIR, dir_name=str(item_id))
    return remove_folder(folder_path=folder_path)


# Ensures the image folder exists and returns its path along with existing
# image numbers
def prepare_image_folder(item_id: int) -> Tuple[str, List[int]]:
    return prepare_folder(base_dir=UPLOAD_DIR, id=str(item_id))
