from __future__ import annotations

import json
import uuid

from ..typing import AsyncResult, Messages
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..image import ImageResponse
from ..requests import StreamSession, raise_for_status
from ..errors import ResponseStatusError

MODELS = {
    'chat': {
        'gpt-4o-2024-11-20': {'persona_id': "gpt"},
        'gpt-4o': {'persona_id': "summarizer"},
        'gpt-4o-mini': {'persona_id': "amigo"},

        'o1-preview-': {'persona_id': "openai-o-one"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'o1-preview-2024-09-12-': {'persona_id': "orion"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'o1-mini-': {'persona_id': "openai-o-one-mini"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        
        'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo': {'persona_id': "llama-three-point-one"},
        'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo': {'persona_id': "llama-3-2"},
        'codellama/CodeLlama-34b-Instruct-hf': {'persona_id': "codellama-CodeLlama-34b-Instruct-hf"},
        
        'gemini-1.5-pro': {'persona_id': "gemini-1-5-pro"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'gemini-1.5-flash': {'persona_id': "gemini-1.5-flash"},
        
        'claude-3-5-sonnet-20240620': {'persona_id': "claude"},
        'claude-3-5-sonnet-20241022': {'persona_id': "clude-claude-3-5-sonnet-20241022"},
        'claude-3-5-haiku-latest': {'persona_id': "3-5-haiku"},
        
        'Qwen/Qwen2.5-72B-Instruct-Turbo': {'persona_id': "qwen-2-5"},
        
        'google/gemma-2b-it': {'persona_id': "google-gemma-2b-it"},
        'google/gemma-7b': {'persona_id': "google-gemma-7b"}, # Error handling AIML chat completion stream
        
        'Gryphe/MythoMax-L2-13b': {'persona_id': "Gryphe-MythoMax-L2-13b"},
        
        'mistralai/Mistral-7B-Instruct-v0.3': {'persona_id': "mistralai-Mistral-7B-Instruct-v0.1"},
        'mistralai/mistral-tiny': {'persona_id': "mistralai-mistral-tiny"},
        'mistralai/mistral-nemo': {'persona_id': "mistralai-mistral-nemo"},
        
        'deepseek-ai/deepseek-llm-67b-chat': {'persona_id': "deepseek-ai-deepseek-llm-67b-chat"},
        
        'databricks/dbrx-instruct': {'persona_id': "databricks-dbrx-instruct"},
        
        'NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO': {'persona_id': "NousResearch-Nous-Hermes-2-Mixtral-8x7B-DPO"},
        
        'x-ai/grok-beta': {'persona_id': "x-ai-grok-beta"},
        
        'anthracite-org/magnum-v4-72b': {'persona_id': "anthracite-org-magnum-v4-72b"},
        
        'cohere/command-r-plus': {'persona_id': "cohere-command-r-plus"},
        
        'ai21/jamba-1-5-mini': {'persona_id': "ai21-jamba-1-5-mini"},
        
        'zero-one-ai/Yi-34B': {'persona_id': "zero-one-ai-Yi-34B"} # Error handling AIML chat completion stream
    },
    
    'image': {
        'flux-pro/v1.1': {'persona_id': "flux-1-1-pro"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'flux-realism': {'persona_id': "flux-realism"},
        'flux-pro': {'persona_id': "flux-pro"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'flux-pro/v1.1-ultra': {'persona_id': "flux-pro-v1.1-ultra"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'flux-pro/v1.1-ultra-raw': {'persona_id': "flux-pro-v1.1-ultra-raw"}, # Amigo, your balance is not enough to make the request, wait until 12 UTC or upgrade your plan
        'flux/dev': {'persona_id': "flux-dev"},

        'dall-e-3': {'persona_id': "dalle-three"},

        'recraft-v3': {'persona_id': "recraft"}
    }
}

class AmigoChat(AsyncGeneratorProvider, ProviderModelMixin):
    url = "https://amigochat.io/chat/"
    chat_api_endpoint = "https://api.amigochat.io/v1/chat/completions"
    image_api_endpoint = "https://api.amigochat.io/v1/images/generations"
    working = True
    supports_stream = True
    supports_system_message = True
    supports_message_history = True
       
    default_model = 'gpt-4o-mini'

    chat_models = list(MODELS['chat'].keys())
    image_models = list(MODELS['image'].keys())
    models = chat_models + image_models
    
    model_aliases = {
        ### chat ###
        "gpt-4o": "gpt-4o-2024-11-20",
        "gpt-4o-mini": "gpt-4o-mini",
        
        "llama-3.1-405b": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
        "llama-3.2-90b": "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
        "codellama-34b": "codellama/CodeLlama-34b-Instruct-hf",
        
        "gemini-flash": "gemini-1.5-flash",
        
        "claude-3.5-sonnet": "claude-3-5-sonnet-20240620",
        "claude-3.5-sonnet": "claude-3-5-sonnet-20241022",
        "claude-3.5-haiku": "claude-3-5-haiku-latest",
        
        "qwen-2.5-72b": "Qwen/Qwen2.5-72B-Instruct-Turbo",
        "gemma-2b": "google/gemma-2b-it",
        
        "mythomax-13b": "Gryphe/MythoMax-L2-13b",
        
        "mixtral-7b": "mistralai/Mistral-7B-Instruct-v0.3",
        "mistral-nemo": "mistralai/mistral-nemo",
        
        "deepseek-chat": "deepseek-ai/deepseek-llm-67b-chat",
        
        "dbrx-instruct": "databricks/dbrx-instruct",
        
        "mixtral-8x7b-dpo": "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
        
        "grok-beta": "x-ai/grok-beta",
        
        "magnum-72b": "anthracite-org/magnum-v4-72b",
        
        "command-r-plus": "cohere/command-r-plus",
        
        "jamba-mini": "ai21/jamba-1-5-mini",
        
        
        ### image ###
        "flux-dev": "flux/dev",
    }

    @classmethod
    def get_personaId(cls, model: str) -> str:
        if model in cls.chat_models:
            return MODELS['chat'][model]['persona_id']
        elif model in cls.image_models:
            return MODELS['image'][model]['persona_id']
        else:
            raise ValueError(f"Unknown model: {model}")
            
    @staticmethod
    def generate_chat_id() -> str:
        """Generate a chat ID in format: 8-4-4-4-12 hexadecimal digits"""
        return str(uuid.uuid4())
		
    @classmethod
    async def create_async_generator(
        cls,
        model: str,
        messages: Messages,
        proxy: str = None,
        stream: bool = False,
        timeout: int = 300,
        frequency_penalty: float = 0,
        max_tokens: int = 4000,
        presence_penalty: float = 0,
        temperature: float = 0.5,
        top_p: float = 0.95,
        **kwargs
    ) -> AsyncResult:
        model = cls.get_model(model)
        
        device_uuid = str(uuid.uuid4())
        max_retries = 3
        retry_count = 0

        while retry_count < max_retries:
            try:
                headers = {
                    "accept": "*/*",
                    "accept-language": "en-US,en;q=0.9",
                    "authorization": "Bearer",
                    "cache-control": "no-cache",
                    "content-type": "application/json",
                    "origin": cls.url,
                    "pragma": "no-cache",
                    "priority": "u=1, i",
                    "referer": f"{cls.url}/",
                    "sec-ch-ua": '"Chromium";v="129", "Not=A?Brand";v="8"',
                    "sec-ch-ua-mobile": "?0",
                    "sec-ch-ua-platform": '"Linux"',
                    "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
                    "x-device-language": "en-US",
                    "x-device-platform": "web",
                    "x-device-uuid": device_uuid,
                    "x-device-version": "1.0.45"
                }
                
                async with StreamSession(headers=headers, proxy=proxy) as session:
                    if model not in cls.image_models:
                        data = {
                            "chatId": cls.generate_chat_id(),
                            "frequency_penalty": frequency_penalty,
                            "max_tokens": max_tokens,
                            "messages": messages,
                            "model": model,
                            "personaId": cls.get_personaId(model),
                            "presence_penalty": presence_penalty,
                            "stream": stream,
                            "temperature": temperature,
                            "top_p": top_p
                        }
                        async with session.post(cls.chat_api_endpoint, json=data, timeout=timeout) as response:
                            await raise_for_status(response)
                            async for line in response.iter_lines():
                                line = line.decode('utf-8').strip()
                                if line.startswith('data: '):
                                    if line == 'data: [DONE]':
                                        break
                                    try:
                                        chunk = json.loads(line[6:])  # Remove 'data: ' prefix
                                        if 'choices' in chunk and len(chunk['choices']) > 0:
                                            choice = chunk['choices'][0]
                                            if 'delta' in choice:
                                                content = choice['delta'].get('content')
                                            elif 'text' in choice:
                                                content = choice['text']
                                            else:
                                                content = None
                                            if content:
                                                yield content
                                    except json.JSONDecodeError:
                                        pass
                    else:
                        # Image generation
                        prompt = messages[-1]['content']
                        data = {
                            "prompt": prompt,
                            "model": model,
                            "personaId": cls.get_personaId(model)
                        }
                        async with session.post(cls.image_api_endpoint, json=data) as response:
                            await raise_for_status(response)
                            response_data = await response.json()
                            if "data" in response_data:
                                image_urls = []
                                for item in response_data["data"]:
                                    if "url" in item:
                                        image_url = item["url"]
                                        image_urls.append(image_url)
                                if image_urls:
                                    yield ImageResponse(image_urls, prompt)
                            else:
                                yield None
                break
            except (ResponseStatusError, Exception) as e:
                retry_count += 1
                if retry_count >= max_retries:
                    raise e
                device_uuid = str(uuid.uuid4())
