import shutil
import datetime
import json
from pathlib import Path
from runtime.memory.vector_store import VectorStore
from runtime.memory.project import ProjectMemory
from runtime.memory.conversation import ConversationMemory
from runtime.memory.people import PeopleMemory
from runtime.memory.decision import DecisionMemory
from runtime.memory.outcome import OutcomeMemory

class MemoryManager:
    def __init__(self, data_dir: Path):
        self.data_dir = data_dir
        self.vector_store = VectorStore(data_dir / 'memory' / 'vectors.db')
        self.project_memory = ProjectMemory(self.vector_store, data_dir)
        self.conversation_memory = ConversationMemory(self.vector_store, data_dir)
        self.people_memory = PeopleMemory(self.vector_store, data_dir)
        self.decision_memory = DecisionMemory(self.vector_store, data_dir)
        self.outcome_memory = OutcomeMemory(self.vector_store, data_dir)
        self._tracker = None

    def search_all(self, query: str, top_k: int = 10) -> list[dict]:
        res = self.vector_store.search(query, top_k=top_k*2)
        seen = set()
        out = []
        for r in res:
            if r['id'] not in seen:
                seen.add(r['id'])
                out.append({
                    'id': r['id'], 'content': r['content'], 'score': r['score'],
                    'namespace': r['namespace'], 'type': r['metadata'].get('type'), 'created_at': r['created_at']
                })
        return out[:top_k]

    def context_for_prompt(self, query: str, max_tokens: int = 2000) -> str:
        ctx = ""
        ctx += self.project_memory.context_for_prompt("", query, max_tokens=int(max_tokens * 0.3))
        ctx += self.conversation_memory.context_for_prompt(query, max_tokens=int(max_tokens * 0.3))
        ctx += self.decision_memory.context_for_prompt(query, max_tokens=int(max_tokens * 0.2))
        ctx += self.outcome_memory.context_for_prompt(query, max_tokens=int(max_tokens * 0.2))
        return ctx

    def remember(self, content: str, memory_type: str = 'general', metadata: dict | None = None) -> str:
        if memory_type == 'project':
            return self.vector_store.add(content, metadata, namespace='project')
        elif memory_type == 'conversation':
            return self.vector_store.add(content, metadata, namespace='conversation')
        elif memory_type == 'decision':
            return self.vector_store.add(content, metadata, namespace='decisions')
        elif memory_type == 'outcome':
            return self.vector_store.add(content, metadata, namespace='outcomes')
        elif memory_type == 'person':
            return self.vector_store.add(content, metadata, namespace='people')
        else:
            return self.vector_store.add(content, metadata, namespace='general')

    def recall(self, query: str, memory_type: str | None = None, top_k: int = 5) -> list[dict]:
        if memory_type:
            ns_map = {'project': 'project', 'conversation': 'conversation', 'decision': 'decisions', 'outcome': 'outcomes', 'person': 'people'}
            ns = ns_map.get(memory_type, memory_type)
            res = self.vector_store.search(query, top_k=top_k, namespace=ns)
            return [{'id': r['id'], 'content': r['content'], 'score': r['score'], 'namespace': r['namespace'], 'type': r['metadata'].get('type'), 'created_at': r['created_at']} for r in res]
        else:
            return self.search_all(query, top_k)

    def forget(self, doc_id: str) -> bool:
        return self.vector_store.delete(doc_id)

    def forget_about(self, query: str, namespace: str | None = None, threshold: float = 0.9) -> int:
        res = self.vector_store.search(query, top_k=100, namespace=namespace, min_score=threshold)
        ids = [r['id'] for r in res]
        if ids:
            self.vector_store.batch_delete(ids)
        return len(ids)

    def export_all(self) -> dict:
        data = {
            'project': self.vector_store.export_namespace('project'),
            'conversation': self.vector_store.export_namespace('conversation'),
            'decisions': self.vector_store.export_namespace('decisions'),
            'outcomes': self.vector_store.export_namespace('outcomes'),
            'people': self.vector_store.export_namespace('people'),
            'general': self.vector_store.export_namespace('general')
        }
        data['metadata'] = {
            'export_date': datetime.datetime.now(datetime.timezone.utc).isoformat(),
            'version': '1.0',
            'total_entries': sum(len(v) for k, v in data.items() if k != 'metadata')
        }
        return data

    def import_all(self, data: dict) -> dict:
        counts = {}
        for ns in ['project', 'conversation', 'decisions', 'outcomes', 'people', 'general']:
            if ns in data and isinstance(data[ns], list):
                counts[ns] = self.vector_store.import_namespace(ns, data[ns])
        return counts

    def get_stats(self) -> dict:
        v_stats = self.vector_store.stats()
        by_ns = {}
        for ns in v_stats['namespaces']:
            by_ns[ns] = self.vector_store.count(ns)
        return {
            'total_memories': v_stats['total_vectors'],
            'by_namespace': by_ns,
            'db_size_bytes': v_stats['db_size_bytes'],
            'oldest_memory': v_stats['oldest_entry'],
            'newest_memory': v_stats['newest_entry']
        }

    def prune_all(self, older_than_days: int = 180) -> dict:
        counts = {
            'project': self.vector_store.prune_old(older_than_days, 'project'),
            'conversation': self.conversation_memory.prune(older_than_days),
            'decisions': self.vector_store.prune_old(older_than_days, 'decisions'),
            'outcomes': self.outcome_memory.prune(older_than_days),
            'people': self.vector_store.prune_old(older_than_days, 'people'),
            'general': self.vector_store.prune_old(older_than_days, 'general')
        }
        return counts

    def auto_memorize_session(self, session_id: str, messages: list[dict], models_used: list[str] | None = None) -> dict:
        res = self.conversation_memory.auto_memorize(session_id, messages)
        return res

    def health_check(self) -> dict:
        v_ok = False
        try:
            self.vector_store.count()
            v_ok = True
        except: pass
        
        e_ok = False
        try:
            emb = self.vector_store._get_embedding("test")
            e_ok = len(emb) > 0
        except: pass
        
        df = 0.0
        try:
            st = shutil.disk_usage(self.data_dir)
            df = st.free / (1024 * 1024)
        except: pass
        
        return {'vector_store_ok': v_ok, 'embeddings_available': e_ok, 'disk_free_mb': df}

    def reindex_all(self) -> int:
        count = 0
        for ns in self.vector_store.list_namespaces():
            count += self.vector_store.reindex(ns)
        return count

    def deduplicate_all(self) -> int:
        count = 0
        for ns in self.vector_store.list_namespaces():
            count += self.vector_store.deduplicate(ns)
        return count

    def close(self) -> None:
        self.vector_store.close()

    def get_memory_timeline(self, days: int = 30) -> list[dict]:
        cutoff = (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=days)).isoformat()
        c = self.vector_store.conn.cursor()
        c.execute('SELECT id, namespace, metadata, content, created_at FROM vectors WHERE created_at >= ? ORDER BY created_at ASC', (cutoff,))
        return [{
            'id': r['id'], 'namespace': r['namespace'], 'type': json.loads(r['metadata'] or '{}').get('type'),
            'content_preview': r['content'][:100], 'created_at': r['created_at']
        } for r in c.fetchall()]

    def smart_remember(self, text: str) -> dict:
        ns = 'conversation'
        t = 'general'
        text_lower = text.lower()
        if 'decided' in text_lower or 'decision' in text_lower:
            ns = 'decisions'
            t = 'decision'
        elif '/' in text or '\\' in text or ' file' in text_lower:
            ns = 'project'
            t = 'note'
        else:
            words = text.split()
            if any(w[0].isupper() and w.isalpha() and w.lower() not in ['i', 'the', 'a', 'it', 'decided', 'decision'] for w in words):
                ns = 'people'
                t = 'person'
        
        doc_id = self.vector_store.add(text, metadata={'type': t}, namespace=ns)
        return {'id': doc_id, 'detected_type': t, 'namespace': ns}

    def connect_learning_system(self, tracker: 'UsageTracker') -> None:
        self._tracker = tracker

    def backup(self, backup_dir: Path) -> str:
        backup_dir.mkdir(parents=True, exist_ok=True)
        ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        dest = backup_dir / f'vectors_{ts}.db'
        shutil.copy2(self.vector_store.db_path, dest)
        return str(dest)
