from typing import Dict, Optional, List, Any
from dataclasses import dataclass, asdict
import threading
from datetime import datetime
from collections import defaultdict, deque
import json
from pathlib import Path

from ..base_processor import ProcessedResult


@dataclass
class StorageEntry:
    result: ProcessedResult
    stored_at: float


class DataStorage:
    def __init__(self, max_entries_per_processor: int = 100, enable_persistence: bool = False, storage_dir: Optional[Path] = None):
        self.max_entries = max_entries_per_processor
        self.enable_persistence = enable_persistence
        self.storage_dir = storage_dir
        self._data: Dict[str, deque] = defaultdict(lambda: deque(maxlen=self.max_entries))
        self._lock = threading.RLock()

        if self.enable_persistence and self.storage_dir:
            self.storage_dir.mkdir(parents=True, exist_ok=True)

    def store_result(self, processor_id: str, result: ProcessedResult):
        with self._lock:
            entry = StorageEntry(
                result=result,
                stored_at=datetime.now().timestamp()
            )

            self._data[processor_id].append(entry)

            if self.enable_persistence:
                self._persist_entry(processor_id, entry)

    def get_latest_result(self, processor_id: str) -> Optional[ProcessedResult]:
        with self._lock:
            processor_data = self._data.get(processor_id)
            if processor_data:
                return processor_data[-1].result
            return None

    def get_results_history(self, processor_id: str, limit: Optional[int] = None) -> List[ProcessedResult]:
        with self._lock:
            processor_data = self._data.get(processor_id, deque())
            entries = list(processor_data)

            if limit:
                entries = entries[-limit:]

            return [entry.result for entry in entries]

    def get_all_latest_results(self) -> Dict[str, ProcessedResult]:
        results = {}
        with self._lock:
            for processor_id in self._data:
                latest = self.get_latest_result(processor_id)
                if latest:
                    results[processor_id] = latest
        return results

    def clear_processor_data(self, processor_id: str):
        with self._lock:
            if processor_id in self._data:
                self._data[processor_id].clear()

    def get_processor_count(self, processor_id: str) -> int:
        with self._lock:
            return len(self._data.get(processor_id, deque()))

    def get_storage_stats(self) -> Dict[str, Any]:
        with self._lock:
            stats = {
                'total_processors': len(self._data),
                'max_entries_per_processor': self.max_entries,
                'enable_persistence': self.enable_persistence,
                'processors': {}
            }

            for processor_id, entries in self._data.items():
                stats['processors'][processor_id] = {
                    'count': len(entries),
                    'oldest_entry': entries[0].stored_at if entries else None,
                    'latest_entry': entries[-1].stored_at if entries else None
                }

        return stats

    def _persist_entry(self, processor_id: str, entry: StorageEntry):
        if not self.storage_dir:
            return

        processor_dir = self.storage_dir / processor_id
        processor_dir.mkdir(exist_ok=True)

        timestamp = datetime.fromtimestamp(entry.stored_at).strftime('%Y%m%d_%H%M%S_%f')
        filename = f"{timestamp}.json"
        filepath = processor_dir / filename

        try:
            data = {
                'stored_at': entry.stored_at,
                'processor_id': entry.result.processor_id,
                'timestamp': entry.result.timestamp,
                'data': entry.result.data,
                'plotly_config': entry.result.plotly_config,
                'ui_parameters': [asdict(param) for param in entry.result.ui_parameters],
                'metadata': entry.result.metadata
            }

            with open(filepath, 'w') as f:
                json.dump(data, f, indent=2, default=str)

        except Exception as e:
            # Log error but don't fail the storage operation
            pass

    def load_persisted_data(self, processor_id: str, limit: Optional[int] = None) -> List[ProcessedResult]:
        if not self.enable_persistence or not self.storage_dir:
            return []

        processor_dir = self.storage_dir / processor_id
        if not processor_dir.exists():
            return []

        results = []
        json_files = sorted(processor_dir.glob('*.json'))

        if limit:
            json_files = json_files[-limit:]

        for filepath in json_files:
            try:
                with open(filepath, 'r') as f:
                    data = json.load(f)

                # Reconstruct UIParameter objects
                ui_parameters = []
                for param_data in data.get('ui_parameters', []):
                    from ..base_processor import UIParameter
                    ui_parameters.append(UIParameter(**param_data))

                result = ProcessedResult(
                    processor_id=data['processor_id'],
                    timestamp=data['timestamp'],
                    data=data['data'],
                    plotly_config=data['plotly_config'],
                    ui_parameters=ui_parameters,
                    metadata=data['metadata']
                )
                results.append(result)

            except Exception as e:
                # Skip corrupted files
                continue

        return results