"""
Unified config loader for LambdaRAG scripts.
All scripts import paths from here instead of hardcoding.

Usage:
    from config import cfg
    print(cfg.raw_dir)       # Path to raw files
    print(cfg.processed_dir) # Path to processed text
    print(cfg.wiki_dir)      # Path to wiki
    print(cfg.base_dir)      # Knowledge base directory
"""
import os
import sys
from pathlib import Path

import yaml


class Config:
    """Loads paths from agent-config.yml + environment variables."""

    def __init__(self):
        self._cfg = {}
        self._load()

    def _find_agent_config(self):
        """Search for agent-config.yml in standard locations"""
        candidates = [
            os.environ.get('AGENT_CONFIG', ''),
            # Relative to this script: scripts/ -> parent = agent dir
            str(Path(__file__).parent.parent / 'agent-config.yml'),
            # Docker paths
            '/app/agentexample/qaagent67lambda/agent-config.yml',
            '/app/agentexample/qaagent67/agent-config.yml',
        ]
        for p in candidates:
            if p and Path(p).is_file():
                return Path(p)
        return None

    def _find_instance_config(self):
        """Search for instance.yml.

        Resolution order:
        1. INSTANCE_CONFIG env var
        2. {baseDir}/instance.yml (after agent-config.yml is loaded)
        3. instance.yml in the directory containing this config.py file
           (lets build scripts auto-detect domain when run from a domain dir)
        """
        here = Path(__file__).parent
        base_dir = (self._cfg.get("knowledge", {}) or {}).get("baseDir", "")
        candidates = [
            os.environ.get("INSTANCE_CONFIG", ""),
            str(Path(base_dir) / "instance.yml") if base_dir else "",
            str(here / "instance.yml"),
        ]
        for p in candidates:
            if p and Path(p).is_file():
                return Path(p)
        return None

    def _load(self):
        config_path = self._find_agent_config()
        if config_path:
            with open(config_path) as f:
                self._cfg = yaml.safe_load(f) or {}

        # Load instance.yml (overrides agent-config.yml)
        instance_path = self._find_instance_config()
        if instance_path and Path(instance_path).is_file():
            with open(instance_path) as f:
                instance = yaml.safe_load(f) or {}
            # Deep merge: instance overrides agent config
            self._cfg = self._deep_merge(self._cfg, instance)

    def _deep_merge(self, base, override):
        import copy
        result = copy.deepcopy(base)
        for k, v in override.items():
            if k in result and isinstance(result[k], dict) and isinstance(v, dict):
                result[k] = self._deep_merge(result[k], v)
            else:
                result[k] = copy.deepcopy(v)
        return result

    # ── Knowledge paths ──
    @property
    def base_dir(self):
        return Path(
            self._cfg.get('knowledge', {}).get('baseDir', '')
            or os.environ.get('KNOWLEDGE_DIR', '')
            or str(Path(__file__).parent.parent / 'knowledge')
        )

    @property
    def raw_dir(self):
        return Path(
            self._cfg.get('knowledge', {}).get('rawDir', '')
            or str(self.base_dir / 'raw')
        )

    @property
    def processed_dir(self):
        return Path(
            self._cfg.get('knowledge', {}).get('processedDir', '')
            or str(self.base_dir / 'processed')
        )

    @property
    def index_file(self):
        return Path(
            self._cfg.get('knowledge', {}).get('indexFile', '')
            or str(self.base_dir / 'rag_index.json')
        )

    @property
    def keyword_file(self):
        return Path(
            self._cfg.get('knowledge', {}).get('keywordFile', '')
            or str(self.base_dir / 'rag_index.keywords.json')
        )

    @property
    def vector_index(self):
        return Path(
            self._cfg.get('knowledge', {}).get('vectorIndex', '')
            or str(self.base_dir / 'rag_vectors_v2.npy')
        )

    @property
    def vector_meta(self):
        return Path(
            self._cfg.get('knowledge', {}).get('vectorMeta', '')
            or str(self.base_dir / 'rag_vectors_meta_v2.pkl')
        )

    # ── Wiki paths ──
    @property
    def wiki_dir(self):
        return Path(
            self._cfg.get('wiki', {}).get('dir', '')
            or os.environ.get('WIKI_DIR', '')
            or str(Path(__file__).parent.parent / 'wiki')
        )

    @property
    def relations_file(self):
        return Path(
            self._cfg.get('wiki', {}).get('relationsFile', '')
            or str(self.wiki_dir / 'relations.json')
        )

    # ── Embedding config ──
    @property
    def embed_url(self):
        return (
            self._cfg.get('retrieval', {}).get('skills', {}).get('embedding', {}).get('baseUrl', '')
            or os.environ.get('OLLAMA_EMBED_URL', 'http://127.0.0.1:11435/api/embed')
        )

    @property
    def embed_model(self):
        return (
            self._cfg.get('retrieval', {}).get('skills', {}).get('embedding', {}).get('model', '')
            or os.environ.get('EMBED_MODEL', 'bge-m3')
        )

    # ── LLM config ──
    @property
    def vllm_url(self):
        return os.environ.get('VLLM_URL', 'http://127.0.0.1:8000/v1/chat/completions')

    @property
    def llm_model(self):
        return self._cfg.get('model', {}).get('name', 'qwen2.5-32b')

    # ── Retrieval weights ──
    @property
    def weight_profiles(self):
        return self._cfg.get('retrieval', {}).get('weights', {
            'fact':      {'bm25': 1.0, 'vector': 0.5, 'graph': 0.1, 'wiki': 0.1},
            'compare':   {'bm25': 0.5, 'vector': 0.8, 'graph': 0.4, 'wiki': 0.7},
            'relation':  {'bm25': 0.5, 'vector': 0.3, 'graph': 0.7, 'wiki': 0.8},
            'synthesis': {'bm25': 0.6, 'vector': 0.5, 'graph': 0.5, 'wiki': 0.6},
            'temporal':  {'bm25': 1.0, 'vector': 0.2, 'graph': 0.05, 'wiki': 0.2},
        })

    @property
    def default_weights(self):
        return self._cfg.get('retrieval', {}).get('weights', {}).get('default',
            {'bm25': 0.7, 'vector': 0.5, 'graph': 0.3, 'wiki': 0.3})

    @property
    def rrf_k(self):
        return self._cfg.get('retrieval', {}).get('orchestration', {}).get('rrfK', 60)

    # ── RAG params ──
    @property
    def chunk_size(self):
        return self._cfg.get('rag', {}).get('chunkSize', 512)

    @property
    def chunk_overlap(self):
        return self._cfg.get('rag', {}).get('chunkOverlap', 64)

    @property
    def top_k(self):
        return self._cfg.get('rag', {}).get('topK', 5)

    # ── Workspace ──
    @property
    def workspace_dir(self):
        return Path(__file__).parent.parent / 'workspace'

    # ── Full config dict ──
    @property
    def raw(self):
        return self._cfg

    def __repr__(self):
        return (f"Config(base_dir={self.base_dir}, raw_dir={self.raw_dir}, "
                f"wiki_dir={self.wiki_dir}, embed_url={self.embed_url})")


# Singleton
cfg = Config()

if __name__ == '__main__':
    print(cfg)
    print(f"\n  base_dir:      {cfg.base_dir}")
    print(f"  raw_dir:       {cfg.raw_dir}")
    print(f"  processed_dir: {cfg.processed_dir}")
    print(f"  index_file:    {cfg.index_file}")
    print(f"  wiki_dir:      {cfg.wiki_dir}")
    print(f"  relations:     {cfg.relations_file}")
    print(f"  embed_url:     {cfg.embed_url}")
    print(f"  vllm_url:      {cfg.vllm_url}")
    print(f"  weights:       {cfg.weight_profiles}")
    print(f"  chunk_size:    {cfg.chunk_size}")
    print(f"  workspace:     {cfg.workspace_dir}")
