"""Shared utilities for path resolution, data loading, and optional integrations."""

import json
import os
from importlib import resources as importlib_resources
from pathlib import Path
from typing import Any, Dict, List, Optional


# ---------------------------------------------------------------------------
# Path Resolution
# ---------------------------------------------------------------------------

def get_package_data_path(*parts: str) -> Path:
    """Resolve path to a bundled data file inside the installed package."""
    ref = importlib_resources.files("mentorr") / "data"
    for part in parts:
        ref = ref / part
    # Convert Traversable to a real Path for file operations
    return Path(str(ref))


def get_user_data_dir() -> Path:
    """Resolve the user data directory, creating subdirs as needed.

    Default: ~/.mentorr/
    Override: set MENTORR_DATA_DIR environment variable.
    """
    base = Path(os.environ.get("MENTORR_DATA_DIR", Path.home() / ".mentorr"))
    for subdir in ["projects", "processed", "raw", "lab_inbox", "lab_processed"]:
        (base / subdir).mkdir(parents=True, exist_ok=True)
    return base


# ---------------------------------------------------------------------------
# Data Loaders (bundled static data)
# ---------------------------------------------------------------------------

def load_physics_db() -> List[Dict[str, Any]]:
    """Load the wood physics database from bundled data."""
    path = get_package_data_path("knowledge", "wood_physics.json")
    if path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    return []


def load_template(template_name: str) -> Optional[List[Dict[str, Any]]]:
    """Load a build template by name (e.g. 'electric_6_string')."""
    path = get_package_data_path("knowledge", "templates", f"{template_name}.json")
    if path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    return None


def load_reference(skill_name: str, filename: str) -> str:
    """Read a markdown reference file from bundled package data."""
    path = get_package_data_path("references", skill_name, filename)
    if path.exists():
        return path.read_text(encoding="utf-8")
    return f"Reference file '{filename}' not found for skill '{skill_name}'."


def load_json_data(skill_name: str, filename: str) -> List[Dict[str, Any]]:
    """Load a JSON data file from bundled skills data."""
    path = get_package_data_path("skills", skill_name, filename)
    if path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    return []


# ---------------------------------------------------------------------------
# OpenAI Integration (optional)
# ---------------------------------------------------------------------------

_openai_available = False
try:
    from openai import OpenAI
    _openai_available = True
except ImportError:
    pass


def get_openai_client() -> Optional[Any]:
    """Return an OpenAI client if the package and API key are available."""
    if not _openai_available:
        return None
    api_key = os.environ.get("OPENAI_API_KEY")
    if not api_key:
        return None
    return OpenAI(api_key=api_key)


def get_embedding(text: str) -> List[float]:
    """Get an embedding vector for text using OpenAI. Returns [] if unavailable."""
    client = get_openai_client()
    if client is None:
        return []
    model = os.environ.get("OPENAI_EMBEDDING_MODEL", "text-embedding-3-small")
    try:
        response = client.embeddings.create(input=text, model=model)
        return response.data[0].embedding
    except Exception:
        return []


def cosine_similarity(a: List[float], b: List[float]) -> float:
    """Compute cosine similarity between two vectors using numpy."""
    import numpy as np
    a_arr, b_arr = np.array(a), np.array(b)
    denom = np.linalg.norm(a_arr) * np.linalg.norm(b_arr)
    if denom < 1e-10:
        return 0.0
    return float(np.dot(a_arr, b_arr) / denom)


# ---------------------------------------------------------------------------
# Library Index (semantic search over PDF knowledge base)
# ---------------------------------------------------------------------------

def build_library_index() -> None:
    """Build or refresh the embedding index for the PDF knowledge library.

    Reads PDFs from bundled data, chunks text, generates embeddings,
    and caches the index to the user data directory.
    """
    user_dir = get_user_data_dir()
    index_path = user_dir / "library_index.json"

    if index_path.exists():
        return  # Already built

    client = get_openai_client()
    if client is None:
        return  # Can't build without OpenAI

    library_dir = get_package_data_path("knowledge", "library")
    if not library_dir.exists():
        return

    from pypdf import PdfReader

    index: Dict[str, Any] = {}

    for pdf_file in library_dir.glob("*.pdf"):
        try:
            reader = PdfReader(str(pdf_file))
            full_text = ""
            for page in reader.pages:
                full_text += page.extract_text() or ""

            # Chunk into ~500 char segments
            chunks = []
            for i in range(0, len(full_text), 500):
                chunk_text = full_text[i:i + 500].strip()
                if len(chunk_text) < 50:
                    continue
                vec = get_embedding(chunk_text)
                if vec:
                    chunks.append({"text": chunk_text, "vector": vec})

            if chunks:
                index[pdf_file.name] = {"chunks": chunks}

        except Exception:
            continue

    if index:
        index_path.write_text(json.dumps(index), encoding="utf-8")


# ---------------------------------------------------------------------------
# Inventory Check Helper
# ---------------------------------------------------------------------------

def check_inventory(material_keyword: str) -> bool:
    """Simple check if a material keyword exists in the master inventory."""
    inventory_path = get_user_data_dir() / "processed" / "master_inventory.csv"
    if not inventory_path.exists():
        return False
    content = inventory_path.read_text(encoding="utf-8", errors="ignore").lower()
    return material_keyword.lower() in content
