"""Resource Manager for fetching and caching external information."""
import json
import os
import hashlib
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Optional, Any, Union
import logging
import aiohttp
import asyncio
from dataclasses import dataclass, asdict

logger = logging.getLogger(__name__)

@dataclass
class Resource:
    """Represents an external resource with metadata."""
    url: str
    content: str
    content_type: str
    source: str
    tags: List[str]
    fetched_at: str
    expires_at: str
    cache_key: str

class ResourceManager:
    """Manages external resources and their caching."""
    
    def __init__(self, workspace_root: str, cache_dir: str = '.cursor/cache'):
        """Initialize the Resource Manager."""
        self.workspace_root = Path(workspace_root)
        self.cache_dir = self.workspace_root / cache_dir
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.cache_index_path = self.cache_dir / 'resource_index.json'
        self.cache_index: Dict[str, Dict[str, Any]] = {}
        self.load_cache_index()

    def load_cache_index(self):
        """Load the cache index from disk."""
        if self.cache_index_path.exists():
            try:
                with open(self.cache_index_path, 'r') as f:
                    self.cache_index = json.load(f)
            except json.JSONDecodeError:
                logger.error("Error loading cache index, creating new one")
                self.cache_index = {}
        else:
            self.cache_index = {}

    def save_cache_index(self):
        """Save the cache index to disk."""
        with open(self.cache_index_path, 'w') as f:
            json.dump(self.cache_index, f, indent=2)

    def _generate_cache_key(self, url: str, params: Optional[Dict] = None) -> str:
        """Generate a unique cache key for a resource."""
        key_parts = [url]
        if params:
            key_parts.extend(f"{k}={v}" for k, v in sorted(params.items()))
        key_string = "|".join(key_parts)
        return hashlib.sha256(key_string.encode()).hexdigest()

    async def fetch_resource(self, url: str, source: str, 
                           tags: List[str] = None, 
                           cache_ttl: int = 3600,
                           params: Optional[Dict] = None) -> Resource:
        """Fetch a resource from URL or cache."""
        cache_key = self._generate_cache_key(url, params)
        cached = self._get_cached_resource(cache_key)
        
        if cached and not self._is_expired(cached):
            logger.info(f"Using cached resource: {url}")
            return Resource(**cached)
            
        async with aiohttp.ClientSession() as session:
            try:
                async with session.get(url, params=params) as response:
                    response.raise_for_status()
                    content = await response.text()
                    content_type = response.headers.get('content-type', 'text/plain')
                    
                    now = datetime.now()
                    resource = Resource(
                        url=url,
                        content=content,
                        content_type=content_type,
                        source=source,
                        tags=tags or [],
                        fetched_at=now.isoformat(),
                        expires_at=(now + timedelta(seconds=cache_ttl)).isoformat(),
                        cache_key=cache_key
                    )
                    
                    self._cache_resource(resource)
                    return resource
                    
            except Exception as e:
                logger.error(f"Error fetching resource {url}: {e}")
                if cached:
                    logger.info("Using expired cache as fallback")
                    return Resource(**cached)
                raise

    def _get_cached_resource(self, cache_key: str) -> Optional[Dict[str, Any]]:
        """Get a resource from cache if it exists."""
        return self.cache_index.get(cache_key)

    def _is_expired(self, resource: Dict[str, Any]) -> bool:
        """Check if a cached resource is expired."""
        expires_at = datetime.fromisoformat(resource['expires_at'])
        return datetime.now() > expires_at

    def _cache_resource(self, resource: Resource):
        """Cache a resource."""
        self.cache_index[resource.cache_key] = asdict(resource)
        
        # Write content to separate file to keep index small
        content_path = self.cache_dir / f"{resource.cache_key}.content"
        with open(content_path, 'w', encoding='utf-8') as f:
            f.write(resource.content)
            
        # Store path instead of content in index
        resource_dict = asdict(resource)
        resource_dict['content'] = str(content_path)
        self.cache_index[resource.cache_key] = resource_dict
        self.save_cache_index()

    def get_resources_by_tag(self, tag: str) -> List[Resource]:
        """Get all cached resources with a specific tag."""
        resources = []
        for resource_dict in self.cache_index.values():
            if tag in resource_dict['tags']:
                # Load content from file
                content_path = Path(resource_dict['content'])
                if content_path.exists():
                    with open(content_path, 'r', encoding='utf-8') as f:
                        resource_dict['content'] = f.read()
                resources.append(Resource(**resource_dict))
        return resources

    def get_resources_by_source(self, source: str) -> List[Resource]:
        """Get all cached resources from a specific source."""
        resources = []
        for resource_dict in self.cache_index.values():
            if resource_dict['source'] == source:
                # Load content from file
                content_path = Path(resource_dict['content'])
                if content_path.exists():
                    with open(content_path, 'r', encoding='utf-8') as f:
                        resource_dict['content'] = f.read()
                resources.append(Resource(**resource_dict))
        return resources

    def clear_expired_cache(self):
        """Remove expired resources from cache."""
        expired_keys = []
        for cache_key, resource in self.cache_index.items():
            if self._is_expired(resource):
                expired_keys.append(cache_key)
                # Remove content file
                content_path = Path(resource['content'])
                if content_path.exists():
                    content_path.unlink()
                    
        for key in expired_keys:
            del self.cache_index[key]
            
        if expired_keys:
            self.save_cache_index()
            logger.info(f"Cleared {len(expired_keys)} expired resources from cache")

    def clear_cache(self):
        """Clear all cached resources."""
        # Remove all content files
        for resource in self.cache_index.values():
            content_path = Path(resource['content'])
            if content_path.exists():
                content_path.unlink()
                
        self.cache_index = {}
        self.save_cache_index()
        logger.info("Cleared resource cache") 