# Flyto2 Core - Source Available License
# Copyright (c) 2025 Flyto2. All Rights Reserved.
#
# This source code is licensed under the Flyto2 Source Available License v1.0.
# Commercial use requires a license. See LICENSE for details.

"""
Image Convert Module
Convert image between formats (PNG, JPEG, WEBP, etc.)
"""
import logging
import os
from pathlib import Path
from typing import Any, Dict, Optional

from ...registry import register_module
from ...schema import compose, presets


logger = logging.getLogger(__name__)


# Supported formats and their extensions
SUPPORTED_FORMATS = {
    'jpeg': ['.jpg', '.jpeg'],
    'png': ['.png'],
    'webp': ['.webp'],
    'gif': ['.gif'],
    'bmp': ['.bmp'],
    'tiff': ['.tiff', '.tif'],
    'ico': ['.ico'],
}

FORMAT_ALIASES = {
    'jpg': 'jpeg',
    'tif': 'tiff',
}


def get_format_from_extension(path: str) -> Optional[str]:
    """Get format name from file extension"""
    ext = os.path.splitext(path)[1].lower()
    for fmt, extensions in SUPPORTED_FORMATS.items():
        if ext in extensions:
            return fmt
    return None


@register_module(
    module_id='image.convert',
    version='1.0.0',
    category='image',
    subcategory='convert',
    tags=['image', 'convert', 'format', 'media', 'path_restricted'],
    label='Convert Image',
    label_key='modules.image.convert.label',
    description='Convert image to different format (PNG, JPEG, WEBP, etc.)',
    description_key='modules.image.convert.description',
    icon='Image',
    color='#8B5CF6',

    # Connection types
    input_types=['file_path', 'binary'],
    output_types=['file_path', 'binary'],
    can_connect_to=['image.*', 'file.*'],
    can_receive_from=['file.*', 'browser.*', 'http.*', 'flow.*', 'start'],

    # Execution settings
    timeout_ms=120000,
    retryable=False,
    concurrent_safe=True,

    # Security settings
    requires_credentials=False,
    handles_sensitive_data=False,
    required_permissions=[],

    params_schema=compose(
        presets.IMAGE_INPUT_PATH(),
        presets.IMAGE_OUTPUT_PATH(),
        presets.IMAGE_FORMAT(required=True),
        presets.IMAGE_QUALITY(),
        presets.IMAGE_RESIZE_OPTIONS(),
    ),
    output_schema={
        'path': {
            'type': 'string',
            'description': 'Path to the converted image'
        ,
                'description_key': 'modules.image.convert.output.path.description'},
        'size': {
            'type': 'number',
            'description': 'File size in bytes'
        ,
                'description_key': 'modules.image.convert.output.size.description'},
        'format': {
            'type': 'string',
            'description': 'Output format'
        ,
                'description_key': 'modules.image.convert.output.format.description'},
        'dimensions': {
            'type': 'object',
            'description': 'Image dimensions {width, height}'
        ,
                'description_key': 'modules.image.convert.output.dimensions.description'}
    },
    examples=[
        {
            'title': 'Convert PNG to JPEG',
            'title_key': 'modules.image.convert.examples.png_to_jpeg.title',
            'params': {
                'input_path': '/tmp/image.png',
                'format': 'jpeg',
                'quality': 90
            }
        }
    ],
    author='Flyto Team',
    license='MIT'
)
async def image_convert(context: Dict[str, Any]) -> Dict[str, Any]:
    """Convert image to different format"""
    try:
        from PIL import Image
    except ImportError:
        raise ImportError("Pillow is required for image conversion. Install with: pip install Pillow")

    params = context['params']
    input_path = params['input_path']
    output_format = params['format'].lower()
    quality = params.get('quality', 85)
    resize = params.get('resize')
    output_path = params.get('output_path')

    # Normalize format
    if output_format in FORMAT_ALIASES:
        output_format = FORMAT_ALIASES[output_format]

    if output_format not in SUPPORTED_FORMATS:
        raise ValueError(f"Unsupported format: {output_format}")

    # Validate input file
    if not os.path.exists(input_path):
        raise FileNotFoundError(f"Input file not found: {input_path}")

    # Determine output path
    if not output_path:
        base_name = os.path.splitext(input_path)[0]
        extension = SUPPORTED_FORMATS[output_format][0]
        output_path = f"{base_name}{extension}"

    # Ensure output directory exists
    Path(os.path.dirname(output_path)).mkdir(parents=True, exist_ok=True)

    # Open and convert image
    with Image.open(input_path) as img:
        # Handle resize
        if resize:
            width = resize.get('width')
            height = resize.get('height')
            keep_aspect = resize.get('keep_aspect', True)

            if width and height:
                if keep_aspect:
                    img.thumbnail((width, height), Image.Resampling.LANCZOS)
                else:
                    img = img.resize((width, height), Image.Resampling.LANCZOS)
            elif width:
                ratio = width / img.width
                new_height = int(img.height * ratio)
                img = img.resize((width, new_height), Image.Resampling.LANCZOS)
            elif height:
                ratio = height / img.height
                new_width = int(img.width * ratio)
                img = img.resize((new_width, height), Image.Resampling.LANCZOS)

        # Convert color mode if needed
        if output_format == 'jpeg' and img.mode in ('RGBA', 'P'):
            img = img.convert('RGB')

        # Save with appropriate options
        save_kwargs = {}
        if output_format in ('jpeg', 'webp'):
            save_kwargs['quality'] = quality
        if output_format == 'png':
            save_kwargs['optimize'] = True

        img.save(output_path, format=output_format.upper(), **save_kwargs)

        dimensions = {'width': img.width, 'height': img.height}

    file_size = os.path.getsize(output_path)

    logger.info(f"Converted image: {input_path} -> {output_path} ({output_format})")

    return {
        'ok': True,
        'path': output_path,
        'size': file_size,
        'format': output_format,
        'dimensions': dimensions
    }
