from typing import Any, Dict, Optional, Union
import aiohttp
import logging
import json
import csv
import io
from app.services.service_manager import ServiceManager
from app.schemas.file import FileInDB
from sqlalchemy.orm import Session

logger = logging.getLogger(__name__)

class BaseExternalService:
    def __init__(self, base_url: str, api_key: Optional[str] = None):
        self.base_url = base_url
        self.api_key = api_key
        self.session = None
        self.service_manager = ServiceManager()
        self._services = {}

    async def _get_session(self) -> aiohttp.ClientSession:
        if self.session is None or self.session.closed:
            self.session = aiohttp.ClientSession()
        return self.session

    async def _make_request(
        self,
        method: str,
        endpoint: str,
        params: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        data: Optional[Dict[str, Any]] = None,
        content_type: str = "application/json",
        binary: bool = False
    ) -> Union[Dict[str, Any], str, bytes]:
        session = await self._get_session()
        url = f"{self.base_url}{endpoint}"
        
        if self.api_key:
            headers = headers or {}
            headers["Authorization"] = f"Bearer {self.api_key}"
        
        try:
            async with session.request(method, url, params=params, headers=headers, json=data) as response:
                response.raise_for_status()
                
                # Get the actual content type from the response
                response_content_type = response.headers.get('content-type', '').lower()
                logger.info(f"Making request to {url}")
                
                if binary:
                    return await response.read()
                
                # Get the raw response text first
                raw_response = await response.text()
                
                # Only try to parse as JSON if the content type indicates JSON
                if 'application/json' in response_content_type or 'text/json' in response_content_type:
                    try:
                        # First try to parse the response directly
                        return await response.json()
                    except Exception as e:
                        try:
                            # If that fails, try parsing the raw text as JSON (in case it's double-encoded)
                            return json.loads(raw_response)
                        except Exception as e:
                            logger.error(f"Failed to parse JSON response: {e}")
                            return raw_response
                return raw_response
                
        except aiohttp.ClientError as e:
            logger.error(f"Request failed: {e}")
            raise
        except Exception as e:
            logger.error(f"Unexpected error during API request: {str(e)}")
            raise

    async def close(self):
        """Close the aiohttp session if it exists"""
        if self.session and not self.session.closed:
            await self.session.close()
            self.session = None
            logger.info(f"Closed session for {self.__class__.__name__}")

    def get_service(self, name: str) -> Any:
        """Get a service by name with lazy loading"""
        if name not in self._services:
            self._services[name] = self.service_manager.get_service(name)
        return self._services[name]

    @property
    def file_service(self):
        """Convenience property for file service"""
        return self.get_service('file')
