"""
stream_ingest.py — Real-time Data Stream Soul Ingestion

Real-time data from wearable devices/phones/IoT (voice transcripts, video descriptions, sensor readings),
processed by AI to extract soul-related information and automatically routed to the corresponding recording functions.

Data flow:
  Device → ASR/video summary/sensor → POST /stream/ingest
  → Claude API soul information extraction
  → Auto-route to record_soul / record_state / record_silence

Design principles:
  · Device side does heavy lifting (ASR, video→text), API only accepts text+metadata
  · AI extracts soul information: decisions, emotions, states, silences, relationship interactions
  · Non-soul-related content is silently discarded (saves storage)
  · source_engine='device_stream', weight 0.6 (lower than direct recording, higher than game inference)
  · Content dedup: similar content not re-recorded within 30 minutes
"""

import hashlib
import json
import urllib.request
import urllib.error
from datetime import datetime, timedelta
from pathlib import Path

# ── AI extraction prompt ───────────────────────────────────────

_EXTRACT_PROMPT = """You are a soul data extractor. Analyze the following real-time data from a wearable device and extract information related to the user's inner world.

Data source: {source_type}
Device: {device_id}
Time: {timestamp}
{context_line}

Raw content:
{raw_content}

Determine whether this content contains soul-related information (decisions, emotions, values, interpersonal relationships, inner thoughts, physical states, etc.).

If it does, return JSON (multiple items allowed):
```json
{{
  "relevant": true,
  "items": [
    {{
      "type": "soul | state | silence | relation",
      "content": {{
        // When type=soul:
        "context": "what was happening at the time",
        "human_decision": "what decision was made / what opinion was expressed",
        "divergence_reason": "why they did/thought this",
        "emotional_state": "emotion",
        "tags": ["tags"],
        "decision_layer": "body/habit/identity/values/survival (optional)"

        // When type=state:
        "emotional_state": "emotion",
        "energy": "high/medium/low",
        "body_state": "physical state",
        "environment": "environment description",
        "notes": "other observations"

        // When type=silence:
        "topic": "what topic was avoided",
        "silence_form": "no_response/deflection/half_said/pause",
        "observation": "observation"

        // When type=relation:
        "person": "related person",
        "event_type": "interaction/conflict/support",
        "description": "interaction description",
        "emotional_state": "emotion"
      }}
    }}
  ]
}}
```

If it does not contain soul-related information (mundane daily tasks, meaningless conversation, purely transactional content), return:
```json
{{"relevant": false, "reason": "brief explanation of why it's not relevant"}}
```

Important: Only extract information with genuine soul value. Casual chat, weather discussion, ordering food delivery, etc. do not count.
Better to miss something than to fabricate — device data is an indirect source, only extract explicitly expressed content."""


def extract_soul_data(raw_content: str, source_type: str = "voice_transcript",
                      device_id: str = "wearable", context: str = "",
                      timestamp: str = None) -> dict:
    """
    Use Claude API to extract soul information from raw device data.

    Args:
        raw_content: Raw text content (voice transcript/video description/sensor summary)
        source_type: Source type voice_transcript/video_summary/sensor_reading/chat_log
        device_id: Device identifier
        context: Additional context
        timestamp: Data timestamp

    Returns:
        {"relevant": bool, "items": [...]} or {"relevant": false, "reason": "..."}
    """
    if not raw_content or len(raw_content.strip()) < 10:
        return {"relevant": False, "reason": "Content too short"}

    ts = timestamp or datetime.now().isoformat()
    context_line = f"Additional context: {context}" if context else ""

    prompt = _EXTRACT_PROMPT.format(
        source_type=source_type,
        device_id=device_id,
        timestamp=ts,
        context_line=context_line,
        raw_content=raw_content[:3000],  # Truncate to prevent excessive length
    )

    text = ""
    try:
        import anthropic
        client = anthropic.Anthropic()
        msg = client.messages.create(
            model="claude-sonnet-4-6",  # Use Sonnet to balance cost and quality
            max_tokens=1024,
            messages=[{"role": "user", "content": prompt}],
        )
        text = msg.content[0].text

        # Extract JSON (may be wrapped in markdown code blocks)
        json_text = text
        if "```json" in text:
            json_text = text.split("```json")[1].split("```")[0]
        elif "```" in text:
            json_text = text.split("```")[1].split("```")[0]

        return json.loads(json_text.strip())
    except json.JSONDecodeError:
        return {"relevant": False, "reason": "AI response format parsing failed", "raw_response": text[:200]}
    except Exception as e:
        return {"relevant": False, "reason": f"AI call failed: {str(e)[:100]}"}


# ── Deduplication ──────────────────────────────────────────────

_recent_hashes: dict[str, datetime] = {}
_DEDUP_WINDOW = timedelta(minutes=30)


def _content_hash(text: str) -> str:
    """Content fingerprint for deduplication."""
    return hashlib.md5(text.strip().encode("utf-8")).hexdigest()[:12]


def _is_duplicate(text: str) -> bool:
    """Check if identical content exists within the last 30 minutes."""
    now = datetime.now()
    # Clean up expired entries
    expired = [k for k, t in _recent_hashes.items() if now - t > _DEDUP_WINDOW]
    for k in expired:
        del _recent_hashes[k]

    h = _content_hash(text)
    if h in _recent_hashes:
        return True
    _recent_hashes[h] = now
    return False


# ── Route and record ──────────────────────────────────────────

def route_and_record(items: list, device_id: str, raw_content: str) -> dict:
    """
    Route AI-extracted soul information to the corresponding recording functions.

    Returns {"recorded": int, "skipped": int, "details": [...]}
    """
    import db as soul_db

    recorded = 0
    skipped = 0
    details = []
    source_device = f"stream:{device_id}"

    for item in items:
        item_type = item.get("type", "")
        content = item.get("content", {})

        try:
            if item_type == "soul":
                human_decision = content.get("human_decision", "")
                if not human_decision or len(human_decision) < 5:
                    skipped += 1
                    continue
                soul_db.record(
                    context=content.get("context", f"[device stream:{device_id}]"),
                    human_decision=human_decision,
                    divergence_reason=content.get("divergence_reason", "Auto-extracted from device stream"),
                    tags=content.get("tags", []) + ["device-stream"],
                    emotional_state=content.get("emotional_state"),
                    decision_layer=content.get("decision_layer"),
                    source_device=source_device,
                )
                recorded += 1
                details.append({"type": "soul", "decision": human_decision[:50]})

            elif item_type == "state":
                soul_db.record_state(
                    emotional_state=content.get("emotional_state"),
                    energy=content.get("energy"),
                    body_state=content.get("body_state"),
                    environment=content.get("environment"),
                    notes=content.get("notes", ""),
                    tags=["device-stream"],
                    source_device=source_device,
                )
                recorded += 1
                details.append({"type": "state",
                                "emotion": content.get("emotional_state", "")})

            elif item_type == "silence":
                topic = content.get("topic", "")
                if not topic or len(topic) < 3:
                    skipped += 1
                    continue
                soul_db.record_silence(
                    topic=topic,
                    silence_form=content.get("silence_form", "pause"),
                    observation=content.get("observation", "Auto-detected from device stream"),
                    tags=["device-stream"],
                )
                recorded += 1
                details.append({"type": "silence", "topic": topic[:50]})

            elif item_type == "relation":
                # Try to match existing relationship
                person = content.get("person", "")
                if not person:
                    skipped += 1
                    continue
                conn = soul_db._open_db()
                rel = conn.execute(
                    "SELECT id FROM soul_relations WHERE alias=?", (person,)
                ).fetchone()
                conn.close()
                if rel:
                    soul_db.record_relation_event(
                        relation_id=rel[0],
                        event_type=content.get("event_type", "interaction"),
                        description=content.get("description", ""),
                        emotional_state=content.get("emotional_state"),
                    )
                    recorded += 1
                    details.append({"type": "relation", "person": person})
                else:
                    # Unknown person, record as soul event
                    soul_db.record(
                        context=f"Interaction with {person}",
                        human_decision=content.get("description", ""),
                        divergence_reason=f"Device stream captured interaction with {person}",
                        tags=["device-stream", "relation"],
                        emotional_state=content.get("emotional_state"),
                        source_device=source_device,
                    )
                    recorded += 1
                    details.append({"type": "soul_from_relation", "person": person})
            else:
                skipped += 1
        except Exception as e:
            skipped += 1
            details.append({"type": item_type, "error": str(e)[:80]})

    return {"recorded": recorded, "skipped": skipped, "details": details}


# ── Main entry point ──────────────────────────────────────────

def ingest_stream(raw_content: str, source_type: str = "voice_transcript",
                  device_id: str = "wearable", context: str = "",
                  timestamp: str = None) -> dict:
    """
    Complete stream data ingestion flow: dedup → AI extraction → route and record.

    Returns:
        {"ok": bool, "relevant": bool, "recorded": int, "skipped": int, ...}
    """
    # Dedup check
    if _is_duplicate(raw_content):
        return {"ok": True, "relevant": False, "reason": "Duplicate content within 30 minutes",
                "recorded": 0, "skipped": 0}

    # AI extraction
    result = extract_soul_data(raw_content, source_type, device_id, context, timestamp)

    if not result.get("relevant"):
        # Log to stream_log but don't write to soul data
        _log_stream(raw_content, source_type, device_id, relevant=False,
                    reason=result.get("reason", ""))
        return {"ok": True, "relevant": False, "reason": result.get("reason", "Not soul-related"),
                "recorded": 0, "skipped": 0}

    # Route and record
    items = result.get("items", [])
    write_result = route_and_record(items, device_id, raw_content)

    # Log ingestion
    _log_stream(raw_content, source_type, device_id, relevant=True,
                recorded=write_result["recorded"])

    return {
        "ok": True,
        "relevant": True,
        "recorded": write_result["recorded"],
        "skipped": write_result["skipped"],
        "details": write_result["details"],
    }


def _log_stream(raw_content: str, source_type: str, device_id: str,
                relevant: bool, reason: str = "", recorded: int = 0):
    """Log ingestion to the stream_log table."""
    try:
        import db as soul_db
        conn = soul_db._open_db()
        conn.execute(
            """INSERT INTO stream_log
               (timestamp, device_id, source_type, content_hash, content_length,
                relevant, recorded_count, reason)
               VALUES (?,?,?,?,?,?,?,?)""",
            (datetime.now().isoformat(), device_id, source_type,
             _content_hash(raw_content), len(raw_content),
             1 if relevant else 0, recorded, reason))
        conn.commit()
        conn.close()
    except Exception:
        pass  # Log failure doesn't affect main flow


# ── Batch ingestion ───────────────────────────────────────────

def ingest_batch(items: list[dict]) -> dict:
    """
    Batch ingestion (for offline sync scenarios).

    items: [{"raw_content": "...", "source_type": "...", "device_id": "...",
             "context": "...", "timestamp": "..."}, ...]

    Returns {"total": N, "relevant": N, "recorded": N, "skipped": N}
    """
    total = len(items)
    total_relevant = 0
    total_recorded = 0
    total_skipped = 0

    for item in items:
        result = ingest_stream(
            raw_content=item.get("raw_content", ""),
            source_type=item.get("source_type", "voice_transcript"),
            device_id=item.get("device_id", "wearable"),
            context=item.get("context", ""),
            timestamp=item.get("timestamp"),
        )
        if result.get("relevant"):
            total_relevant += 1
        total_recorded += result.get("recorded", 0)
        total_skipped += result.get("skipped", 0)

    return {
        "ok": True,
        "total": total,
        "relevant": total_relevant,
        "recorded": total_recorded,
        "skipped": total_skipped,
    }
