import json
import os
from pathlib import Path
from runtime.memory.vector_store import VectorStore

class ProjectMemory:
    def __init__(self, vector_store: VectorStore, data_dir: Path):
        self.vector_store = vector_store
        self.data_dir = data_dir
        self.namespace = 'project'
        (self.data_dir / 'memory').mkdir(parents=True, exist_ok=True)

    def detect_project_type(self, project_path: str) -> dict:
        p = Path(project_path)
        markers = {
            'pyproject.toml': ('python', 'poetry/pip'),
            'requirements.txt': ('python', 'pip'),
            'package.json': ('javascript/typescript', 'npm/yarn'),
            'Cargo.toml': ('rust', 'cargo'),
            'go.mod': ('go', 'go modules'),
            'pom.xml': ('java', 'maven'),
            'Makefile': ('c/c++', 'make')
        }
        detected = []
        project_type = 'unknown'
        language = 'unknown'
        build_system = 'unknown'
        for f, (lang, build) in markers.items():
            if (p / f).exists():
                detected.append(f)
                if project_type == 'unknown':
                    project_type = lang
                    language = lang
                    build_system = build
        return {
            'project_type': project_type,
            'language': language,
            'build_system': build_system,
            'detected_files': detected
        }

    def index_structure(self, project_path: str) -> dict:
        p = Path(project_path)
        excludes = {'node_modules', '.git', '__pycache__', '.venv', 'dist', 'build'}
        file_count = 0
        dir_count = 0
        langs = set()
        
        for root, dirs, files in os.walk(p):
            dirs[:] = [d for d in dirs if d not in excludes]
            dir_count += 1
            for f in files:
                file_count += 1
                ext = Path(f).suffix
                if ext:
                    langs.add(ext)
                fp = str(Path(root) / f)
                self.vector_store.add(
                    content=f"File: {fp}",
                    metadata={'type': 'file_structure', 'project': project_path},
                    namespace=self.namespace
                )
        return {'file_count': file_count, 'dir_count': dir_count, 'languages_detected': list(langs)}

    def remember_convention(self, project_path: str, convention: str, source: str = 'user') -> str:
        return self.vector_store.add(
            content=convention,
            metadata={'type': 'convention', 'project': project_path, 'source': source},
            namespace=self.namespace
        )

    def get_conventions(self, project_path: str) -> list[dict]:
        res = self.vector_store.search_by_metadata(
            {'type': 'convention', 'project': project_path},
            namespace=self.namespace,
            limit=100
        )
        return [{'id': r['id'], 'convention': r['content'], 'source': r['metadata'].get('source'), 'created_at': r['created_at']} for r in res]

    def index_readme(self, project_path: str) -> str | None:
        p = Path(project_path)
        for name in ['README.md', 'README.rst', 'README.txt']:
            rp = p / name
            if rp.exists():
                text = rp.read_text(errors='ignore')
                chunks = text.split('\n## ')
                for i, chunk in enumerate(chunks):
                    prefix = '## ' if i > 0 else ''
                    self.vector_store.add(
                        content=prefix + chunk,
                        metadata={'type': 'readme', 'project': project_path, 'section': f'chunk_{i}'},
                        namespace=self.namespace
                    )
                return str(rp)
        return None

    def remember_dependency(self, project_path: str, name: str, version: str, purpose: str) -> str:
        return self.vector_store.add(
            content=purpose,
            metadata={'type': 'dependency', 'project': project_path, 'name': name, 'version': version},
            namespace=self.namespace
        )

    def get_dependencies(self, project_path: str) -> list[dict]:
        res = self.vector_store.search_by_metadata(
            {'type': 'dependency', 'project': project_path},
            namespace=self.namespace,
            limit=100
        )
        return [{'name': r['metadata']['name'], 'version': r['metadata']['version'], 'purpose': r['content']} for r in res]

    def auto_detect_conventions(self, project_path: str) -> list[str]:
        convs = ["Uses standard formatting"]
        res = []
        for c in convs:
            self.remember_convention(project_path, c, source='auto')
            res.append(c)
        return res

    def track_git_info(self, project_path: str) -> dict | None:
        p = Path(project_path) / '.git'
        if not p.exists():
            return None
        
        branch = "unknown"
        try:
            head = (p / 'HEAD').read_text().strip()
            if head.startswith('ref: '):
                branch = head.split('/')[-1]
        except Exception:
            pass
            
        remote_url = "unknown"
        try:
            config = (p / 'config').read_text()
            for line in config.splitlines():
                if line.strip().startswith('url = '):
                    remote_url = line.strip().split('=', 1)[1].strip()
                    break
        except Exception:
            pass
            
        info = {'branch': branch, 'remote_url': remote_url, 'approx_commits': 0}
        self.vector_store.add(
            content=json.dumps(info),
            metadata={'type': 'git_info', 'project': project_path},
            namespace=self.namespace
        )
        return info

    def search_project(self, project_path: str, query: str, top_k: int = 5) -> list[dict]:
        res = self.vector_store.search(query, top_k=top_k * 2, namespace=self.namespace)
        filtered = [r for r in res if r['metadata'].get('project') == project_path][:top_k]
        return [{'content': r['content'], 'type': r['metadata'].get('type'), 'score': r['score']} for r in filtered]

    def remember_architecture_note(self, project_path: str, note: str, component: str = '') -> str:
        return self.vector_store.add(
            content=note,
            metadata={'type': 'architecture', 'project': project_path, 'component': component},
            namespace=self.namespace
        )

    def get_architecture_notes(self, project_path: str) -> list[dict]:
        res = self.vector_store.search_by_metadata(
            {'type': 'architecture', 'project': project_path},
            namespace=self.namespace,
            limit=100
        )
        return [{'id': r['id'], 'note': r['content'], 'component': r['metadata'].get('component'), 'created_at': r['created_at']} for r in res]

    def index_config_files(self, project_path: str) -> int:
        configs = ['.env.example', '.eslintrc', 'tsconfig.json', 'pyproject.toml', 'setup.cfg', 'ruff.toml']
        p = Path(project_path)
        count = 0
        for c in configs:
            cp = p / c
            if cp.exists():
                text = cp.read_text(errors='ignore')
                self.vector_store.add(
                    content=text,
                    metadata={'type': 'config', 'project': project_path, 'filename': c},
                    namespace=self.namespace
                )
                count += 1
        return count

    def remember_decision(self, project_path: str, decision: str, rationale: str, alternatives: list[str] | None = None) -> str:
        content = f"Decision: {decision}\nRationale: {rationale}"
        return self.vector_store.add(
            content=content,
            metadata={'type': 'project_decision', 'project': project_path, 'alternatives': alternatives or []},
            namespace=self.namespace
        )

    def get_project_summary(self, project_path: str) -> dict:
        ptype = self.detect_project_type(project_path)
        convs = self.get_conventions(project_path)
        arch = self.get_architecture_notes(project_path)
        deps = self.get_dependencies(project_path)
        return {
            'project_type': ptype,
            'convention_count': len(convs),
            'architecture_note_count': len(arch),
            'dependency_count': len(deps)
        }

    def forget_project(self, project_path: str) -> int:
        res = self.vector_store.search_by_metadata({'project': project_path}, namespace=self.namespace, limit=10000)
        ids = [r['id'] for r in res]
        if ids:
            self.vector_store.batch_delete(ids)
        return len(ids)

    def diff_since_last_index(self, project_path: str) -> dict:
        return {'added_files': [], 'removed_files': [], 'modified_count': 0}

    def auto_index(self, project_path: str) -> dict:
        ptype = self.detect_project_type(project_path)
        struct = self.index_structure(project_path)
        convs = self.auto_detect_conventions(project_path)
        readme = self.index_readme(project_path)
        configs = self.index_config_files(project_path)
        git = self.track_git_info(project_path)
        return {
            'project_type': ptype,
            'structure': struct,
            'conventions': convs,
            'readme_indexed': readme,
            'configs_indexed': configs,
            'git': git
        }

    def list_projects(self) -> list[str]:
        c = self.vector_store.conn.cursor()
        c.execute('SELECT metadata FROM vectors WHERE namespace = ?', (self.namespace,))
        projs = set()
        for row in c.fetchall():
            meta = json.loads(row['metadata'] or '{}')
            if 'project' in meta:
                projs.add(meta['project'])
        return sorted(list(projs))

    def context_for_prompt(self, project_path: str, query: str, max_tokens: int = 1000) -> str:
        res = self.search_project(project_path, query, top_k=10)
        max_chars = max_tokens * 4
        context = "[Project Context]\n"
        for r in res:
            add = f"--- {r['type']} ---\n{r['content']}\n\n"
            if len(context) + len(add) > max_chars:
                break
            context += add
        context += "[End Project Context]\n"
        return context

    def remember_file_purpose(self, project_path: str, file_path: str, purpose: str) -> str:
        return self.vector_store.add(
            content=purpose,
            metadata={'type': 'file_purpose', 'project': project_path, 'file': file_path},
            namespace=self.namespace
        )

    def get_file_purpose(self, project_path: str, file_path: str) -> str | None:
        res = self.vector_store.search_by_metadata(
            {'type': 'file_purpose', 'project': project_path, 'file': file_path},
            namespace=self.namespace,
            limit=1
        )
        return res[0]['content'] if res else None

    def export_project_memory(self, project_path: str) -> dict:
        res = self.vector_store.search_by_metadata({'project': project_path}, namespace=self.namespace, limit=10000)
        data = {}
        for r in res:
            t = r['metadata'].get('type', 'other')
            if t not in data:
                data[t] = []
            data[t].append(r)
        return data

    def import_project_memory(self, project_path: str, data: dict) -> int:
        count = 0
        for t, items in data.items():
            for item in items:
                self.vector_store.add(
                    content=item['content'],
                    metadata=item['metadata'],
                    namespace=self.namespace
                )
                count += 1
        return count
