from fastapi import APIRouter, Depends, HTTPException, Form, UploadFile, File
from typing import Optional
from pydantic import BaseModel
import db
import schemas
import auth_utils
import config
import os
import uuid
import shutil
import zipfile
import json
from services.global_manager import global_manager

from dependencies import require_admin
from services.audit_service import audit_service

# Test deployment trigger - comment update
router = APIRouter(tags=["portfolio"])

class ScanUpdate(BaseModel):
    is_public: bool

@router.get("/portfolio")
async def get_public_portfolio():
    query = """
    SELECT p.id, p.file_path, COALESCE(o.material_name, 'Showcase') as material_name, p.order_id, p.caption
    FROM order_photos p
    LEFT JOIN orders o ON p.order_id = o.id
    WHERE p.is_public = TRUE AND (o.id IS NULL OR o.allow_portfolio = TRUE)
    ORDER BY p.created_at DESC
    """
    return db.execute_query(query)

@router.get("/admin/all-photos")
async def admin_get_all_photos(admin: dict = Depends(require_admin)):
    query = """
    SELECT p.id, p.file_path, p.is_public, p.order_id, p.caption, o.allow_portfolio, 
           o.first_name, o.last_name, COALESCE(o.material_name, 'Manual') as material_name
    FROM order_photos p
    LEFT JOIN orders o ON p.order_id = o.id
    ORDER BY p.created_at DESC
    """
    return db.execute_query(query)

@router.post("/admin/orders/{order_id}/photos")
async def admin_upload_order_photo(
    order_id: int, 
    is_public: bool = Form(False),
    caption: Optional[str] = Form(None),
    file: UploadFile = File(...), 
    admin: dict = Depends(require_admin)
):
    order = db.execute_query("SELECT allow_portfolio FROM orders WHERE id = %s", (order_id,))
    if not order: raise HTTPException(status_code=404, detail="Order not found")
    if is_public and not order[0]['allow_portfolio']:
         raise HTTPException(status_code=400, detail="Cannot make public: User did not consent to portfolio usage")
    if not file.filename: raise HTTPException(status_code=400, detail="Invalid file")
    unique_filename = f"{uuid.uuid4()}{os.path.splitext(file.filename)[1]}"
    disk_path = os.path.join(config.UPLOAD_DIR, unique_filename)
    db_file_path = f"uploads/{unique_filename}"
    
    with open(disk_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
        
    query = "INSERT INTO order_photos (order_id, file_path, is_public, caption) VALUES (%s, %s, %s, %s)"
    photo_id = db.execute_commit(query, (order_id, db_file_path, is_public, caption))
    
    await audit_service.log(
        user_id=admin['id'],
        action="upload_order_photo",
        target_type="order",
        target_id=order_id,
        details={"photo_id": photo_id, "is_public": is_public, "caption": caption}
    )

    # NOTIFY USER VIA WEBSOCKET
    order_info = db.execute_query("SELECT user_id FROM orders WHERE id = %s", (order_id,))
    if order_info:
        await global_manager.notify_order_update(order_info[0]['user_id'], order_id)

    return {"id": photo_id, "file_path": db_file_path, "is_public": is_public, "caption": caption}

@router.post("/admin/portfolio/upload")
async def admin_upload_portfolio_photo(
    is_public: bool = Form(True),
    caption: Optional[str] = Form(None),
    file: UploadFile = File(...),
    admin: dict = Depends(require_admin)
):
    if not file.filename: raise HTTPException(status_code=400, detail="Invalid file")
    unique_filename = f"{uuid.uuid4()}{os.path.splitext(file.filename)[1]}"
    disk_path = os.path.join(config.UPLOAD_DIR, unique_filename)
    db_file_path = f"uploads/{unique_filename}"
    
    with open(disk_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
        
    query = "INSERT INTO order_photos (order_id, file_path, is_public, caption) VALUES (%s, %s, %s, %s)"
    photo_id = db.execute_commit(query, (None, db_file_path, is_public, caption))
    
    await audit_service.log(
        user_id=admin['id'],
        action="upload_portfolio_photo",
        target_type="photo",
        target_id=photo_id,
        details={"is_public": is_public, "caption": caption}
    )

    return {"id": photo_id, "file_path": db_file_path, "is_public": is_public, "caption": caption}

@router.patch("/admin/photos/{photo_id}")
async def admin_update_photo_status(photo_id: int, data: schemas.PhotoUpdate, admin: dict = Depends(require_admin)):
    query = "SELECT p.*, o.allow_portfolio FROM order_photos p LEFT JOIN orders o ON p.order_id = o.id WHERE p.id = %s"
    photo_data = db.execute_query(query, (photo_id,))
    if not photo_data: raise HTTPException(status_code=404, detail="Photo not found")
    
    # Check consent if linked to order
    if data.is_public and photo_data[0]['order_id'] is not None and not photo_data[0]['allow_portfolio']:
        raise HTTPException(status_code=400, detail="Cannot make public: User did not consent to portfolio usage")
    
    db.execute_commit("UPDATE order_photos SET is_public = %s, caption = %s WHERE id = %s", (data.is_public, data.caption, photo_id))
    
    await audit_service.log(
        user_id=admin['id'],
        action="update_photo_visibility",
        target_type="photo",
        target_id=photo_id,
        details={"is_public": data.is_public, "caption": data.caption}
    )

    # NOTIFY USER VIA WEBSOCKET if linked to order
    order_id = photo_data[0]['order_id']
    if order_id:
        order_info = db.execute_query("SELECT user_id FROM orders WHERE id = %s", (order_id,))
        if order_info:
            await global_manager.notify_order_update(order_info[0]['user_id'], order_id)

    return {"id": photo_id, "is_public": data.is_public, "caption": data.caption}

@router.delete("/admin/photos/{photo_id}")
async def admin_delete_photo(photo_id: int, admin: dict = Depends(require_admin)):
    photo = db.execute_query("SELECT file_path, order_id FROM order_photos WHERE id = %s", (photo_id,))
    if not photo:
        raise HTTPException(status_code=404, detail="Photo not found")
    
    order_id = photo[0]['order_id']
        
    try:
        path = os.path.join(config.BASE_DIR, photo[0]['file_path'])
        if os.path.exists(path):
            os.remove(path)
    except Exception as e:
        print(f"Error deleting photo file: {e}")
        
    db.execute_commit("DELETE FROM order_photos WHERE id = %s", (photo_id,))

    await audit_service.log(
        user_id=admin['id'],
        action="delete_photo",
        target_type="photo",
        target_id=photo_id,
        details={"order_id": order_id}
    )

    # NOTIFY USER VIA WEBSOCKET
    order_info = db.execute_query("SELECT user_id FROM orders WHERE id = %s", (order_id,))
    if order_info:
        await global_manager.notify_order_update(order_info[0]['user_id'], order_id)

    return {"id": photo_id, "status": "deleted"}


# --- 3D Scan Portfolio Endpoints ---

@router.get("/scans")
async def get_public_scans(page: int = 1, size: int = 10):
    offset = (page - 1) * size
    
    # Get total count
    count_res = db.execute_query("SELECT COUNT(*) as total FROM portfolio_scans WHERE is_public = TRUE")
    total = count_res[0]['total'] if count_res else 0
    
    query = """
    SELECT id, title_en, title_ru, title_me, title_ua,
           description_en, description_ru, description_me, description_ua,
           folder_name, frames_count, created_at
    FROM portfolio_scans
    WHERE is_public = TRUE
    ORDER BY created_at DESC
    LIMIT %s OFFSET %s
    """
    scans = db.execute_query(query, (size, offset))
    scans_dir = os.path.join(config.UPLOAD_DIR, "scans")
    for scan in scans:
        photo_path = os.path.join(scans_dir, scan["folder_name"], "photo.webp")
        scan["has_photo"] = os.path.exists(photo_path)
    return {"scans": scans, "total": total}

@router.get("/admin/scans")
async def admin_get_all_scans(page: int = 1, size: int = 10, admin: dict = Depends(require_admin)):
    offset = (page - 1) * size
    
    # Get total count
    count_res = db.execute_query("SELECT COUNT(*) as total FROM portfolio_scans")
    total = count_res[0]['total'] if count_res else 0
    
    query = """
    SELECT id, title_en, title_ru, title_me, title_ua,
           description_en, description_ru, description_me, description_ua,
           folder_name, frames_count, is_public, created_at
    FROM portfolio_scans
    ORDER BY created_at DESC
    LIMIT %s OFFSET %s
    """
    scans = db.execute_query(query, (size, offset))
    scans_dir = os.path.join(config.UPLOAD_DIR, "scans")
    for scan in scans:
        photo_path = os.path.join(scans_dir, scan["folder_name"], "photo.webp")
        scan["has_photo"] = os.path.exists(photo_path)
    return {"scans": scans, "total": total}

@router.post("/admin/scans/upload-bundle")
async def admin_upload_scan_bundle(
    file: UploadFile = File(...),
    admin: dict = Depends(require_admin)
):
    if not file.filename or not file.filename.endswith(".zip"):
        raise HTTPException(status_code=400, detail="Invalid file: must be a ZIP archive")
        
    scan_id = str(uuid.uuid4())
    scans_dir = os.path.join(config.UPLOAD_DIR, "scans")
    os.makedirs(scans_dir, exist_ok=True)
    dest_dir = os.path.join(scans_dir, f"scan_{scan_id}")
    
    os.makedirs(dest_dir, exist_ok=True)
    
    temp_zip_path = os.path.join(dest_dir, "temp_bundle.zip")
    with open(temp_zip_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
        
    try:
        # Extract files
        with zipfile.ZipFile(temp_zip_path, 'r') as zip_ref:
            zip_ref.extractall(dest_dir)
            
        # Clean up zip
        if os.path.exists(temp_zip_path):
            os.remove(temp_zip_path)
            
        # Validate metadata.json
        metadata_path = os.path.join(dest_dir, "metadata.json")
        if not os.path.exists(metadata_path):
            raise HTTPException(status_code=400, detail="Invalid bundle: metadata.json is missing")
            
        with open(metadata_path, "r", encoding="utf-8") as f:
            metadata = json.load(f)
            
        title_en = metadata.get("title_en")
        if not title_en:
            raise HTTPException(status_code=400, detail="Invalid bundle: title_en is required")
            
        title_ru = metadata.get("title_ru", "") or title_en
        title_me = metadata.get("title_me", "") or title_en
        title_ua = metadata.get("title_ua", "") or title_en
        
        description_en = metadata.get("description_en", "")
        description_ru = metadata.get("description_ru", "") or description_en
        description_me = metadata.get("description_me", "") or description_en
        description_ua = metadata.get("description_ua", "") or description_en
        
        frames_count = int(metadata.get("frames_count", 48))
        
        # Verify frame WebP files
        for i in range(frames_count):
            frame_path = os.path.join(dest_dir, f"{i:03d}.webp")
            if not os.path.exists(frame_path):
                raise HTTPException(status_code=400, detail=f"Invalid bundle: frame {i:03d}.webp is missing")
                
        # Verify thumbnail
        thumbnail_path = os.path.join(dest_dir, "thumbnail.webp")
        if not os.path.exists(thumbnail_path):
            raise HTTPException(status_code=400, detail="Invalid bundle: thumbnail.webp is missing")
            
        # Check and convert original photograph if present
        for ext in [".webp", ".png", ".jpg", ".jpeg"]:
            p_path = os.path.join(dest_dir, f"photo{ext}")
            if os.path.exists(p_path):
                if ext != ".webp":
                    from PIL import Image
                    try:
                        with Image.open(p_path) as img:
                            img.save(os.path.join(dest_dir, "photo.webp"), "WEBP", quality=85)
                        os.remove(p_path)
                    except Exception as ex:
                        print(f"Failed to convert photo to WebP: {ex}")
                break
            
        # Insert DB record
        query = """
        INSERT INTO portfolio_scans (
            title_en, title_ru, title_me, title_ua,
            description_en, description_ru, description_me, description_ua,
            folder_name, frames_count, is_public
        ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
        db_id = db.execute_commit(query, (
            title_en, title_ru, title_me, title_ua,
            description_en, description_ru, description_me, description_ua,
            f"scan_{scan_id}", frames_count, 1
        ))
        
        await audit_service.log(
            user_id=admin['id'],
            action="upload_scan_bundle",
            target_type="scan",
            target_id=db_id,
            details={"title_en": title_en, "folder_name": f"scan_{scan_id}"}
        )
        
        return {
            "id": db_id,
            "title_en": title_en,
            "folder_name": f"scan_{scan_id}",
            "frames_count": frames_count
        }
        
    except Exception as e:
        if os.path.exists(dest_dir):
            shutil.rmtree(dest_dir)
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail=f"Error processing bundle: {str(e)}")

@router.patch("/admin/scans/{scan_id}")
async def admin_update_scan_status(
    scan_id: int, 
    data: ScanUpdate, 
    admin: dict = Depends(require_admin)
):
    scan = db.execute_query("SELECT id FROM portfolio_scans WHERE id = %s", (scan_id,))
    if not scan:
        raise HTTPException(status_code=404, detail="Scan not found")
        
    db.execute_commit("UPDATE portfolio_scans SET is_public = %s WHERE id = %s", (data.is_public, scan_id))
    
    await audit_service.log(
        user_id=admin['id'],
        action="update_scan_visibility",
        target_type="scan",
        target_id=scan_id,
        details={"is_public": data.is_public}
    )
    
    return {"id": scan_id, "is_public": data.is_public}

@router.delete("/admin/scans/{scan_id}")
async def admin_delete_scan(scan_id: int, admin: dict = Depends(require_admin)):
    scan = db.execute_query("SELECT folder_name, title_en FROM portfolio_scans WHERE id = %s", (scan_id,))
    if not scan:
        raise HTTPException(status_code=404, detail="Scan not found")
        
    folder_name = scan[0]['folder_name']
    title_en = scan[0]['title_en']
    
    dest_dir = os.path.join(config.UPLOAD_DIR, "scans", folder_name)
    try:
        if os.path.exists(dest_dir):
            shutil.rmtree(dest_dir)
    except Exception as e:
        print(f"Error removing scan directory {folder_name}: {e}")
        
    db.execute_commit("DELETE FROM portfolio_scans WHERE id = %s", (scan_id,))
    
    await audit_service.log(
        user_id=admin['id'],
        action="delete_scan_bundle",
        target_type="scan",
        target_id=scan_id,
        details={"title_en": title_en, "folder_name": folder_name}
    )
    
    return {"id": scan_id, "status": "deleted"}
