"""
Step 4 Brand Asset Routes
Handles brand avatar generation, enhancement, and variation.
"""

from typing import Dict, Any, Optional
from fastapi import APIRouter, HTTPException, Depends, File, UploadFile, Form
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
from pydantic import BaseModel
from loguru import logger
from .step4_persona_routes import _extract_user_id
from middleware.auth_middleware import get_current_user
import base64
import os
from pathlib import Path
from utils.file_storage import save_file_safely, generate_unique_filename
from services.database import get_db, WORKSPACE_DIR
from utils.asset_tracker import save_asset_to_library
from models.content_asset_models import ContentAsset, AssetType, AssetSource
from sqlalchemy import desc

from services.llm_providers.main_image_generation import (
    generate_image_with_provider,
    enhance_image_prompt,
    generate_image_variation,
    generate_image_enhance
)
from services.llm_providers.main_audio_generation import clone_voice, qwen3_voice_clone, cosyvoice_voice_clone, qwen3_voice_design
import asyncio
import random
import string

router = APIRouter(prefix="/onboarding/assets")

# --- Models ---
class VoiceDesignRequest(BaseModel):
    user_id: Optional[str] = None
    text: str
    voice_description: str
    language: str = "auto"

class AvatarPromptRequest(BaseModel):
    user_id: Optional[str] = None
    prompt: str
    aspect_ratio: str = "1:1"
    style_preset: Optional[str] = None
    negative_prompt: Optional[str] = None
    num_inference_steps: int = 30
    guidance_scale: float = 7.5
    model: Optional[str] = None
    rendering_speed: Optional[str] = None
    provider: Optional[str] = None

class AvatarEnhanceRequest(BaseModel):
    user_id: Optional[str] = None
    prompt: str

class VoiceCloneRequest(BaseModel):
    user_id: Optional[str] = None
    voice_name: str
    description: Optional[str] = None
    engine: str = "qwen3" # qwen3 or minimax

# --- Routes ---

@router.get("/latest-avatar")
async def get_latest_avatar(
    current_user: Dict[str, Any] = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get the latest generated brand avatar for the user."""
    try:
        user_id = _extract_user_id(current_user)
        
        # Search for assets that are either:
        # 1. Saved with source_module=BRAND_AVATAR_GENERATOR (new)
        # 2. Saved with source_module=STORY_WRITER but have metadata category='brand_avatar' (legacy)
        
        # Fetch candidates (limit to recent 20 to avoid performance issues)
        candidates = db.query(ContentAsset).filter(
            ContentAsset.user_id == user_id,
            ContentAsset.asset_type == AssetType.IMAGE,
            ContentAsset.source_module.in_([
                AssetSource.BRAND_AVATAR_GENERATOR, 
                AssetSource.STORY_WRITER
            ])
        ).order_by(desc(ContentAsset.created_at)).limit(50).all()
        
        asset = None
        for candidate in candidates:
            # Check for direct match (new assets)
            if candidate.source_module == AssetSource.BRAND_AVATAR_GENERATOR:
                asset = candidate
                break
            
            # Check for legacy match (metadata category)
            if candidate.source_module == AssetSource.STORY_WRITER:
                meta = candidate.asset_metadata or {}
                if meta.get('category') == 'brand_avatar':
                    asset = candidate
                    break
        
        if not asset:
            return {"success": False, "message": "No avatar found"}
            
        # Fallback to metadata prompt if main column is empty (legacy support)
        prompt = asset.prompt
        if not prompt and asset.asset_metadata:
            prompt = asset.asset_metadata.get('prompt', '')
            
        return {
            "success": True,
            "image_url": asset.file_url,
            "prompt": prompt,
            "asset_id": asset.id,
            "provider": asset.provider
        }
    except Exception as e:
        logger.error(f"Failed to fetch latest avatar: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/latest-voice-clone")
async def get_latest_voice_clone(
    current_user: Dict[str, Any] = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Get the latest generated voice clone for the user."""
    try:
        user_id = _extract_user_id(current_user)
        
        # Fetch latest voice clone asset
        asset = db.query(ContentAsset).filter(
            ContentAsset.user_id == user_id,
            ContentAsset.asset_type == AssetType.AUDIO,
            ContentAsset.source_module == AssetSource.VOICE_CLONER
        ).order_by(desc(ContentAsset.created_at)).first()
        
        if not asset:
            # Try to find legacy assets or assets that might have been saved differently
            # For example, voice designs might be saved as VOICE_CLONER too?
            # Or check for 'voice_design' logic if needed, but 'voice_cloner' is primary
            return {"success": False, "message": "No voice clone found"}
            
        meta = asset.asset_metadata or {}
        
        return {
            "success": True,
            "custom_voice_id": meta.get("custom_voice_id"),
            "preview_audio_url": meta.get("preview_url") or asset.file_url,
            "asset_id": asset.id,
            "voice_name": meta.get("voice_name"),
            "engine": meta.get("engine")
        }
    except Exception as e:
        logger.error(f"Failed to fetch latest voice clone: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/generate-avatar")
async def generate_avatar(
    request: AvatarPromptRequest,
    current_user: Dict[str, Any] = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Generate a brand avatar using available image providers."""
    try:
        user_id = _extract_user_id(current_user)
        
        logger.info(f"Generating avatar for user {user_id} with prompt: {request.prompt}")
        
        # 1. Generate Image
        result = await generate_image_with_provider(
            prompt=request.prompt,
            aspect_ratio=request.aspect_ratio,
            negative_prompt=request.negative_prompt,
            num_inference_steps=request.num_inference_steps,
            guidance_scale=request.guidance_scale,
            style_preset=request.style_preset,
            model=request.model,
            rendering_speed=request.rendering_speed,
            provider=request.provider,
            user_id=user_id
        )
        
        if not result.get("success"):
            raise HTTPException(status_code=500, detail=result.get("error", "Generation failed"))
            
        # 2. Save to local storage and Asset Library
        # The result typically contains image_base64 or image_url
        # For simplicity, we assume image_base64 is returned or we download the URL
        
        image_data = result.get("image_base64")
        if not image_data and result.get("image_url"):
            try:
                import httpx
                async with httpx.AsyncClient() as client:
                    response = await client.get(result["image_url"], timeout=30.0)
                    response.raise_for_status()
                    image_data = response.content
            except ImportError:
                # Fallback to requests if httpx is not installed
                import requests
                response = requests.get(result["image_url"], timeout=30.0)
                response.raise_for_status()
                image_data = response.content
            except Exception as e:
                logger.error(f"Failed to download image from URL: {e}")
                raise HTTPException(status_code=500, detail=f"Failed to download generated image: {str(e)}")
            
        if image_data:
            # Decode if needed (usually it's already base64 string)
            # Save file
            filename = generate_unique_filename("avatar", "png")
            # If image_data is bytes (from URL download), pass it directly
            # If it's base64 string (from API), decode it
            content_to_save = base64.b64decode(image_data) if isinstance(image_data, str) else image_data
            
            # Construct user assets directory
            user_assets_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "avatars"
            
            saved_path, error = save_file_safely(
                content_to_save,
                user_assets_dir,
                filename
            )
            
            if error or not saved_path:
                raise HTTPException(status_code=500, detail=f"Failed to save image file: {error}")
            
            # Construct public URL
            image_url = f"/api/assets/{user_id}/avatars/{filename}"
            
            # Save to Asset Library
            asset_id = save_asset_to_library(
                db=db,
                user_id=user_id,
                asset_type="image",
                source_module="brand_avatar_generator",
                filename=filename,
                file_url=image_url,
                file_path=str(saved_path),
                prompt=request.prompt,
                asset_metadata={
                    "provider": result.get("provider", "unknown"),
                    "style": request.style_preset,
                    "category": "brand_avatar"
                }
            )
            
            return {
                "success": True,
                "image_url": image_url,
                "image_base64": image_data if isinstance(image_data, str) else base64.b64encode(image_data).decode('utf-8'),
                "asset_id": asset_id
            }
            
        return {"success": False, "error": "No image data returned"}

    except Exception as e:
        logger.error(f"Avatar generation failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/enhance-prompt")
async def enhance_prompt_route(
    request: AvatarEnhanceRequest,
    current_user: Dict[str, Any] = Depends(get_current_user)
):
    """Enhance a simple prompt into a detailed midjourney-style prompt."""
    try:
        user_id = _extract_user_id(current_user)
        logger.info(f"Enhancing prompt for user {user_id}: {request.prompt}")
        
        enhanced_prompt = await enhance_image_prompt(request.prompt, user_id=user_id)
        
        return {
            "success": True,
            "original_prompt": request.prompt,
            "optimized_prompt": enhanced_prompt
        }
    except Exception as e:
        logger.error(f"Prompt enhancement failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/create-variation")
async def create_variation_route(
    prompt: str = Form(...),
    file: UploadFile = File(...),
    user_id: Optional[str] = Form(None), # Ignored in favor of authenticated user
    current_user: Dict[str, Any] = Depends(get_current_user)
):
    """Generate a variation of an existing avatar."""
    try:
        user_id = _extract_user_id(current_user)
        logger.info(f"Creating variation for user {user_id} with prompt: {prompt}")
        
        # Read file
        file_content = await file.read()
        
        result = await generate_image_variation(
            image=file_content,
            prompt=prompt,
            user_id=user_id
        )
        
        if not result.get("success"):
            raise HTTPException(status_code=500, detail=result.get("error", "Variation generation failed"))
            
        # Save result
        image_data = result.get("image_base64")
        if image_data:
            filename = generate_unique_filename("avatar_variation", "png")
            content_to_save = base64.b64decode(image_data)
            
            # Construct user assets directory
            user_assets_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "avatars"
            
            saved_path, error = save_file_safely(
                content_to_save,
                user_assets_dir,
                filename
            )
            
            if error or not saved_path:
                raise HTTPException(status_code=500, detail=f"Failed to save variation file: {error}")
            
            # Construct public URL
            image_url = f"/api/assets/{user_id}/avatars/{filename}"
            
            # Save to Asset Library
            asset_id = save_asset_to_library(
                db=next(get_db()),
                user_id=user_id,
                asset_type="image",
                source_module="brand_avatar_variation",
                filename=filename,
                file_url=image_url,
                file_path=str(saved_path),
                asset_metadata={
                    "prompt": prompt,
                    "provider": "wavespeed",
                    "original_filename": file.filename,
                    "category": "brand_avatar_variation"
                }
            )
            
            return {
                "success": True,
                "image_url": image_url,
                "image_base64": image_data,
                "asset_id": asset_id
            }
            
        return {"success": False, "error": "No image data returned"}
        
    except Exception as e:
        logger.error(f"Variation generation failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/enhance-avatar")
async def enhance_avatar_route(
    file: UploadFile = File(...),
    user_id: Optional[str] = Form(None), # Ignored in favor of authenticated user
    current_user: Dict[str, Any] = Depends(get_current_user)
):
    """Enhance/Upscale an existing avatar."""
    try:
        user_id = _extract_user_id(current_user)
        logger.info(f"Enhancing avatar for user {user_id}")
        
        # Read file
        file_content = await file.read()
        
        result = await generate_image_enhance(
            image=file_content,
            user_id=user_id
        )
        
        if not result.get("success"):
            raise HTTPException(status_code=500, detail=result.get("error", "Enhancement failed"))
            
        # Save result
        image_data = result.get("image_base64")
        if image_data:
            filename = generate_unique_filename("avatar_enhanced", "png")
            content_to_save = base64.b64decode(image_data)
            
            # Construct user assets directory
            user_assets_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "avatars"
            
            saved_path, error = save_file_safely(
                content_to_save,
                user_assets_dir,
                filename
            )
            
            if error or not saved_path:
                raise HTTPException(status_code=500, detail=f"Failed to save enhanced file: {error}")
            
            # Construct public URL
            image_url = f"/api/assets/{user_id}/avatars/{filename}"
            
            # Save to Asset Library
            asset_id = save_asset_to_library(
                db=next(get_db()),
                user_id=user_id,
                asset_type="image",
                source_module="brand_avatar_enhancer",
                filename=filename,
                file_url=image_url,
                file_path=str(saved_path),
                asset_metadata={
                    "provider": "wavespeed",
                    "category": "brand_avatar_enhanced",
                    "original_filename": file.filename
                }
            )
            
            return {
                "success": True,
                "image_url": image_url,
                "image_base64": image_data,
                "asset_id": asset_id
            }
            
        return {"success": False, "error": "No image data returned"}
        
    except Exception as e:
        logger.error(f"Avatar enhancement failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/create-voice-clone")
async def create_voice_clone(
    voice_name: str = Form(...),
    description: str = Form(None),
    engine: str = Form("qwen3"),
    file: UploadFile = File(...),
    user_id: Optional[str] = Form(None), # Ignored in favor of authenticated user
    current_user: Dict[str, Any] = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Create a voice clone from an audio file."""
    try:
        user_id = _extract_user_id(current_user)
        logger.info(f"Creating voice clone '{voice_name}' (engine={engine}) for user {user_id}")
        
        # 1. Save uploaded audio file
        file_content = await file.read()
        filename = generate_unique_filename("voice_sample", Path(file.filename).suffix.lstrip("."))
        
        user_voice_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "voice_samples"
        saved_path, error = save_file_safely(file_content, user_voice_dir, filename)
        
        if error or not saved_path:
             raise HTTPException(status_code=500, detail=f"Failed to save voice sample: {error}")
             
        file_path = str(saved_path)
        
        # 2. Call Voice Cloning API
        preview_audio_bytes = None
        custom_voice_id = None
        
        loop = asyncio.get_event_loop()
        
        # Default preview text
        preview_text = "Hello! This is a preview of my cloned voice using AI technology. I hope you like it!"
        
        if engine.lower() == "minimax":
            # Generate valid voice ID for Minimax (alphanumeric, starts with letter, 8+ chars)
            random_suffix = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
            custom_voice_id = f"vc_{random_suffix}"
            
            logger.info(f"Cloning voice with Minimax, ID: {custom_voice_id}")
            
            # Run blocking call in executor
            result = await loop.run_in_executor(
                None,
                lambda: clone_voice(
                    audio_bytes=file_content,
                    custom_voice_id=custom_voice_id,
                    text=preview_text,
                    user_id=user_id
                )
            )
            preview_audio_bytes = result.preview_audio_bytes
            
        elif engine.lower() == "cosyvoice":
            logger.info("Cloning voice with CosyVoice")
            result = await loop.run_in_executor(
                None,
                lambda: cosyvoice_voice_clone(
                    audio_bytes=file_content,
                    text=preview_text,
                    user_id=user_id
                )
            )
            preview_audio_bytes = result.preview_audio_bytes
            # CosyVoice doesn't persist ID on provider side, but we need one for DB
            asset_uuid = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
            custom_voice_id = f"vc_cosy_{asset_uuid}"
            
        else: # qwen3 (default)
            logger.info("Cloning voice with Qwen3")
            result = await loop.run_in_executor(
                None,
                lambda: qwen3_voice_clone(
                    audio_bytes=file_content,
                    text=preview_text,
                    user_id=user_id
                )
            )
            preview_audio_bytes = result.preview_audio_bytes
            # Qwen3 doesn't persist ID on provider side
            asset_uuid = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
            custom_voice_id = f"vc_qwen_{asset_uuid}"

        # 3. Save Preview Audio (if generated)
        preview_url = None
        if preview_audio_bytes:
            preview_filename = f"preview_{filename}"
            # Ensure it ends with .wav
            if not preview_filename.endswith(".wav"):
                preview_filename = str(Path(preview_filename).with_suffix('.wav'))
            
            user_voice_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "voice_samples"
            saved_preview_path, error = save_file_safely(preview_audio_bytes, user_voice_dir, preview_filename)
            
            if not error and saved_preview_path:
                preview_url = f"/api/assets/{user_id}/voice_samples/{preview_filename}"
            
        # 4. Save to Asset Library
        asset_id = save_asset_to_library(
            db=db,
            user_id=user_id,
            file_path=file_path,
            asset_type="audio",
            source_module="voice_cloner",
            filename=filename,
            file_url=f"/api/assets/{user_id}/voice_samples/{filename}",
            asset_metadata={
                "voice_name": voice_name,
                "engine": engine,
                "description": description,
                "original_filename": file.filename,
                "custom_voice_id": custom_voice_id,
                "preview_url": preview_url,
                "category": "voice_clone"
            }
        )
        
        return {
            "success": True,
            "custom_voice_id": custom_voice_id,
            "preview_audio_url": preview_url or f"/api/assets/{user_id}/voice_samples/{filename}",
            "asset_id": asset_id,
            "message": "Voice clone created successfully"
        }
        
    except Exception as e:
        logger.error(f"Voice cloning failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/create-voice-design")
async def create_voice_design(
    request: VoiceDesignRequest,
    current_user: Dict[str, Any] = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """Create a voice from text description (Voice Design)."""
    try:
        user_id = _extract_user_id(current_user)
        logger.info(f"Designing voice for user {user_id}")
        
        loop = asyncio.get_event_loop()
        
        result = await loop.run_in_executor(
            None,
            lambda: qwen3_voice_design(
                text=request.text,
                voice_description=request.voice_description,
                language=request.language,
                user_id=user_id
            )
        )
        
        # Save the result to a temporary file
        filename = generate_unique_filename("voice_design_preview", "wav")
        user_voice_dir = Path(WORKSPACE_DIR) / f"workspace_{user_id}" / "assets" / "voice_samples"
        saved_path, error = save_file_safely(result.preview_audio_bytes, user_voice_dir, filename)
        
        if error or not saved_path:
             raise HTTPException(status_code=500, detail=f"Failed to save voice design: {error}")
             
        # Generate URL
        preview_url = f"/api/assets/{user_id}/voice_samples/{filename}"
        
        # Save to Asset Library
        asset_id = save_asset_to_library(
            db=db,
            user_id=user_id,
            file_path=str(saved_path),
            asset_type="audio",
            source_module="voice_cloner",
            filename=filename,
            file_url=preview_url,
            asset_metadata={
                "voice_description": request.voice_description,
                "text": request.text,
                "language": request.language,
                "engine": "qwen3-design",
                "category": "voice_design",
                "preview_url": preview_url
            }
        )
        
        return {
            "success": True,
            "preview_audio_url": preview_url,
            "asset_id": asset_id,
            "message": "Voice generated successfully"
        }
        
    except Exception as e:
        logger.error(f"Voice design failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))
