import os
import json
import time
import threading
from typing import Optional, Any, Dict, Tuple
from collections import OrderedDict
from .interfaces import CacheInterface

class CacheItem:
    """Cache item with value and expiration time."""
    def __init__(self, value: Any, expiration: int):
        self.value = value
        self.expiration = time.time() + expiration
        self.last_access = time.time()

class CacheSystem(CacheInterface):
    """Cache system implementation."""
    
    def __init__(self, base_dir: str, max_cache_size: int = 1000):
        self.base_dir = base_dir
        self.max_cache_size = max_cache_size
        self.cache: Dict[str, Dict[str, CacheItem]] = {}
        self.lock = threading.RLock()
        self._start_cleanup_thread()
    
    def _start_cleanup_thread(self):
        """Start the cleanup thread."""
        def cleanup():        
            while True:
                time.sleep(300)  # 5 minutes
                self._cleanup_expired()
        
        thread = threading.Thread(target=cleanup, daemon=True)
        thread.start()
    
    def _cleanup_expired(self):
        """Clean up expired cache items."""
        with self.lock:
            current_time = time.time()
            
            # Clean up memory cache
            for module_name, module_cache in list(self.cache.items()):
                for key, item in list(module_cache.items()):
                    if item.expiration < current_time:
                        del module_cache[key]
                        # Also delete from file
                        cache_file = self.get_path(module_name, key)
                        if os.path.exists(cache_file):
                            try:
                                os.remove(cache_file)
                            except:
                                pass
                if not module_cache:
                    del self.cache[module_name]
            
            # Clean up file cache
            if os.path.exists(self.base_dir):
                for module_name in os.listdir(self.base_dir):
                    module_dir = os.path.join(self.base_dir, module_name)
                    if os.path.isdir(module_dir):
                        for cache_file in os.listdir(module_dir):
                            cache_path = os.path.join(module_dir, cache_file)
                            if os.path.isfile(cache_path):
                                try:
                                    with open(cache_path, 'r', encoding='utf-8') as f:
                                        data = json.load(f)
                                    if data.get('expiration', 0) < current_time:
                                        os.remove(cache_path)
                                except:
                                    pass
    
    def get_path(self, module_name: str, relative_path: str) -> str:
        """Get the full path for a given module and relative path."""
        module_dir = os.path.join(self.base_dir, module_name)
        os.makedirs(module_dir, exist_ok=True)
        return os.path.join(module_dir, f"{relative_path}.json")
    
    def exists(self, module_name: str, relative_path: str) -> bool:
        """Check if a path exists."""
        return os.path.exists(self.get_path(module_name, relative_path))
    
    def create(self, module_name: str, relative_path: str) -> bool:
        """Create a file or directory."""
        try:
            path = self.get_path(module_name, relative_path)
            os.makedirs(os.path.dirname(path), exist_ok=True)
            with open(path, 'w', encoding='utf-8') as f:
                json.dump({"value": None, "expiration": time.time() + 3600}, f)
            return True
        except:
            return False
    
    def read(self, module_name: str, relative_path: str, encoding: str = 'utf-8') -> Optional[str]:
        """Read content from a file."""
        try:
            with open(self.get_path(module_name, relative_path), 'r', encoding=encoding) as f:
                return f.read()
        except:
            return None
    
    def write(self, module_name: str, relative_path: str, content: str, encoding: str = 'utf-8') -> bool:
        """Write content to a file."""
        try:
            path = self.get_path(module_name, relative_path)
            os.makedirs(os.path.dirname(path), exist_ok=True)
            with open(path, 'w', encoding=encoding) as f:
                f.write(content)
            return True
        except:
            return False
    
    def delete(self, module_name: str, relative_path: str) -> bool:
        """Delete a file or directory."""
        try:
            path = self.get_path(module_name, relative_path)
            if os.path.exists(path):
                os.remove(path)
            # Also remove from memory cache
            with self.lock:
                if module_name in self.cache and relative_path in self.cache[module_name]:
                    del self.cache[module_name][relative_path]
            return True
        except:
            return False
    
    def update(self, module_name: str, relative_path: str, content: str, encoding: str = 'utf-8') -> bool:
        """Update content in a file."""
        return self.write(module_name, relative_path, content, encoding)
    
    def set_cache(self, module_name: str, key: str, value: Any, expiration: int = 3600) -> bool:
        """Set a cache value with expiration."""
        with self.lock:
            # Initialize module cache if not exists
            if module_name not in self.cache:
                self.cache[module_name] = OrderedDict()
            
            # Check cache size
            if len(self.cache[module_name]) >= self.max_cache_size:
                # Remove oldest item
                oldest_key, _ = self.cache[module_name].popitem(last=False)
                # Delete from file
                self.delete(module_name, oldest_key)
            
            # Add new item
            self.cache[module_name][key] = CacheItem(value, expiration)
            # Move to end to mark as most recently used
            self.cache[module_name].move_to_end(key)
            
            # Asynchronously persist to file
            def persist():
                try:
                    path = self.get_path(module_name, key)
                    os.makedirs(os.path.dirname(path), exist_ok=True)
                    with open(path, 'w', encoding='utf-8') as f:
                        json.dump({
                            "value": value,
                            "expiration": time.time() + expiration
                        }, f)
                except:
                    pass
            
            threading.Thread(target=persist, daemon=True).start()
            return True
    
    def get_cache(self, module_name: str, key: str) -> Optional[Any]:
        """Get a cache value."""
        with self.lock:
            if module_name in self.cache and key in self.cache[module_name]:
                item = self.cache[module_name][key]
                # Check expiration
                if item.expiration < time.time():
                    # Remove expired item
                    del self.cache[module_name][key]
                    # Delete from file
                    self.delete(module_name, key)
                    return None
                # Update last access time
                item.last_access = time.time()
                # Move to end to mark as most recently used
                self.cache[module_name].move_to_end(key)
                return item.value
            
            # Try to load from file
            try:
                path = self.get_path(module_name, key)
                if os.path.exists(path):
                    with open(path, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                    if data.get('expiration', 0) > time.time():
                        # Add to memory cache
                        self.set_cache(module_name, key, data['value'], int(data['expiration'] - time.time()))
                        return data['value']
                    else:
                        # Delete expired file
                        os.remove(path)
            except:
                pass
            
            return None
    
    def delete_cache(self, module_name: str, key: str) -> bool:
        """Delete a cache value."""
        return self.delete(module_name, key)
    
    def clear_cache(self, module_name: str) -> bool:
        """Clear all cache for a module."""
        try:
            with self.lock:
                if module_name in self.cache:
                    # Delete all files
                    module_dir = os.path.join(self.base_dir, module_name)
                    if os.path.exists(module_dir):
                        for file in os.listdir(module_dir):
                            file_path = os.path.join(module_dir, file)
                            if os.path.isfile(file_path):
                                os.remove(file_path)
                    # Clear memory cache
                    del self.cache[module_name]
            return True
        except:
            return False
