#!/usr/bin/env python3
"""
Recall API examples for Hindsight.
Run: python examples/api/recall.py
"""
import os
import requests

HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")

# =============================================================================
# Setup (not shown in docs)
# =============================================================================
from hindsight_client import Hindsight

client = Hindsight(base_url=HINDSIGHT_URL)

# Seed some data for recall examples
client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer")
client.retain(bank_id="my-bank", content="Alice loves hiking on weekends")
client.retain(bank_id="my-bank", content="Bob is a data scientist who works with Alice")

# =============================================================================
# Doc Examples
# =============================================================================

# [docs:recall-basic]
response = client.recall(bank_id="my-bank", query="What does Alice do?")

# response.results is a list of RecallResult objects, each with:
# - id:             fact ID
# - text:           the extracted fact
# - type:           "world", "experience", or "observation"
# - context:        context label set during retain
# - metadata:       dict[str, str] set during retain
# - tags:           list of tags
# - entities:       list of entity name strings linked to this fact
# - occurred_start: ISO datetime of when the event started
# - occurred_end:   ISO datetime of when the event ended
# - mentioned_at:   ISO datetime of when the fact was retained
# - document_id:    document this fact belongs to
# - chunk_id:       chunk this fact was extracted from

# Example response.results:
# [
#   RecallResult(id="a1b2...", text="Alice works at Google as a software engineer", type="world", context="career", ...),
#   RecallResult(id="c3d4...", text="Alice got promoted to senior engineer", type="experience", occurred_start="2024-03-15T00:00:00Z", ...),
# ]
# [/docs:recall-basic]


# [docs:recall-with-options]
response = client.recall(
    bank_id="my-bank",
    query="What does Alice do?",
    types=["world", "experience"],
    budget="high",
    max_tokens=8000,
    trace=True,
)

# Access results
for r in response.results:
    print(f"- {r.text}")
# [/docs:recall-with-options]


# [docs:recall-world-only]
# Only world facts (objective information)
world_facts = client.recall(
    bank_id="my-bank",
    query="Where does Alice work?",
    types=["world"]
)
# [/docs:recall-world-only]


# [docs:recall-experience-only]
# Only experience (conversations and events)
experience = client.recall(
    bank_id="my-bank",
    query="What have I recommended?",
    types=["experience"]
)
# [/docs:recall-experience-only]


# [docs:recall-observations-only]
# Only observations (consolidated knowledge)
observations = client.recall(
    bank_id="my-bank",
    query="What patterns have I learned?",
    types=["observation"]
)
# [/docs:recall-observations-only]


# [docs:recall-with-observations]
# Include observations in recall
results = client.recall(
    bank_id="my-bank",
    query="What programming languages does Alice prefer?",
    types=["world", "experience", "observation"]
)

# Observations only
observations = client.recall(
    bank_id="my-bank",
    query="What patterns have I learned?",
    types=["observation"]
)
# [/docs:recall-with-observations]


# [docs:recall-source-facts]
# Recall observations and include their source facts
response = client.recall(
    bank_id="my-bank",
    query="What patterns have I learned about Alice?",
    types=["observation"],
    include_source_facts=True,
    max_source_facts_tokens=4096,
)

for obs in response.results:
    print(f"Observation: {obs.text}")
    if obs.source_fact_ids and response.source_facts:
        print("  Derived from:")
        for fact_id in obs.source_fact_ids:
            fact = response.source_facts.get(fact_id)
            if fact:
                print(f"    - [{fact.type}] {fact.text}")
# [/docs:recall-source-facts]


# [docs:recall-token-budget]
# Fill up to 4K tokens of context with relevant memories
results = client.recall(bank_id="my-bank", query="What do I know about Alice?", max_tokens=4096)

# Smaller budget for quick lookups
results = client.recall(bank_id="my-bank", query="Alice's email", max_tokens=500)
# [/docs:recall-token-budget]




# [docs:recall-budget-levels]
# Quick lookup
results = client.recall(bank_id="my-bank", query="Alice's email", budget="low")

# Deep exploration
results = client.recall(bank_id="my-bank", query="How are Alice and Bob connected?", budget="high")
# [/docs:recall-budget-levels]


# [docs:recall-with-tags]
# Filter recall to only memories tagged for a specific user
response = client.recall(
    bank_id="my-bank",
    query="What feedback did the user give?",
    tags=["user:alice"],
    tags_match="any"  # OR matching, includes untagged (default)
)
# [/docs:recall-with-tags]


# [docs:recall-tags-strict]
# Strict mode: only return memories that have matching tags (exclude untagged)
response = client.recall(
    bank_id="my-bank",
    query="What did the user say?",
    tags=["user:alice"],
    tags_match="any_strict"  # OR matching, excludes untagged memories
)
# [/docs:recall-tags-strict]


# [docs:recall-tags-all]
# AND matching: require ALL specified tags to be present
response = client.recall(
    bank_id="my-bank",
    query="What bugs were reported?",
    tags=["user:alice", "bug-report"],
    tags_match="all_strict"  # Memory must have BOTH tags
)
# [/docs:recall-tags-all]


# [docs:recall-tags-any]
response = client.recall(
    bank_id="my-bank",
    query="communication preferences",
    tags=["user:alice"],
    tags_match="any",  # default
)
# Returns:
#   [match]    "Alice prefers async communication"     — has "user:alice"
#   [no match] "Bob dislikes long meetings"             — no overlap with ["user:alice"]
#   [match]    "Team uses Slack for announcements"      — has "user:alice"
#   [match]    "Company policy: no meetings on Fridays" — untagged, included by default
# [/docs:recall-tags-any]


# [docs:recall-tags-any-strict]
response = client.recall(
    bank_id="my-bank",
    query="communication preferences",
    tags=["user:alice"],
    tags_match="any_strict",
)
# Returns:
#   [match]    "Alice prefers async communication"     — has "user:alice"
#   [no match] "Bob dislikes long meetings"             — no overlap with ["user:alice"]
#   [match]    "Team uses Slack for announcements"      — has "user:alice"
#   [no match] "Company policy: no meetings on Fridays" — untagged, excluded
# [/docs:recall-tags-any-strict]


# [docs:recall-tags-all-mode]
response = client.recall(
    bank_id="my-bank",
    query="communication tools",
    tags=["user:alice", "team"],
    tags_match="all",
)
# Returns:
#   [no match] "Alice prefers async communication"     — missing "team"
#   [no match] "Bob dislikes long meetings"             — missing both tags
#   [match]    "Team uses Slack for announcements"      — has both "user:alice" and "team"
#   [match]    "Company policy: no meetings on Fridays" — untagged, included by default
# [/docs:recall-tags-all-mode]


# [docs:recall-tags-all-strict]
response = client.recall(
    bank_id="my-bank",
    query="communication tools",
    tags=["user:alice", "team"],
    tags_match="all_strict",
)
# Returns:
#   [no match] "Alice prefers async communication"     — missing "team"
#   [no match] "Bob dislikes long meetings"             — missing both tags
#   [match]    "Team uses Slack for announcements"      — has both "user:alice" and "team"
#   [no match] "Company policy: no meetings on Fridays" — untagged, excluded
# [/docs:recall-tags-all-strict]


# =============================================================================
# Legacy snippets for v0.3 docs (kept for backward compatibility)
# =============================================================================

# [docs:recall-opinions-only]
# Legacy: opinions replaced by observations in v0.4+
# Only retrieve opinions (beliefs and preferences)
opinions = client.recall(
    bank_id="my-bank",
    query="What are my preferences?",
    types=["opinion"]
)
# [/docs:recall-opinions-only]


# [docs:recall-include-entities]
# Legacy: entity summaries replaced by observations in v0.4+
# Include entity summaries in recall results
response = client.recall(
    bank_id="my-bank",
    query="What do I know about Alice?",
    include_entities=True,
    max_entity_tokens=500
)
# [/docs:recall-include-entities]


# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank")

print("recall.py: All examples passed")
