"""Video Upscaler Service - Topaz Labs API"""
import httpx
import os
from uuid import uuid4
from datetime import datetime
import asyncio

from app.database import SessionLocal
from app.models.job import Job
from app.models.asset import Asset
from app.config import settings
import logging

logger = logging.getLogger(__name__)

# Topaz Video AI Models Mapping
VIDEO_MODELS = {
    "Proteus": "prob-4",
    "Artemis High Quality": "ahq-12",
    "Artemis Medium Quality": "amq-13",
    "Artemis Low Quality": "alq-13",
    "Gaia High Quality": "ghq-5",
    "Gaia CG": "gcg-5",
    "Theia Detail": "thd-3",
    "Theia Fidelity": "thf-4",
    "Nyx": "nyx-3",
    "Nyx Fast": "nxf-1",
    "Dione DV": "ddv-3",
    "Dione TV": "dtv-4",
    "Iris": "iris-2", # Updated from iris-1 to valid iris-2
    "Auto": "prob-4" # Fallback/Default
}


async def upscale(job_id: str):
    """Upscale video using Topaz Labs API"""
    db = SessionLocal()
    try:
        job = db.query(Job).filter(Job.id == job_id).first()
        if not job:
            return

        input_data = job.input_data
        input_asset_ids = job.input_asset_ids

        if not input_asset_ids:
            raise ValueError("No input asset provided")

        input_asset = db.query(Asset).filter(Asset.id == input_asset_ids[0]).first()
        if not input_asset:
            raise ValueError("Input asset not found")

        job.progress = 5
        job.api_provider = "topaz"
        job.api_model = input_data.get("model", "auto")
        db.commit()

        scale = input_data.get("scale", 2)
        model = input_data.get("model", "auto")
        frame_interpolation = input_data.get("frame_interpolation", 1)
        
        # New parameters
        fps = input_data.get("fps")
        sharpening = input_data.get("sharpening")
        recover_detail = input_data.get("recover_detail")
        add_noise = input_data.get("add_noise")
        video_type = input_data.get("video_type", "Progressive")
        face_enhancement = input_data.get("face_enhancement", False)

        # Get video metadata with ffprobe
        from app.utils.video import extract_video_metadata
        metadata = extract_video_metadata(input_asset.file_path)

        # Use extracted metadata or fallback to asset record
        duration = metadata.get('duration_seconds') or float(input_asset.duration_seconds or 10)
        source_fps = metadata.get('fps') or 30
        width = metadata.get('width') or input_asset.width or 1920
        height = metadata.get('height') or input_asset.height or 1080

        video_info = {
            "container": "mp4",
            "size": input_asset.file_size_bytes,
            "duration": duration,
            "frameCount": int(duration * source_fps),
            "frameRate": source_fps,
            "resolution": {
                "width": width,
                "height": height
            }
        }

        output_width = video_info["resolution"]["width"] * scale
        output_height = video_info["resolution"]["height"] * scale
        
        video_type = input_data.get("video_type", "progressive")
        face_enhancement = input_data.get("face_enhancement", False)

        # Determine target FPS
        target_fps = fps if fps else (video_info["frameRate"] * frame_interpolation)

        job.progress = 10
        db.commit()

        async with httpx.AsyncClient(timeout=1800) as client:
            # Build filters
            filters = []
            
            # 1. Enhancement filter
            # Logic: If face_enhancement is True, strictly use 'iris-2' (Iris).
            # Otherwise, lookup model in VIDEO_MODELS. If not found, default to 'prob-4' (Proteus).
            
            # Build filters logic matching PHP structure exactly (from estimate.php)
            
            selected_model_code = "prob-4"
            if face_enhancement:
                 selected_model_code = "iris-2"
            else:
                 selected_model_code = VIDEO_MODELS.get(model, "prob-4")

            # PHP used "Progressive" (User's working code)
            video_type_val = video_type.capitalize() if video_type else "Progressive"
            
            enhance_filter = {
                "model": selected_model_code,
                "videoType": video_type_val,
                "auto": "Auto",
                "fieldOrder": "Auto",     # Added from estimate.php
                "focusFixLevel": "None",  # Added from estimate.php
                "blur": 0.0,              # Default from estimate.php
                "grain": 0.0,             # Default from estimate.php
                "grainSize": 1.5,         # Default from estimate.php
                "recoverOriginalDetailValue": 0.2 # Default from estimate.php
            }

            # Override defaults with inputs if present
            if sharpening is not None:
                # Note: estimate.php doesn't map 'sharpening' to 'details', it uses 'recoverOriginalDetailValue'
                # But typically 'details' is the param. Let's stick to valid defaults from PHP first.
                # Actually estimate.php uses $_POST['recoverDetail'] -> recoverOriginalDetailValue.
                pass 
            
            if recover_detail is not None:
                enhance_filter["recoverOriginalDetailValue"] = int(recover_detail) / 100.0 if int(recover_detail) > 1 else int(recover_detail)
            
            # Map other UI sliders if we want, but let's stick to working PHP defaults + Model for now to FIX it.
            
            filters.append(enhance_filter)

            # Create video enhancement request - match PHP keys layout
            payload = {
                    "source": video_info,
                    "filters": filters,
                    "output": {
                        "resolution": {
                            "width": output_width,
                            "height": output_height
                        },
                        "frameRate": target_fps,
                        "audioCodec": "AAC",
                        "audioTransfer": "Copy",
                        
                        # Added missing fields from estimate.php
                        "videoEncoder": "H265",
                        "videoBitrate": "6000k",
                        "videoProfile": "Main",
                        "cropToFit": False,
                        "container": "mp4"
                    }
                }
            print(f"DEBUG: Topaz Video Payload: {payload}")
            
            # Revert to root /video endpoint as /v1 returned 404
            response = await client.post(
                "https://api.topazlabs.com/video/",
                headers={
                    "X-API-Key": settings.topaz_api_key,
                    "Content-Type": "application/json",
                    "Accept": "application/json"
                },
                json=payload
            )
            if response.status_code >= 400:
                logger.error(f"Topaz Video API Error: {response.text}")
            response.raise_for_status()
            result = response.json()
            logger.info(f"Topaz Video Creation Response: {result}")

            request_id = result.get("requestId")
            if not request_id:
                raise ValueError(f"No requestId returned from Topaz: {result}")

            job.progress = 15
            job.api_request_id = request_id
            db.commit()

            # Accept the request and get upload URLs
            accept_response = await client.patch(
                f"https://api.topazlabs.com/video/{request_id}/accept",
                headers={"X-API-Key": settings.topaz_api_key}
            )
            logger.info(f"Topaz Video Accept Response: {accept_response.text}")
            accept_response.raise_for_status()
            accept_data = accept_response.json()
            upload_urls = accept_data.get("urls", [])

            job.progress = 20
            db.commit()

            # Upload video file in parts
            with open(input_asset.file_path, "rb") as f:
                video_data = f.read()

            part_size = len(video_data) // len(upload_urls) if upload_urls else len(video_data)
            upload_results = []

            for i, url in enumerate(upload_urls):
                start = i * part_size
                end = start + part_size if i < len(upload_urls) - 1 else len(video_data)
                part_data = video_data[start:end]

                upload_response = await client.put(
                    url,
                    content=part_data,
                    headers={"Content-Type": "application/octet-stream"}
                )

                etag = upload_response.headers.get("ETag", "").strip('"')
                upload_results.append({
                    "partNum": i + 1,
                    "eTag": etag
                })

                job.progress = 20 + (i + 1) * (30 / len(upload_urls))
                db.commit()

            # Complete the upload
            complete_response = await client.patch(
                f"https://api.topazlabs.com/video/{request_id}/complete-upload",
                headers={
                    "X-API-Key": settings.topaz_api_key,
                    "Content-Type": "application/json"
                },
                json={"uploadResults": upload_results}
            )
            logger.info(f"Topaz Video Complete Upload Response: {complete_response.text}")
            complete_response.raise_for_status()

            job.progress = 50
            db.commit()

            # Poll for completion
            output_asset = None
            # Poll for completion
            output_asset = None
            for i in range(900):  # Wait up to 30 minutes (2s * 900)
                await asyncio.sleep(2)

                try:
                    # Generic logging for debugging
                    if i % 10 == 0:
                        logger.info(f"Polling Topaz Video Job {request_id} (Attempt {i})")

                    # Trying generic resource URL first (some APIs use GET /resource/{id} for status)
                    status_url = f"https://api.topazlabs.com/video/{request_id}"
                    
                    status_response = await client.get(
                        status_url,
                        headers={"X-API-Key": settings.topaz_api_key}
                    )
                    
                    if status_response.status_code == 404:
                         # Fallback to /status if generic 404s (just in case)
                         # But user reported /status 404s.
                         logger.warning(f"Topaz Status Check 404 at {status_url}. Job might be lost or URL wrong.")
                         # Don't break immediately, maybe ephemeral?
                         pass
                    elif status_response.status_code != 200:
                         logger.warning(f"Topaz Status Check returned {status_response.status_code}: {status_response.text}")
                         continue

                    status_data = status_response.json()
                    status = status_data.get("status", "").lower()
                    
                    if i % 10 == 0:
                        logger.info(f"Topaz Video Status: {status} Data: {status_data}")

                    if status == "completed" or status_data.get("outputUrl") or status_data.get("url"):
                        output_url = status_data.get("outputUrl") or status_data.get("url")
                        if output_url:
                            logger.info(f"Topaz Video API Success. Output URL: {output_url}")
                            video_response = await client.get(output_url)
                            upscaled_data = video_response.content

                            # Save output
                            base_name = os.path.splitext(input_asset.original_filename)[0]
                            clean_base_name = base_name.replace(" ", "_")
                            clean_model = model.replace(" ", "_")
                            filename = f"{clean_base_name}_{scale}X_{clean_model}.mp4"
                            
                            storage_path = os.path.join(settings.storage_path, "videos")
                            os.makedirs(storage_path, exist_ok=True)
                            file_path = os.path.join(storage_path, filename)

                            with open(file_path, "wb") as f:
                                f.write(upscaled_data)

                            # Generate thumbnail
                            thumbnail_path = None
                            try:
                                from app.utils.video import generate_video_thumbnail
                                thumb_filename = f"{os.path.splitext(filename)[0]}_thumb.jpg"
                                thumb_path = os.path.join(storage_path, thumb_filename)
                                if generate_video_thumbnail(file_path, thumb_path, timestamp=1.0):
                                    thumbnail_path = thumb_path
                                    logger.info(f"Generated thumbnail for upscaled video: {thumb_path}")
                            except Exception as e:
                                logger.warning(f"Failed to generate thumbnail: {e}")

                            # Create output asset
                            output_asset = Asset(
                                user_id=job.user_id,
                                project_id=job.project_id,
                                original_filename=filename,
                                stored_filename=filename,
                                file_path=file_path,
                                thumbnail_path=thumbnail_path,
                                file_type="video",
                                mime_type="video/mp4",
                                file_size_bytes=len(upscaled_data),
                                width=output_width,
                                height=output_height,
                                duration_seconds=input_asset.duration_seconds,
                                source_module="video_upscaler",
                                source_job_id=job.id,
                                parent_asset_id=input_asset.id,
                                asset_metadata={
                                    "scale": scale,
                                    "model": model,
                                    "frame_interpolation": frame_interpolation
                                }
                            )
                            db.add(output_asset)
                            db.commit()
                            db.refresh(output_asset)

                            job.output_asset_ids = [output_asset.id]
                            job.output_data = {"asset_id": str(output_asset.id), "file_path": file_path}
                            break
                    elif status == "failed":
                        raise ValueError(f"Video enhancement failed: {status_data.get('error')}")
                except Exception as e:
                     logger.warning(f"Error checking status for job {job.id}: {e}")
                     # Continue polling

                job.progress = min(50 + (i * 0.05), 95) # Slower progress for longer wait
                db.commit()

            if not output_asset:
                raise TimeoutError("Video upscaling timed out or failed to return output")

        job.progress = 100
        job.status = "completed"
        job.completed_at = datetime.utcnow()
        db.commit()

    except Exception as e:
        job.status = "failed"
        job.error_message = str(e)
        db.commit()
    finally:
        db.close()
