import os

import sys

from pathlib import Path

from typing import Optional, List, Dict

import json

from datetime import datetime



# Add studio directory to path for absolute imports

studio_dir = Path(__file__).parent

sys.path.insert(0, str(studio_dir))

# Add shared library (after studio to avoid import conflicts)

if '/home/coolhand/shared' not in sys.path:

    sys.path.append('/home/coolhand/shared')



import requests

from dotenv import load_dotenv

from flask import Flask, Blueprint, render_template, request, jsonify, session, redirect, url_for, flash

from werkzeug.middleware.proxy_fix import ProxyFix

import tempfile

from cache_manager import CacheManager



# Import local modules first to ensure they're in import cache

import config  # Force loading of local config before blueprints



# Import shared library utilities after local config

from web.health import create_health_endpoint



# Import providers

from providers import (

    AnthropicProvider,

    OpenAIProvider,

    CohereProvider,

    MistralProvider,

    PerplexityProvider,

    XAIProvider,

    GeminiProvider,

    HuggingFaceProvider,

    GroqProvider,

    ManusProvider,

    ElevenLabsProvider

)



# Import Swarm Portal blueprints

from blueprints.portal import portal_bp

from blueprints.search import search_bp

from blueprints.chat import chat_bp

from blueprints.hive import hive_bp



# Load from master .env file first, then local overrides

load_dotenv('/home/coolhand/.env')

load_dotenv()  # Load local .env for any overrides



# Load configuration (uses environment variables + defaults)

from config import get_config

studio_config = get_config()



# Base path from config (uses environment variable BASE_PATH or default)

# IMPORTANT: Must be defined BEFORE Flask app creation for SESSION_COOKIE_PATH

BASE_PATH = os.getenv("BASE_PATH", studio_config.base_path)



app = Flask(__name__)

app.secret_key = os.getenv("FLASK_SECRET_KEY", "change-me")

# Session cookie path must match BASE_PATH for authentication to work

app.config['SESSION_COOKIE_PATH'] = BASE_PATH



# Configure ProxyFix for Caddy reverse proxy

# This properly handles X-Forwarded-* headers from Caddy

app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)



# Password protection - now uses environment variable STUDIO_PASSWORD or config default

PASSWORD = os.getenv("STUDIO_PASSWORD", studio_config.password)



# Initialize cache manager

cache = CacheManager(use_redis=True)



# Usage tracking

USAGE_STATS = {

    "total_requests": 0,

    "requests_by_provider": {},

    "requests_by_model": {},

    "total_tokens": 0,

    "cache_savings": 0

}



# Security constants

MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB limit for image uploads

ALLOWED_IMAGE_MIMES = {'image/jpeg', 'image/png', 'image/gif', 'image/webp'}

ALLOWED_IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp'}



# Audio security constants

MAX_AUDIO_SIZE = 25 * 1024 * 1024  # 25MB limit for audio (Whisper API limit)

ALLOWED_AUDIO_MIMES = {

    'audio/mpeg',      # mp3

    'audio/mp4',       # m4a

    'audio/wav',       # wav

    'audio/x-wav',     # wav alternate

    'audio/webm',      # webm

    'audio/ogg',       # ogg

    'video/mp4',       # mp4 (audio track)

}

ALLOWED_AUDIO_EXTENSIONS = {'.mp3', '.mp4', '.mpeg', '.mpga', '.m4a', '.wav', '.webm', '.ogg'}



# Create Blueprint with URL prefix for proper routing

studio_bp = Blueprint('studio', __name__,

                      url_prefix=BASE_PATH,

                      static_folder='static',

                      static_url_path='/static',

                      template_folder='templates')





# Data storage files

PROMPTS_FILE = "prompts.json"

IMAGES_FILE = "saved_images.json"



# Initialize providers

PROVIDERS = {}



# Try to initialize each provider

try:

    if os.getenv("ANTHROPIC_API_KEY"):

        PROVIDERS['anthropic'] = AnthropicProvider(os.getenv("ANTHROPIC_API_KEY"))

        print("✓ Anthropic provider initialized")

except Exception as e:

    print(f"✗ Anthropic provider failed: {e}")



try:

    if os.getenv("OPENAI_API_KEY"):

        PROVIDERS['openai'] = OpenAIProvider(os.getenv("OPENAI_API_KEY"))

        print("✓ OpenAI provider initialized")

except Exception as e:

    print(f"✗ OpenAI provider failed: {e}")



try:

    if os.getenv("COHERE_API_KEY"):

        PROVIDERS['cohere'] = CohereProvider(os.getenv("COHERE_API_KEY"))

        print("✓ Cohere provider initialized")

except Exception as e:

    print(f"✗ Cohere provider failed: {e}")



try:

    if os.getenv("MISTRAL_API_KEY"):

        PROVIDERS['mistral'] = MistralProvider(os.getenv("MISTRAL_API_KEY"))

        print("✓ Mistral provider initialized")

except Exception as e:

    print(f"✗ Mistral provider failed: {e}")



try:

    if os.getenv("PERPLEXITY_API_KEY"):

        PROVIDERS['perplexity'] = PerplexityProvider(os.getenv("PERPLEXITY_API_KEY"))

        print("✓ Perplexity provider initialized")

except Exception as e:

    print(f"✗ Perplexity provider failed: {e}")



try:

    if os.getenv("XAI_API_KEY"):

        PROVIDERS['xai'] = XAIProvider(os.getenv("XAI_API_KEY"))

        print("✓ xAI provider initialized")

except Exception as e:

    print(f"✗ xAI provider failed: {e}")



try:

    if os.getenv("GEMINI_API_KEY"):

        PROVIDERS['gemini'] = GeminiProvider(os.getenv("GEMINI_API_KEY"))

        print("✓ Gemini provider initialized")

except Exception as e:

    print(f"✗ Gemini provider failed: {e}")



try:

    if os.getenv("HUGGINGFACE_API_KEY") or os.getenv("HF_API_KEY"):

        PROVIDERS['huggingface'] = HuggingFaceProvider()

        print("✓ Hugging Face provider initialized")

except Exception as e:

    print(f"✗ Hugging Face provider failed: {e}")



try:

    if os.getenv("GROQ_API_KEY"):

        PROVIDERS['groq'] = GroqProvider(os.getenv("GROQ_API_KEY"))

        print("✓ Groq provider initialized")

except Exception as e:

    print(f"✗ Groq provider failed: {e}")



try:

    if os.getenv("MANUS_API_KEY"):

        PROVIDERS['manus'] = ManusProvider(os.getenv("MANUS_API_KEY"))

        print("✓ Manus provider initialized")

except Exception as e:

    print(f"✗ Manus provider failed: {e}")



try:

    if os.getenv("ELEVENLABS_API_KEY"):

        PROVIDERS['elevenlabs'] = ElevenLabsProvider(os.getenv("ELEVENLABS_API_KEY"))

        print("✓ ElevenLabs provider initialized")

except Exception as e:

    print(f"✗ ElevenLabs provider failed: {e}")





def load_data(filename: str) -> Dict:

    """Load data from JSON file, create if doesn't exist"""

    try:

        with open(filename, 'r') as f:

            return json.load(f)

    except FileNotFoundError:

        return {"image_prompts": [], "chat_prompts": []} if filename == PROMPTS_FILE else {"images": []}





def save_data(filename: str, data: Dict):

    """Save data to JSON file"""

    with open(filename, 'w') as f:

        json.dump(data, f, indent=2)





@studio_bp.route("/login", methods=["GET", "POST"])

def login():

    if request.method == "POST":

        password = request.form.get("password", "")

        if password == PASSWORD:

            session['authenticated'] = True

            return redirect(url_for('studio.index'))

        else:

            flash("Invalid password", "error")



    return render_template("login.html")





@studio_bp.route("/logout")

def logout():

    session.pop('authenticated', None)

    return redirect(url_for('studio.login'))





@studio_bp.route("/")

def index():

    if not session.get('authenticated'):

        return redirect(url_for('studio.login'))



    prompts_data = load_data(PROMPTS_FILE)



    # Get available providers and their models

    provider_info = {}

    for provider_name, provider in PROVIDERS.items():

        try:

            models = provider.list_models()

            provider_info[provider_name] = {

                "available": True,

                "models": models

            }

        except Exception as e:

            provider_info[provider_name] = {

                "available": False,

                "error": str(e)

            }



    api_endpoints = {

        "chat": url_for('studio.chat_endpoint'),

        "generate_image": url_for('studio.generate_image_endpoint'),

        "generate_video": url_for('studio.generate_video_endpoint'),

        "save_prompt": url_for('studio.save_prompt'),

        "clear_chat": url_for('studio.clear_chat'),

        "analyze_image": url_for('studio.analyze_image_endpoint'),

    }



    return render_template(

        "index.html",

        provider_info=provider_info,

        image_prompts=prompts_data["image_prompts"],

        chat_prompts=prompts_data["chat_prompts"],

        api_endpoints=api_endpoints,

        logout_url=url_for('studio.logout'),

    )





@studio_bp.route("/chat", methods=["POST"])

def chat_endpoint():

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    message = data.get("message", "").strip()

    provider_name = data.get("provider", "xai")

    model = data.get("model")

    conversation_id = data.get("conversation_id", "default")



    if not message:

        return jsonify({"error": "Message is required"}), 400



    if provider_name not in PROVIDERS:

        return jsonify({"error": f"Provider {provider_name} not available"}), 400



    provider = PROVIDERS[provider_name]



    # Check cache first

    messages_for_cache = [{"role": "user", "content": message}]

    cached_response = cache.get(provider_name, model or "default", messages_for_cache)



    if cached_response:

        # Update stats

        USAGE_STATS["cache_savings"] += 1

        return jsonify({

            "response": cached_response["response"],

            "cached": True,

            "cache_stats": cache.get_stats()

        })



    try:

        # Make API call

        response = provider.chat(message, model=model)



        # Cache the response (1 hour TTL)

        cache.set(provider_name, model or "default", messages_for_cache, {"response": response}, ttl=3600)



        # Update usage stats

        USAGE_STATS["total_requests"] += 1

        USAGE_STATS["requests_by_provider"][provider_name] = USAGE_STATS["requests_by_provider"].get(provider_name, 0) + 1

        if model:

            USAGE_STATS["requests_by_model"][model] = USAGE_STATS["requests_by_model"].get(model, 0) + 1



        return jsonify({

            "response": response,

            "cached": False

        })

    except Exception as exc:

        return jsonify({"error": str(exc)}), 500





@studio_bp.route("/generate-image", methods=["POST"])

def generate_image_endpoint():

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    prompt = data.get("prompt", "").strip()

    provider_name = data.get("provider", "xai")



    if not prompt:

        return jsonify({"error": "Prompt is required"}), 400



    if provider_name not in PROVIDERS:

        return jsonify({"error": f"Provider {provider_name} not available"}), 400



    provider = PROVIDERS[provider_name]



    # Check if provider supports image generation

    if not hasattr(provider, 'generate_image'):

        return jsonify({"error": f"Provider {provider_name} does not support image generation"}), 400



    try:

        result = provider.generate_image(prompt)



        # result is an ImageResponse object

        image_data = result.image_data



        # Save image to file

        images_data = load_data(IMAGES_FILE)

        image_entry = {

            "id": len(images_data["images"]) + 1,

            "prompt": prompt,

            "provider": provider_name,

            "model": result.model,

            "image_data": image_data,

            "timestamp": datetime.now().isoformat()

        }

        images_data["images"].append(image_entry)

        save_data(IMAGES_FILE, images_data)



        return jsonify({

            "image": image_data,

            "saved_id": image_entry["id"],

            "model": result.model,

            "revised_prompt": result.revised_prompt

        })



    except NotImplementedError as exc:

        return jsonify({"error": f"Provider {provider_name} does not support image generation"}), 400

    except Exception as exc:

        return jsonify({"error": str(exc)}), 500





@studio_bp.route("/generate-video", methods=["POST"])

def generate_video_endpoint():

    """Generate video using AI providers (xAI, OpenAI when available)"""

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    prompt = data.get("prompt", "").strip()

    provider_name = data.get("provider", "xai")

    image_prompt = data.get("image_prompt")  # Optional: generate image first



    if not prompt:

        return jsonify({"error": "Prompt is required"}), 400



    if provider_name not in PROVIDERS:

        return jsonify({"error": f"Provider {provider_name} not available"}), 400



    provider = PROVIDERS[provider_name]



    # Check if provider supports video generation

    if not hasattr(provider, 'generate_video'):

        return jsonify({"error": f"Provider {provider_name} does not support video generation"}), 400



    try:

        # If image_prompt is provided, generate image first

        image_path = None

        if image_prompt and hasattr(provider, 'generate_image'):

            image_result = provider.generate_image(image_prompt)

            if image_result.get("success"):

                # Save image temporarily for video generation

                import tempfile

                temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")

                import base64

                image_data = base64.b64decode(image_result["image_data"])

                temp_file.write(image_data)

                temp_file.close()

                image_path = temp_file.name



        # Generate video

        result = provider.generate_video(prompt, image_path=image_path)



        # Clean up temp image if created

        if image_path:

            try:

                os.remove(image_path)

            except:

                pass



        if result.get("success"):

            return jsonify({

                "video_url": result.get("video_url"),

                "video_data": result.get("video_data"),

                "message": "Video generated successfully"

            })

        else:

            return jsonify({"error": result.get("error", "Unknown error")}), 500



    except Exception as exc:

        return jsonify({"error": str(exc)}), 500





@studio_bp.route("/transcribe", methods=["POST"])

def transcribe_audio_endpoint():

    """

    Transcribe audio to text using OpenAI Whisper API.



    Request: multipart/form-data

        - audio_file: Audio file to transcribe

        - language: Optional ISO-639-1 code (e.g., 'en', 'es')

        - model: Optional model override (default: whisper-1)



    Returns:

        JSON: {

            "text": str,

            "language": str,

            "duration": float,

            "model": str

        }

    """

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    # Check if OpenAI provider is available

    if 'openai' not in PROVIDERS:

        return jsonify({"error": "OpenAI provider not available"}), 503



    # Validate file upload

    if 'audio_file' not in request.files:

        return jsonify({"error": "No audio file provided"}), 400



    audio_file = request.files['audio_file']



    if audio_file.filename == '':

        return jsonify({"error": "No file selected"}), 400



    # Validate file extension

    file_ext = Path(audio_file.filename).suffix.lower()

    if file_ext not in ALLOWED_AUDIO_EXTENSIONS:

        return jsonify({

            "error": f"Invalid file type. Allowed: {', '.join(ALLOWED_AUDIO_EXTENSIONS)}"

        }), 400



    # Validate MIME type

    if audio_file.content_type and audio_file.content_type not in ALLOWED_AUDIO_MIMES:

        return jsonify({

            "error": f"Invalid MIME type: {audio_file.content_type}"

        }), 400



    # Save to temporary file (required by OpenAI API)

    temp_file_path = None

    try:

        # Create temporary file with correct extension

        import uuid

        temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=file_ext, prefix=f"audio_{uuid.uuid4().hex[:8]}_")

        audio_file.save(temp_file.name)

        temp_file.close()

        temp_file_path = temp_file.name



        # Check file size

        file_size = os.path.getsize(temp_file_path)

        if file_size > MAX_AUDIO_SIZE:

            return jsonify({

                "error": f"File too large. Max size: 25MB (got {file_size / 1024 / 1024:.1f}MB)"

            }), 413



        # Get optional parameters

        language = request.form.get('language')

        model = request.form.get('model', 'whisper-1')



        # Call OpenAI provider (no caching for transcription - unique files)

        provider = PROVIDERS['openai']

        result = provider.provider.transcribe_audio(

            audio_file=temp_file_path,

            language=language,

            model=model,

            response_format="verbose_json"  # Get detailed info

        )



        # Update usage stats

        USAGE_STATS["total_requests"] += 1
        USAGE_STATS["requests_by_provider"]["openai"] = \
            USAGE_STATS["requests_by_provider"].get("openai", 0) + 1
        USAGE_STATS["requests_by_model"]["whisper-1"] = \
            USAGE_STATS["requests_by_model"].get("whisper-1", 0) + 1



        return jsonify({

            "text": result.text,

            "language": result.language,

            "duration": result.duration,

            "model": result.model,

            "success": True

        })



    except Exception as e:

        app.logger.error(f"Transcription error: {e}")

        return jsonify({"error": str(e), "success": False}), 500



    finally:

        # Clean up temporary file

        if temp_file_path and os.path.exists(temp_file_path):

            try:

                os.unlink(temp_file_path)

            except Exception as e:

                app.logger.warning(f"Failed to delete temp file: {e}")





@studio_bp.route("/generate-speech", methods=["POST"])

def generate_speech_endpoint():

    """

    Generate speech from text using OpenAI TTS or ElevenLabs.



    Request JSON:

        - text: str (required, max 4096 for OpenAI, 5000 for ElevenLabs)

        - provider: "openai" or "elevenlabs" (default: "openai")

        - voice: Voice ID/name (provider-specific)

        - model: Optional model override

        - speed: Playback speed (OpenAI only, 0.25-4.0)

        - stability: Voice stability (ElevenLabs only, 0.0-1.0)

        - similarity_boost: Clarity boost (ElevenLabs only, 0.0-1.0)



    Returns:

        Audio file (audio/mpeg) with proper headers

    """

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    text = data.get("text", "").strip()

    provider_name = data.get("provider", "openai").lower()

    voice = data.get("voice")

    model = data.get("model")



    # Validate text

    if not text:

        return jsonify({"error": "Text is required"}), 400



    # Provider-specific limits

    max_length = 4096 if provider_name == "openai" else 5000

    if len(text) > max_length:

        return jsonify({

            "error": f"Text too long. Max {max_length} characters for {provider_name}"

        }), 400



    # Check provider availability

    if provider_name not in PROVIDERS:

        return jsonify({"error": f"Provider {provider_name} not available"}), 503



    provider = PROVIDERS[provider_name]



    # Check if provider supports TTS

    if not hasattr(provider.provider, 'generate_speech'):

        return jsonify({

            "error": f"Provider {provider_name} does not support text-to-speech"

        }), 400



    try:

        # Create pseudo-message for cache (HIGH cache value - same text = same audio)

        # CacheManager expects (provider, model, messages) format

        cache_model = model or ("tts-1" if provider_name == "openai" else "eleven_turbo_v2_5")

        cache_messages = [{"tts_text": text, "voice": voice}]



        # Check cache (6 hour TTL for TTS)

        cached_audio = cache.get(provider_name, cache_model, cache_messages)

        if cached_audio and isinstance(cached_audio, dict) and 'audio_data' in cached_audio:

            USAGE_STATS["cache_savings"] += 1

            app.logger.info(f"TTS cache HIT for {provider_name}")



            # Return cached audio

            import base64

            audio_bytes = base64.b64decode(cached_audio['audio_data'])

            from flask import make_response

            response = make_response(audio_bytes)

            response.headers['Content-Type'] = 'audio/mpeg'

            response.headers['Content-Disposition'] = 'inline; filename="speech.mp3"'

            response.headers['X-Cache-Hit'] = 'true'

            return response



        # Generate speech (cache miss)

        if provider_name == "openai":

            # OpenAI TTS

            voice = voice or "alloy"

            model = model or "tts-1"

            speed = data.get("speed", 1.0)



            result = provider.provider.generate_speech(

                text=text,

                voice=voice,

                model=model,

                speed=speed

            )



        elif provider_name == "elevenlabs":

            # ElevenLabs TTS

            model = model or "eleven_turbo_v2_5"

            stability = data.get("stability", 0.5)

            similarity_boost = data.get("similarity_boost", 0.75)



            # Support both voice_name and voice_id

            voice_name = data.get("voice_name", voice)

            voice_id = data.get("voice_id")



            result = provider.provider.generate_speech(

                text=text,

                voice_id=voice_id,

                voice_name=voice_name,

                model_id=model,

                stability=stability,

                similarity_boost=similarity_boost

            )



        else:

            return jsonify({"error": f"Unknown provider: {provider_name}"}), 400



        # Cache the audio (6 hours = 21600 seconds)

        import base64

        cache_value = {

            "audio_data": base64.b64encode(result.audio_data).decode('utf-8'),

            "provider": provider_name,

            "voice": voice,

            "model": result.model

        }

        cache.set(provider_name, cache_model, cache_messages, cache_value, ttl=21600)

        app.logger.info(f"Cached TTS audio for {provider_name} (TTL: 6h)")



        # Update usage stats

        USAGE_STATS["total_requests"] += 1
        USAGE_STATS["requests_by_provider"][provider_name] = \
            USAGE_STATS["requests_by_provider"].get(provider_name, 0) + 1
        USAGE_STATS["requests_by_model"][result.model] = \
            USAGE_STATS["requests_by_model"].get(result.model, 0) + 1



        # Return audio file

        from flask import make_response

        response = make_response(result.audio_data)

        response.headers['Content-Type'] = 'audio/mpeg'

        response.headers['Content-Disposition'] = 'inline; filename="speech.mp3"'

        response.headers['X-Cache-Hit'] = 'false'

        response.headers['X-Model'] = result.model

        return response



    except ValueError as e:

        return jsonify({"error": str(e)}), 400

    except Exception as e:

        app.logger.error(f"TTS error: {e}")

        return jsonify({"error": str(e)}), 500





@studio_bp.route("/analyze-image", methods=["POST"])

def analyze_image_endpoint():

    """Analyze an image using vision-capable providers"""

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    provider_name = request.form.get("provider", "anthropic")

    model = request.form.get("model")

    prompt = request.form.get("prompt", "Describe this image in detail")



    if provider_name not in PROVIDERS:

        return jsonify({"error": f"Provider {provider_name} not available"}), 400



    if 'image' not in request.files:

        return jsonify({"error": "No image file provided"}), 400



    image_file = request.files['image']

    if image_file.filename == '':

        return jsonify({"error": "No image file selected"}), 400



    # Security: Validate file size before reading

    if request.content_length and request.content_length > MAX_FILE_SIZE:

        size_mb = request.content_length / (1024 * 1024)

        return jsonify({"error": f"File too large: {size_mb:.1f} MB (max: 10 MB)"}), 400



    # Security: Validate MIME type

    if image_file.content_type and image_file.content_type not in ALLOWED_IMAGE_MIMES:

        return jsonify({"error": f"Invalid file type: {image_file.content_type}. Allowed: JPEG, PNG, GIF, WebP"}), 400



    # Security: Validate file extension

    file_ext = Path(image_file.filename).suffix.lower()

    if file_ext not in ALLOWED_IMAGE_EXTENSIONS:

        return jsonify({"error": f"Invalid file extension: {file_ext}. Allowed: .jpg, .jpeg, .png, .gif, .webp"}), 400



    provider = PROVIDERS[provider_name]



    # Check if provider supports vision

    if not hasattr(provider, 'analyze_image'):

        return jsonify({"error": f"Provider {provider_name} does not support image analysis"}), 400



    try:

        # Read image data

        image_bytes = image_file.read()



        # Security: Double-check actual file size after reading

        if len(image_bytes) > MAX_FILE_SIZE:

            size_mb = len(image_bytes) / (1024 * 1024)

            return jsonify({"error": f"File too large: {size_mb:.1f} MB (max: 10 MB)"}), 400



        # Security: Check for empty file

        if len(image_bytes) == 0:

            return jsonify({"error": "Empty file"}), 400



        # Call analyze_image with bytes

        kwargs = {}

        if model:

            kwargs['model'] = model



        result = provider.analyze_image(image_bytes, prompt, **kwargs)



        # result is a CompletionResponse object

        return jsonify({

            "response": result.content,

            "model": result.model,

            "usage": result.usage,

            "metadata": result.metadata

        })



    except NotImplementedError:

        return jsonify({"error": f"Provider {provider_name} does not support image analysis"}), 400

    except Exception as exc:

        return jsonify({"error": str(exc)}), 500





@studio_bp.route("/save-prompt", methods=["POST"])

def save_prompt():

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    prompt = data.get("prompt", "").strip()

    prompt_type = data.get("type", "chat")  # "chat" or "image"



    if not prompt:

        return jsonify({"error": "Prompt is required"}), 400



    prompts_data = load_data(PROMPTS_FILE)



    prompt_entry = {

        "id": len(prompts_data[f"{prompt_type}_prompts"]) + 1,

        "text": prompt,

        "timestamp": datetime.now().isoformat()

    }



    prompts_data[f"{prompt_type}_prompts"].append(prompt_entry)

    save_data(PROMPTS_FILE, prompts_data)



    return jsonify({"success": True, "id": prompt_entry["id"]})





@studio_bp.route("/get-prompts/<prompt_type>")

def get_prompts(prompt_type):

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    if prompt_type not in ["image", "chat"]:

        return jsonify({"error": "Invalid prompt type"}), 400



    prompts_data = load_data(PROMPTS_FILE)

    return jsonify(prompts_data[f"{prompt_type}_prompts"])





@studio_bp.route("/get-saved-images")

def get_saved_images():

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    images_data = load_data(IMAGES_FILE)

    return jsonify(images_data["images"])





@studio_bp.route("/clear-chat", methods=["POST"])

def clear_chat():

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    data = request.get_json()

    provider_name = data.get("provider", "xai")



    if provider_name in PROVIDERS:

        PROVIDERS[provider_name].clear_conversation()



    return jsonify({"status": "cleared"})





@studio_bp.route("/stats")

def get_stats():

    """Get usage statistics and cache performance."""

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    cache_stats = cache.get_stats()



    return jsonify({

        "usage": USAGE_STATS,

        "cache": cache_stats,

        "providers": {

            "total": len(PROVIDERS),

            "available": list(PROVIDERS.keys())

        }

    })





@studio_bp.route("/cache/clear", methods=["POST"])

def clear_cache():

    """Clear the response cache."""

    if not session.get('authenticated'):

        return jsonify({"error": "Not authenticated"}), 401



    cache.clear()

    return jsonify({"status": "cache_cleared", "stats": cache.get_stats()})





# Register the blueprint

app.register_blueprint(studio_bp)



# Register Swarm Portal blueprints

app.register_blueprint(portal_bp, url_prefix=f'{BASE_PATH}/portal')

app.register_blueprint(search_bp, url_prefix=f'{BASE_PATH}')

app.register_blueprint(chat_bp, url_prefix=f'{BASE_PATH}')

app.register_blueprint(hive_bp, url_prefix=f'{BASE_PATH}')



print(f"✓ Swarm Portal blueprints registered at {BASE_PATH}/portal")





# Add standardized health endpoint with dependency checks

def check_cache():

    """Check if cache is operational"""

    try:

        # Check cache backend type and connectivity

        backend = "redis" if cache.redis_client else "in-memory"

        stats = {

            "backend": backend,

            "hits": cache.cache_hits,

            "misses": cache.cache_misses,

            "hit_rate": f"{(cache.cache_hits / max(cache.cache_hits + cache.cache_misses, 1) * 100):.1f}%"

        }



        # Test Redis connection if using Redis

        if cache.redis_client:

            cache.redis_client.ping()

            stats["redis_connected"] = True



        return stats

    except Exception as e:

        raise Exception(f"Cache check failed: {str(e)}")



def check_providers():

    """Check provider availability"""

    return {

        "total": len(PROVIDERS),

        "providers": list(PROVIDERS.keys())

    }



# Create standardized health endpoint

create_health_endpoint(

    app,

    service_name="Studio Multi-Provider AI Interface",

    version="2.0.0",

    checks={

        "cache": check_cache,

        "providers": check_providers

    }

)





# Initialize tool registry

def init_tool_registry():

    """Load and register all Hive tools"""

    try:

        from core.tool_registry import get_registry

        registry = get_registry()



        # Tools are auto-registered on import

        # Additional manual registration can be done here



        tools_count = len(registry.list_tools())

        modules_count = len(registry.list_modules())



        print(f"✓ Tool registry initialized: {tools_count} tools, {modules_count} modules")

    except Exception as e:

        print(f"⚠ Tool registry initialization failed: {e}")



# Initialize on startup

init_tool_registry()





if __name__ == "__main__":

    import signal

    import sys



    # Port configuration

    PORT = int(os.getenv("STUDIO_PORT", "5413"))



    # Only check port on first run (not when Flask reloader spawns child)

    if os.getenv("WERKZEUG_RUN_MAIN") != "true":

        # Check if port is already in use (only in parent process)

        import socket

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        sock.settimeout(1)

        port_in_use = sock.connect_ex(('localhost', PORT)) == 0

        sock.close()



        if port_in_use:

            print(f"\n⚠️  ERROR: Port {PORT} is already in use!")

            print(f"To stop the existing server, run:")

            print(f"  kill -9 $(lsof -ti :{PORT})")

            print(f"\nOr set a different port:")

            print(f"  export STUDIO_PORT=5414")

            sys.exit(1)



        print("\n" + "="*50)

        print("LLM Studio - Multi-Provider AI Interface")

        print("="*50)

        print(f"\nAvailable providers: {list(PROVIDERS.keys())}")

        print(f"Total providers: {len(PROVIDERS)}")

        print(f"\nStarting server on http://localhost:{PORT}")

        print("="*50 + "\n")



    app.run(host="0.0.0.0", port=PORT, debug=True)


# CLI entry point for pip-installed package
def main():
    """Entry point for llm-studio command"""
    import sys
    import os
    
    # Set up environment
    PORT = int(os.getenv("STUDIO_PORT", "5413"))
    
    # Only check port on first run
    if os.getenv("WERKZEUG_RUN_MAIN") != "true":
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(1)
        port_in_use = sock.connect_ex(('localhost', PORT)) == 0
        sock.close()
        
        if port_in_use:
            print(f"\n⚠️  ERROR: Port {PORT} is already in use!")
            print(f"To stop the existing server, run:")
            print(f"  kill -9 $(lsof -ti :{PORT})")
            print(f"\nOr set a different port:")
            print(f"  export STUDIO_PORT=5414")
            sys.exit(1)
        
        print("\n" + "="*50)
        print("LLM Studio - Multi-Provider AI Interface")
        print("="*50)
        print(f"\nAvailable providers: {list(PROVIDERS.keys())}")
        print(f"Total providers: {len(PROVIDERS)}")
        print(f"\nStarting server on http://localhost:{PORT}")
        print("="*50 + "\n")
    
    app.run(host="0.0.0.0", port=PORT, debug=True)
