from typing import Dict, List, Tuple, Union, Any
from flask import send_file, Response, stream_with_context
from werkzeug.datastructures import ImmutableMultiDict
from app.core.image import (
    process_and_store_uploaded_images,
    build_image_urls,
    get_image_path,
    get_image_directory,
    remove_image,
    remove_image_folder
)
from app.core.data_validator import validate_data
from app.core.database.operations import (
    delete_items_from_database,
    get_items_from_database,
    update_items_in_database,
)
from app.models.registry import MODEL_REGISTRY
from app.utils.file_manager import (
    stream_large_file,
    create_zip_from_directory,
    file_exists,
)


# Handles the upload and storage of image files for a specific property
def upload_images_service(files: ImmutableMultiDict,
                          id: int) -> Tuple[Dict[str, Any], list[int]]:
    if not files:
        return ({'error': 'Slike nisu prosleđene'}, [400])

    if not id:
        return (
            {"error": "Niste poslali informacije o nekretnini čije slike želite da dodate u bazu"}, [400])

    response = get_items_from_database(
        table_name="items", conditions={"id": id})
    if "error" in response:
        return ({'error': 'Nekretnina nije pronađena'}, [404])

    uploaded_files = files.getlist("file")
    uploaded_files = [
        f for f in uploaded_files if f and f.filename and f.filename.strip() != '']

    if not uploaded_files:
        return ({'error': 'Nijedan validan fajl nije poslat'}, [400])

    errors = process_and_store_uploaded_images(
        uploaded_files=uploaded_files, id=id)
    if errors:
        return ({
            'error': 'Došlo je do greške prilikom čuvanja slika na serveru',
            'details': errors
        }, [500])

    return ({}, [200])


# Returns a list of image URLs for selected property
def list_images_service(
        id: int, watermark: bool = False) -> Tuple[Dict[str, Any], List[int]]:
    if not id:
        return (
            {"error": "Niste poslali informacije o nekretnini čije slike želite da vidite"}, [400])

    response = get_items_from_database(
        table_name="images", conditions={
            "item_id": id})
    if isinstance(response, dict) and "error" in response:
        return (response, [500])

    if not response:
        return ({"error": "Nema pronađenih slika"}, [404])

    image_data = []

    for image in response:
        id = image["id"]
        urls = build_image_urls(image_id=id, include_watermark=watermark)
        tag = image["tag"]
        description = image["description"]

        image_data.append(
            {"id": id, "urls": urls, "tag": tag, "description": description})

    return (image_data, [200])


# Retrieves a specific image file from the filesystem, based on image ID
def get_image_service(image_id: int, watermark: bool = False,
                      thumbnail: bool = False) -> Tuple[Dict[str, Any], list[int]]:
    response = get_items_from_database(
        table_name="images", conditions={
            "id": image_id})
    if "error" in response:
        return ({"error": "Slika nije pronađena u bazi"}, [404])

    image = response[0]
    item_id = image["item_id"]
    filename = image["filename"]

    image_path = get_image_path(
        id=item_id,
        filename=filename,
        watermark=watermark,
        thumbnail=thumbnail)

    if not file_exists(path=image_path):
        return ({"error": "Fajl ne postoji na serveru"}, [404])

    return send_file(image_path, mimetype="image/jpeg"), [200]


# Returns tag options for images
def get_image_tag_options_service(
        model_name: str) -> Tuple[List[Dict[str, Any]], List[int]]:
    image_tag_options = MODEL_REGISTRY[model_name]['metadata']['image_tag_options']
    return (
        {tag.name: tag.value for tag in image_tag_options},
        [200],
    )


# Updates the metadata (tag and description) for a specific image
def set_image_metadata_service(
        image_id: int, tag: str, description: str) -> Tuple[Dict[str, Any], List[int]]:
    if tag is None or description is None:
        return ({"error": "Došlo je do greške pri slanju podataka"}, [400])

    form_values = {"tag": tag, "description": description}
    response, status_codes = validate_data(
        form_values=form_values, model_name="image")
    if "error" in response:
        return (response, status_codes)

    updated_fields = {
        "tag": tag,
        "description": description
    }

    response = update_items_in_database(
        table_name="images",
        updated_fields=updated_fields,
        conditions={
            "id": image_id})
    if "error" in response:
        return (response, [500])

    return (response, [200])


# Packages images for a given property into a ZIP file and returns it for
# download
def download_images_service(
        id: int) -> Tuple[Union[Response, Dict[str, Any]], list[int]]:
    if not id:
        return (
            {"error": "Niste poslali informacije o nekretnini čije slike želite da preuzmete"}, [400])

    image_dir = get_image_directory(item_id=id)

    if not file_exists(path=image_dir):
        return {"error": "Nema slika za zadatu nekretninu"}, [404]

    try:
        zip_buffer = create_zip_from_directory(directory_path=image_dir)

        response = Response(
            stream_with_context(stream_large_file(zip_buffer)),
            content_type='application/zip'
        )
        response.headers['Content-Disposition'] = (
            f'attachment; filename={id}_images.zip'
        )

        return (response, [200])
    except Exception as e:
        return ({"error": "Greška pri kreiranju ZIP fajla",
                "details": str(e)}, [500])


# Deletes a single image record from both the filesystem and the database
def delete_image_service(image_id: int) -> Tuple[Dict[str, Any], List[int]]:
    if not image_id:
        return (
            {"error": "Niste poslali ID slike koje želite da obrišete"}, [400])

    response = get_items_from_database(
        table_name="images", conditions={
            "id": image_id})
    if isinstance(response, dict) and "error" in response:
        return (response, [500])

    if not response:
        return ({"error": "Slika nije pronađena u bazi"}, [404])

    image = response[0]
    filename = image["filename"]
    item_id = image["item_id"]

    response = remove_image(id=item_id, filename=filename)
    if response:
        return ({"error": "Greška pri brisanju slike sa diska",
                "details": response}, [500])

    response = delete_items_from_database(
        table_name="images", conditions={"id": image_id})
    if "error" in response:
        return (response, [500])

    return ({}, [200])


# Deletes all images for selected property
def delete_images_by_item_id_service(
        id: int) -> Tuple[Dict[str, Any], List[int]]:
    if not id:
        return (
            {"error": "Niste poslali ID nekretnine čije slike želite da obrišete"}, [400])

    response = get_items_from_database(
        table_name="images", conditions={
            "item_id": id})
    if isinstance(response, dict) and "error" in response:
        return (response, [500])

    images = response
    if not images:
        return ({"error": "Nema pronađenih slika za ovu nekretninu"}, [404])

    response = remove_image_folder(item_id=id)
    if response:
        return ({"error": "Greška pri brisanju foldera sa slikama",
                "details": response}, [500])

    response = delete_items_from_database(
        table_name="images", conditions={"item_id": id})
    if "error" in response:
        return (response, [500])

    return ({}, [200])
