"""
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>

AISBF - AI Service Broker Framework || AI Should Be Free

Anthropic provider handler (API key-based).

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

Why did the programmer quit his job? Because he didn't get arrays!
"""
import time
from typing import Dict, List, Optional, Union
from anthropic import Anthropic
from ..models import Model
from ..config import config
from ..utils import count_messages_tokens
from .base import BaseProviderHandler, AnthropicFormatConverter, AISBF_DEBUG


class AnthropicProviderHandler(BaseProviderHandler):
    def __init__(self, provider_id: str, api_key: str, user_id=None, provider_config=None):
        super().__init__(provider_id, api_key, user_id=user_id)
        self.provider_config = provider_config
        self.client = Anthropic(api_key=api_key)
    
    def validate_credentials(self) -> bool:
        """Validate Anthropic API key presence."""
        if not self.api_key:
            logging.error(f"[{self.provider_id}] API key required but not provided")
            return False
        
        stripped = self.api_key.strip()
        if not stripped or stripped.startswith('YOUR_'):
            logging.error(f"[{self.provider_id}] API key appears to be a placeholder")
            return False
        
        logging.info(f"[{self.provider_id}] API key validated")
        return True

    async def handle_request(self, model: str, messages: List[Dict], max_tokens: Optional[int] = None,
                            temperature: Optional[float] = 1.0, stream: Optional[bool] = False,
                            tools: Optional[List[Dict]] = None, tool_choice: Optional[Union[str, Dict]] = None) -> Dict:
        if self.is_rate_limited():
            raise Exception("Provider rate limited")

        try:
            import logging
            logging.info(f"AnthropicProviderHandler: Handling request for model {model}")
            if AISBF_DEBUG:
                logging.info(f"AnthropicProviderHandler: Messages: {messages}")
            else:
                logging.info(f"AnthropicProviderHandler: Messages count: {len(messages)}")

            # Apply rate limiting
            await self.apply_rate_limit()

            # Check if native caching is enabled for this provider
            provider_config = config.providers.get(self.provider_id)
            enable_native_caching = getattr(provider_config, 'enable_native_caching', False)
            min_cacheable_tokens = getattr(provider_config, 'min_cacheable_tokens', 1000)

            if enable_native_caching and not self._native_cache_user_allows(model):
                enable_native_caching = False
                logging.info(f"User {self.user_id} disabled cache for provider={self.provider_id}, model={model}")

            logging.info(f"AnthropicProviderHandler: Native caching enabled: {enable_native_caching}")
            if enable_native_caching:
                logging.info(f"AnthropicProviderHandler: Min cacheable tokens: {min_cacheable_tokens}")

            # Convert OpenAI messages to Anthropic format
            system_message = None
            anthropic_messages = []
            
            for msg in messages:
                role = msg.get('role')
                content = msg.get('content')
                
                if role == 'system':
                    system_message = content
                    logging.info(f"AnthropicProviderHandler: Extracted system message ({len(content) if content else 0} chars)")
                
                elif role == 'tool':
                    tool_call_id = msg.get('tool_call_id', msg.get('name', 'unknown'))
                    tool_result_block = {
                        'type': 'tool_result',
                        'tool_use_id': tool_call_id,
                        'content': content or ""
                    }
                    
                    if anthropic_messages and anthropic_messages[-1]['role'] == 'user':
                        last_content = anthropic_messages[-1]['content']
                        if isinstance(last_content, str):
                            anthropic_messages[-1]['content'] = [
                                {'type': 'text', 'text': last_content},
                                tool_result_block
                            ]
                        elif isinstance(last_content, list):
                            anthropic_messages[-1]['content'].append(tool_result_block)
                        logging.info(f"AnthropicProviderHandler: Appended tool_result to existing user message")
                    else:
                        anthropic_messages.append({
                            'role': 'user',
                            'content': [tool_result_block]
                        })
                        logging.info(f"AnthropicProviderHandler: Created new user message with tool_result")
                
                elif role == 'assistant':
                    tool_calls = msg.get('tool_calls')
                    
                    if tool_calls:
                        content_blocks = []
                        
                        if content and isinstance(content, str) and content.strip():
                            content_blocks.append({'type': 'text', 'text': content})
                        elif content and isinstance(content, list):
                            content_blocks.extend(content)
                        
                        import json as _json
                        for tc in tool_calls:
                            tool_id = tc.get('id', f"toolu_{len(content_blocks)}")
                            function = tc.get('function', {})
                            tool_name = function.get('name', '')
                            arguments = function.get('arguments', {})
                            if isinstance(arguments, str):
                                try:
                                    arguments = _json.loads(arguments)
                                except _json.JSONDecodeError:
                                    logging.warning(f"AnthropicProviderHandler: Failed to parse tool arguments: {arguments}")
                                    arguments = {}
                            
                            content_blocks.append({
                                'type': 'tool_use',
                                'id': tool_id,
                                'name': tool_name,
                                'input': arguments
                            })
                            logging.info(f"AnthropicProviderHandler: Converted tool_call to tool_use: {tool_name}")
                        
                        if content_blocks:
                            anthropic_messages.append({
                                'role': 'assistant',
                                'content': content_blocks
                            })
                    else:
                        if content is not None:
                            anthropic_messages.append({
                                'role': 'assistant',
                                'content': content
                            })
                        else:
                            logging.info(f"AnthropicProviderHandler: Skipping assistant message with None content")
                
                elif role == 'user':
                    if isinstance(content, list):
                        content_blocks = []
                        for block in content:
                            if isinstance(block, dict):
                                block_type = block.get('type', '')
                                if block_type == 'text':
                                    content_blocks.append(block)
                                elif block_type == 'image_url':
                                    image_url_obj = block.get('image_url', {})
                                    url = image_url_obj.get('url', '') if isinstance(image_url_obj, dict) else ''
                                    if url.startswith('data:'):
                                        try:
                                            header, data = url.split(',', 1)
                                            media_type = header.split(';')[0].replace('data:', '')
                                            content_blocks.append({
                                                'type': 'image',
                                                'source': {
                                                    'type': 'base64',
                                                    'media_type': media_type,
                                                    'data': data
                                                }
                                            })
                                        except (ValueError, IndexError) as e:
                                            logging.warning(f"AnthropicProviderHandler: Failed to parse data URL: {e}")
                                    elif url.startswith(('http://', 'https://')):
                                        content_blocks.append({
                                            'type': 'image',
                                            'source': {
                                                'type': 'url',
                                                'url': url
                                            }
                                        })
                                else:
                                    content_blocks.append(block)
                            elif isinstance(block, str):
                                content_blocks.append({'type': 'text', 'text': block})
                        
                        anthropic_messages.append({
                            'role': 'user',
                            'content': content_blocks if content_blocks else content or ""
                        })
                    else:
                        anthropic_messages.append({
                            'role': 'user',
                            'content': content or ""
                        })
                
                else:
                    logging.warning(f"AnthropicProviderHandler: Unknown message role '{role}', treating as user")
                    anthropic_messages.append({
                        'role': 'user',
                        'content': content or ""
                    })
            
            logging.info(f"AnthropicProviderHandler: Converted {len(messages)} OpenAI messages to {len(anthropic_messages)} Anthropic messages")
            if system_message:
                logging.info(f"AnthropicProviderHandler: System message extracted ({len(system_message)} chars)")
            
            # Apply cache_control if native caching is enabled
            if enable_native_caching:
                cumulative_tokens = 0
                for i, msg in enumerate(anthropic_messages):
                    message_tokens = count_messages_tokens([{'role': msg['role'], 'content': msg['content'] if isinstance(msg['content'], str) else str(msg['content'])}], model)
                    cumulative_tokens += message_tokens
                    
                    if i < len(anthropic_messages) - 2 and cumulative_tokens >= min_cacheable_tokens:
                        content = msg.get('content')
                        if isinstance(content, str) and content.strip():
                            msg['content'] = [
                                {
                                    'type': 'text',
                                    'text': content,
                                    'cache_control': {'type': 'ephemeral'}
                                }
                            ]
                        elif isinstance(content, list) and content:
                            content[-1]['cache_control'] = {'type': 'ephemeral'}
                        logging.info(f"AnthropicProviderHandler: Applied cache_control to message {i} ({message_tokens} tokens, cumulative: {cumulative_tokens})")
                
                # Also apply cache_control to system message if present
                if system_message:
                    system_message_param = [{
                        'type': 'text',
                        'text': system_message,
                        'cache_control': {'type': 'ephemeral'}
                    }]
                else:
                    system_message_param = None
            else:
                system_message_param = system_message
            
            # Convert OpenAI tools to Anthropic format
            anthropic_tools = None
            if tools:
                anthropic_tools = []
                for tool in tools:
                    if tool.get("type") == "function":
                        function = tool.get("function", {})
                        anthropic_tools.append({
                            "name": function.get("name", ""),
                            "description": function.get("description", ""),
                            "input_schema": function.get("parameters", {})
                        })
                        logging.info(f"AnthropicProviderHandler: Converted tool to Anthropic format: {function.get('name')}")
                if not anthropic_tools:
                    anthropic_tools = None
            
            # Convert OpenAI tool_choice to Anthropic format
            anthropic_tool_choice = None
            if tool_choice and anthropic_tools:
                if isinstance(tool_choice, str):
                    if tool_choice == "auto":
                        anthropic_tool_choice = {"type": "auto"}
                    elif tool_choice == "required":
                        anthropic_tool_choice = {"type": "any"}
                    elif tool_choice == "none":
                        anthropic_tool_choice = None
                elif isinstance(tool_choice, dict):
                    if tool_choice.get("type") == "function":
                        func_name = tool_choice.get("function", {}).get("name")
                        if func_name:
                            anthropic_tool_choice = {"type": "tool", "name": func_name}
            
            # Build API call parameters
            api_params = {
                'model': model,
                'messages': anthropic_messages,
                'max_tokens': max_tokens or 4096,
                'temperature': temperature,
            }
            
            if system_message_param:
                api_params['system'] = system_message_param
            
            if anthropic_tools:
                api_params['tools'] = anthropic_tools
            
            if anthropic_tool_choice:
                api_params['tool_choice'] = anthropic_tool_choice
            
            if AISBF_DEBUG:
                import json as _json
                logging.info(f"=== ANTHROPIC API REQUEST PAYLOAD ===")
                debug_params = dict(api_params)
                logging.info(f"Request keys: {list(debug_params.keys())}")
                logging.info(f"Model: {debug_params.get('model')}")
                logging.info(f"Messages count: {len(debug_params.get('messages', []))}")
                logging.info(f"Tools count: {len(debug_params.get('tools', []) or [])}")
                logging.info(f"Tool choice: {debug_params.get('tool_choice')}")
                logging.info(f"System: {'present' if debug_params.get('system') else 'none'}")
                logging.info(f"Full payload: {_json.dumps(debug_params, indent=2, default=str)}")
                logging.info(f"=== END ANTHROPIC API REQUEST PAYLOAD ===")
            
            # Streaming mode: return an async generator that converts Anthropic
            # SSE events into OpenAI-compatible chat.completion.chunk dicts. The
            # caller records success after the stream is primed/consumed, so we
            # do NOT record success here (the upstream call has not run yet).
            if stream:
                logging.info(f"AnthropicProviderHandler: Using streaming mode")
                return self._handle_streaming_request(api_params, model)

            response = self.client.messages.create(**api_params)
            logging.info(f"AnthropicProviderHandler: Response received: {response}")
            self.record_success()
            
            # Dump raw response if AISBF_DEBUG is enabled
            if AISBF_DEBUG:
                logging.info(f"=== RAW ANTHROPIC RESPONSE ===")
                logging.info(f"Raw response type: {type(response)}")
                logging.info(f"Raw response: {response}")
                logging.info(f"Raw response dir: {dir(response)}")
                logging.info(f"=== END RAW ANTHROPIC RESPONSE ===")
            
            logging.info(f"=== ANTHROPIC RESPONSE PARSING START ===")
            logging.info(f"Response type: {type(response)}")
            logging.info(f"Response attributes: {dir(response)}")
            
            content_text = ""
            tool_calls = None
            
            try:
                if hasattr(response, 'content') and response.content:
                    logging.info(f"Response has 'content' attribute")
                    logging.info(f"Content blocks: {response.content}")
                    logging.info(f"Content blocks count: {len(response.content)}")
                    
                    text_parts = []
                    openai_tool_calls = []
                    call_id = 0
                    
                    for idx, block in enumerate(response.content):
                        logging.info(f"Processing block {idx}")
                        logging.info(f"Block type: {type(block)}")
                        logging.info(f"Block attributes: {dir(block)}")
                        
                        if hasattr(block, 'text') and block.text:
                            logging.info(f"Block {idx} has 'text' attribute")
                            text_parts.append(block.text)
                            logging.info(f"Block {idx} text length: {len(block.text)}")
                        
                        if hasattr(block, 'type') and block.type == 'tool_use':
                            logging.info(f"Block {idx} is a tool_use block")
                            logging.info(f"Tool use block: {block}")
                            
                            try:
                                import json as _json_tc
                                raw_input = block.input if hasattr(block, 'input') else {}
                                arguments_str = _json_tc.dumps(raw_input) if isinstance(raw_input, dict) else str(raw_input)
                                openai_tool_call = {
                                    "id": block.id if hasattr(block, 'id') else f"call_{call_id}",
                                    "type": "function",
                                    "function": {
                                        "name": block.name if hasattr(block, 'name') else "",
                                        "arguments": arguments_str
                                    }
                                }
                                openai_tool_calls.append(openai_tool_call)
                                call_id += 1
                                logging.info(f"Converted tool_use to OpenAI format: {openai_tool_call}")
                            except Exception as e:
                                logging.error(f"Error converting tool_use: {e}", exc_info=True)
                    
                    content_text = "\n".join(text_parts)
                    logging.info(f"Combined text length: {len(content_text)}")
                    logging.info(f"Combined text (first 200 chars): {content_text[:200] if content_text else 'None'}")
                    
                    if openai_tool_calls:
                        tool_calls = openai_tool_calls
                        logging.info(f"Total tool calls: {len(tool_calls)}")
                        for tc in tool_calls:
                            logging.info(f"  - {tc}")
                    else:
                        logging.info(f"No tool calls found")
                else:
                    logging.warning(f"Response does NOT have 'content' attribute or content is empty")
                
                stop_reason_map = {
                    'end_turn': 'stop',
                    'max_tokens': 'length',
                    'stop_sequence': 'stop',
                    'tool_use': 'tool_calls'
                }
                stop_reason = getattr(response, 'stop_reason', 'stop')
                finish_reason = stop_reason_map.get(stop_reason, 'stop')
                logging.info(f"Anthropic stop_reason: {stop_reason}")
                logging.info(f"Mapped finish_reason: {finish_reason}")
                
            except Exception as e:
                logging.error(f"AnthropicProviderHandler: Exception during response parsing: {e}", exc_info=True)
                content_text = ""
            
            logging.info(f"=== ANTHROPIC RESPONSE PARSING END ===")
            
            # Build OpenAI-style response
            openai_response = {
                "id": f"anthropic-{model}-{int(time.time())}",
                "object": "chat.completion",
                "created": int(time.time()),
                "model": f"{self.provider_id}/{model}",
                "choices": [{
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": content_text if content_text else None
                    },
                    "finish_reason": finish_reason
                }],
                "usage": {
                    "prompt_tokens": getattr(getattr(response, "usage", None), "input_tokens", 0) or 0,
                    "completion_tokens": getattr(getattr(response, "usage", None), "output_tokens", 0) or 0,
                    "total_tokens": (getattr(getattr(response, "usage", None), "input_tokens", 0) or 0) + (getattr(getattr(response, "usage", None), "output_tokens", 0) or 0)
                }
            }
            
            if tool_calls:
                openai_response["choices"][0]["message"]["tool_calls"] = tool_calls
                openai_response["choices"][0]["message"]["content"] = None
                logging.info(f"Added tool_calls to response message")
            
            logging.info(f"=== FINAL ANTHROPIC RESPONSE STRUCTURE ===")
            logging.info(f"Response id: {openai_response['id']}")
            logging.info(f"Response model: {openai_response['model']}")
            logging.info(f"Response choices[0] message content: {openai_response['choices'][0]['message']['content']}")
            logging.info(f"Response choices[0] message tool_calls: {openai_response['choices'][0]['message'].get('tool_calls')}")
            logging.info(f"Response choices[0] finish_reason: {openai_response['choices'][0]['finish_reason']}")
            logging.info(f"Response usage: {openai_response['usage']}")
            logging.info(f"=== END FINAL ANTHROPIC RESPONSE STRUCTURE ===")
            
            logging.info(f"AnthropicProviderHandler: Returning response dict (no validation)")
            logging.info(f"Response dict keys: {openai_response.keys()}")
            
            if AISBF_DEBUG:
                logging.info(f"=== FINAL ANTHROPIC RESPONSE DICT ===")
                logging.info(f"Final response: {openai_response}")
                logging.info(f"=== END FINAL ANTHROPIC RESPONSE DICT ===")
            
            return openai_response
        except Exception as e:
            import logging
            logging.error(f"AnthropicProviderHandler: Error: {str(e)}", exc_info=True)
            self.record_failure()
            raise e

    async def _handle_streaming_request(self, api_params: Dict, model: str):
        """Async generator that streams an Anthropic request and yields
        OpenAI-compatible chat.completion.chunk dicts.

        The Anthropic SDK client is synchronous, so the underlying event stream
        is iterated with a regular ``for`` loop (matching how the OpenAI provider
        forwards its sync SDK stream). Records failure on error and success once
        the stream finishes.
        """
        logging.info(f"AnthropicProviderHandler: Starting streaming request for model {model}")

        response_id = f"anthropic-{model}-{int(time.time())}"
        created_time = int(time.time())

        stop_reason_map = {
            'end_turn': 'stop',
            'max_tokens': 'length',
            'stop_sequence': 'stop',
            'tool_use': 'tool_calls',
        }

        def _base_chunk():
            return {
                "id": response_id,
                "object": "chat.completion.chunk",
                "created": created_time,
                "model": f"{self.provider_id}/{model}",
                "choices": [{"index": 0, "delta": {}, "finish_reason": None}],
            }

        try:
            stream_params = dict(api_params)
            stream_params["stream"] = True

            prompt_tokens = 0
            completion_tokens = 0
            tool_block_index = -1  # OpenAI tool_calls index, incremented per tool_use block
            finish_reason = "stop"

            # First chunk announces the assistant role (OpenAI convention).
            first = _base_chunk()
            first["choices"][0]["delta"] = {"role": "assistant"}
            yield first

            stream = self.client.messages.create(**stream_params)
            for event in stream:
                    etype = getattr(event, "type", None)

                    if etype == "message_start":
                        usage = getattr(getattr(event, "message", None), "usage", None)
                        if usage is not None:
                            prompt_tokens = getattr(usage, "input_tokens", 0) or 0

                    elif etype == "content_block_start":
                        block = getattr(event, "content_block", None)
                        if block is not None and getattr(block, "type", None) == "tool_use":
                            tool_block_index += 1
                            chunk = _base_chunk()
                            chunk["choices"][0]["delta"] = {
                                "tool_calls": [{
                                    "index": tool_block_index,
                                    "id": getattr(block, "id", "") or f"call_{tool_block_index}",
                                    "type": "function",
                                    "function": {"name": getattr(block, "name", "") or "", "arguments": ""},
                                }]
                            }
                            yield chunk

                    elif etype == "content_block_delta":
                        delta = getattr(event, "delta", None)
                        dtype = getattr(delta, "type", None)
                        if dtype == "text_delta":
                            text = getattr(delta, "text", "") or ""
                            if text:
                                chunk = _base_chunk()
                                chunk["choices"][0]["delta"] = {"content": text}
                                yield chunk
                        elif dtype == "input_json_delta":
                            partial = getattr(delta, "partial_json", "") or ""
                            if partial and tool_block_index >= 0:
                                chunk = _base_chunk()
                                chunk["choices"][0]["delta"] = {
                                    "tool_calls": [{
                                        "index": tool_block_index,
                                        "function": {"arguments": partial},
                                    }]
                                }
                                yield chunk

                    elif etype == "message_delta":
                        delta = getattr(event, "delta", None)
                        sr = getattr(delta, "stop_reason", None)
                        if sr:
                            finish_reason = stop_reason_map.get(sr, "stop")
                        usage = getattr(event, "usage", None)
                        if usage is not None:
                            completion_tokens = getattr(usage, "output_tokens", 0) or completion_tokens

            # Final chunk with finish_reason and usage.
            final = _base_chunk()
            final["choices"][0]["finish_reason"] = finish_reason
            final["usage"] = {
                "prompt_tokens": prompt_tokens,
                "completion_tokens": completion_tokens,
                "total_tokens": prompt_tokens + completion_tokens,
            }
            yield final

            yield b"data: [DONE]\n\n"

            self.record_success()
            logging.info(f"AnthropicProviderHandler: Streaming completed successfully")
        except Exception as e:
            logging.error(f"AnthropicProviderHandler: Streaming error: {str(e)}", exc_info=True)
            self.record_failure()
            raise

    async def get_models(self) -> List[Model]:
        """
        Return list of available Anthropic models.
        """
        try:
            import logging
            logging.info("=" * 80)
            logging.info("AnthropicProviderHandler: Starting model list retrieval")
            logging.info("=" * 80)

            await self.apply_rate_limit()

            try:
                logging.info("AnthropicProviderHandler: Attempting to fetch models from API...")
                logging.info("AnthropicProviderHandler: Note: Anthropic doesn't currently provide a public models endpoint")
                logging.info("AnthropicProviderHandler: Checking if endpoint is now available...")
                
                response = self.client.models.list()
                if response:
                    logging.info(f"AnthropicProviderHandler: ✓ API call successful!")
                    logging.info(f"AnthropicProviderHandler: Retrieved models from API")
                    
                    models = [Model(id=model.id, name=model.id, provider_id=self.provider_id) for model in response]
                    
                    for model in models:
                        logging.info(f"AnthropicProviderHandler:   - {model.id}")
                    
                    logging.info("=" * 80)
                    logging.info(f"AnthropicProviderHandler: ✓ SUCCESS - Returning {len(models)} models from API")
                    logging.info(f"AnthropicProviderHandler: Source: Dynamic API retrieval")
                    logging.info("=" * 80)
                    return models
            except AttributeError as attr_error:
                logging.info(f"AnthropicProviderHandler: ✗ API endpoint not available")
                logging.info(f"AnthropicProviderHandler: Error: {type(attr_error).__name__} - {str(attr_error)}")
                logging.info("AnthropicProviderHandler: Reason: Anthropic SDK doesn't expose models.list() method")
                logging.info("AnthropicProviderHandler: Action: Falling back to static list")
            except Exception as api_error:
                logging.warning(f"AnthropicProviderHandler: ✗ Exception during API call")
                logging.warning(f"AnthropicProviderHandler: Error type: {type(api_error).__name__}")
                logging.warning(f"AnthropicProviderHandler: Error message: {str(api_error)}")
                logging.warning("AnthropicProviderHandler: Action: Falling back to static list")
                if AISBF_DEBUG:
                    logging.warning(f"AnthropicProviderHandler: Full traceback:", exc_info=True)
            
            logging.info("-" * 80)
            logging.info("AnthropicProviderHandler: Using static fallback model list")
            logging.info("AnthropicProviderHandler: Note: This is the expected behavior for Anthropic provider")
            
            static_models = [
                Model(id="claude-3-7-sonnet-20250219", name="Claude 3.7 Sonnet", provider_id=self.provider_id, context_size=200000, context_length=200000),
                Model(id="claude-3-5-sonnet-20241022", name="Claude 3.5 Sonnet", provider_id=self.provider_id, context_size=200000, context_length=200000),
                Model(id="claude-3-5-haiku-20241022", name="Claude 3.5 Haiku", provider_id=self.provider_id, context_size=200000, context_length=200000),
                Model(id="claude-3-opus-20240229", name="Claude 3 Opus", provider_id=self.provider_id, context_size=200000, context_length=200000),
                Model(id="claude-3-haiku-20240307", name="Claude 3 Haiku", provider_id=self.provider_id, context_size=200000, context_length=200000),
                Model(id="claude-3-sonnet-20240229", name="Claude 3 Sonnet", provider_id=self.provider_id, context_size=200000, context_length=200000),
            ]
            
            for model in static_models:
                logging.info(f"AnthropicProviderHandler:   - {model.id} ({model.name})")
            
            logging.info("=" * 80)
            logging.info(f"AnthropicProviderHandler: ✓ Returning {len(static_models)} models from static list")
            logging.info(f"AnthropicProviderHandler: Source: Static fallback configuration")
            logging.info("=" * 80)
            
            return static_models
        except Exception as e:
            import logging
            logging.error("=" * 80)
            logging.error(f"AnthropicProviderHandler: ✗ FATAL ERROR getting models: {str(e)}")
            logging.error("=" * 80)
            logging.error(f"AnthropicProviderHandler: Error details:", exc_info=True)
            raise e
