"""
Tests for OpenMemo v1 Developer API + OpenMemo SDK

Phase: PRD v1 — Developer API & SDK
"""

import json
import time
import pytest

from openmemo.storage.sqlite_store import SQLiteStore
from openmemo.api.v1 import create_v1_blueprint


# ── Fixtures ──────────────────────────────────────────────────────────────────
@pytest.fixture
def store(tmp_path):
    s = SQLiteStore(db_path=str(tmp_path / "test_v1.db"))
    yield s
    s.close()


@pytest.fixture
def app(store):
    from flask import Flask
    from flask_cors import CORS
    a = Flask(__name__)
    CORS(a)
    bp = create_v1_blueprint(store=store)
    a.register_blueprint(bp)
    return a


@pytest.fixture
def client(app):
    return app.test_client()


# ── POST /v1/memory ───────────────────────────────────────────────────────────
class TestIngest:
    def test_basic_ingest(self, client):
        resp = client.post("/v1/memory", json={
            "user_id": "user_1",
            "content": "Tom likes dinosaurs",
            "type": "fact",
            "scene": "conversation",
        })
        assert resp.status_code == 201
        data = resp.get_json()
        assert data["status"] == "stored"
        assert "id" in data

    def test_ingest_minimal(self, client):
        resp = client.post("/v1/memory", json={"content": "Minimal memory"})
        assert resp.status_code == 201
        assert resp.get_json()["status"] == "stored"

    def test_ingest_missing_content(self, client):
        resp = client.post("/v1/memory", json={"user_id": "u1"})
        assert resp.status_code == 400
        assert "error" in resp.get_json()

    def test_ingest_empty_content(self, client):
        resp = client.post("/v1/memory", json={"content": "   "})
        assert resp.status_code == 400

    def test_ingest_all_types(self, client):
        for t in ["fact", "decision", "observation", "preference"]:
            resp = client.post("/v1/memory", json={
                "content": f"Memory of type {t}",
                "type": t,
            })
            assert resp.status_code == 201


# ── POST /v1/recall ───────────────────────────────────────────────────────────
class TestRecall:
    def test_recall_basic(self, client):
        client.post("/v1/memory", json={
            "user_id": "user_2",
            "content": "Tom likes dinosaurs",
        })
        resp = client.post("/v1/recall", json={
            "user_id": "user_2",
            "query": "what does Tom like?",
        })
        assert resp.status_code == 200
        data = resp.get_json()
        assert "summary" in data
        assert "details" in data
        assert "confidence" in data
        assert isinstance(data["details"], list)
        assert 0.0 <= data["confidence"] <= 1.0

    def test_recall_missing_query(self, client):
        resp = client.post("/v1/recall", json={"user_id": "u1"})
        assert resp.status_code == 400

    def test_recall_empty_store(self, client):
        resp = client.post("/v1/recall", json={
            "user_id": "nobody",
            "query": "anything",
        })
        assert resp.status_code == 200
        data = resp.get_json()
        assert "summary" in data
        assert data["confidence"] >= 0.0

    def test_recall_with_scene(self, client):
        client.post("/v1/memory", json={
            "content": "Deploy uses Docker",
            "scene": "deployment",
        })
        resp = client.post("/v1/recall", json={
            "query": "how to deploy?",
            "scene": "deployment",
        })
        assert resp.status_code == 200

    def test_recall_cache(self, client):
        client.post("/v1/memory", json={"content": "Cache test memory"})
        payload = {"query": "cache test", "user_id": "cache_user"}
        resp1 = client.post("/v1/recall", json=payload)
        resp2 = client.post("/v1/recall", json=payload)
        assert resp1.status_code == 200
        assert resp2.status_code == 200
        assert resp1.get_json() == resp2.get_json()

    def test_recall_returns_latency(self, client):
        resp = client.post("/v1/recall", json={"query": "anything"})
        assert resp.status_code == 200
        data = resp.get_json()
        assert "latency_ms" in data
        assert data["latency_ms"] >= 0


# ── POST /v1/experience ───────────────────────────────────────────────────────
class TestExperience:
    def test_experience_basic(self, client):
        resp = client.post("/v1/experience", json={
            "user_id": "user_3",
            "task": "deploy backend",
        })
        assert resp.status_code == 200
        data = resp.get_json()
        assert "workflow" in data
        assert "confidence" in data
        assert isinstance(data["workflow"], list)
        assert 0.0 <= data["confidence"] <= 1.0

    def test_experience_missing_task(self, client):
        resp = client.post("/v1/experience", json={"user_id": "u1"})
        assert resp.status_code == 400

    def test_experience_with_memory_context(self, client):
        client.post("/v1/memory", json={
            "user_id": "user_exp",
            "content": "Deploy backend: build docker image, compose up, check logs",
            "scene": "deployment",
        })
        resp = client.post("/v1/experience", json={
            "user_id": "user_exp",
            "task": "deploy backend",
        })
        assert resp.status_code == 200
        data = resp.get_json()
        assert "workflow" in data

    def test_experience_returns_latency(self, client):
        resp = client.post("/v1/experience", json={"task": "anything"})
        assert resp.status_code == 200
        assert "latency_ms" in resp.get_json()


# ── POST /v1/outcome ──────────────────────────────────────────────────────────
class TestOutcome:
    def test_outcome_success(self, client):
        resp = client.post("/v1/outcome", json={
            "user_id": "user_4",
            "task": "deploy backend",
            "result": "success",
            "latency": 2.3,
        })
        assert resp.status_code == 200
        data = resp.get_json()
        assert data["status"] == "recorded"
        assert data["task"] == "deploy backend"
        assert data["result"] == "success"

    def test_outcome_failure(self, client):
        resp = client.post("/v1/outcome", json={
            "task": "failing task",
            "result": "failure",
        })
        assert resp.status_code == 200
        assert resp.get_json()["status"] == "recorded"

    def test_outcome_missing_task(self, client):
        resp = client.post("/v1/outcome", json={"result": "success"})
        assert resp.status_code == 400

    def test_outcome_minimal(self, client):
        resp = client.post("/v1/outcome", json={
            "task": "minimal task",
            "result": "partial",
        })
        assert resp.status_code == 200


# ── OpenMemo Python SDK ───────────────────────────────────────────────────────
class TestOpenMemoSDK:
    def test_sdk_local_mode(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_test.db"))
        assert not memo._remote

    def test_sdk_ingest(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_test.db"))
        result = memo.ingest("Tom likes dinosaurs")
        assert result["status"] == "stored"

    def test_sdk_recall(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_test.db"))
        memo.ingest("Tom likes dinosaurs")
        result = memo.recall("what does Tom like?")
        assert "summary" in result
        assert "details" in result
        assert "confidence" in result
        assert isinstance(result["details"], list)

    def test_sdk_experience(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_test.db"))
        result = memo.experience("deploy backend")
        assert "workflow" in result
        assert "confidence" in result
        assert isinstance(result["workflow"], list)

    def test_sdk_outcome(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_test.db"))
        result = memo.outcome("deploy backend", "success", latency=1.5)
        assert result["status"] == "recorded"

    def test_sdk_full_flow(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_full.db"), user_id="alice")

        memo.ingest("Alice uses FastAPI for backend")
        memo.ingest("Alice deploys to AWS using ECS")

        memory = memo.recall("how does Alice deploy?")
        assert "summary" in memory
        assert memory["confidence"] >= 0.0

        workflow = memo.experience("deploy to cloud")
        assert isinstance(workflow["workflow"], list)

        result = memo.outcome("deploy to cloud", "success", latency=5.0)
        assert result["status"] == "recorded"

    def test_sdk_remote_mode_init(self):
        from openmemo import OpenMemo
        memo = OpenMemo(api_key="sk-test-key")
        assert memo._remote
        assert memo._api_key == "sk-test-key"

    def test_sdk_default_user_id(self, tmp_path):
        from openmemo import OpenMemo
        memo = OpenMemo(db_path=str(tmp_path / "sdk_uid.db"), user_id="bob")
        result = memo.ingest("Bob likes Python")
        assert result["status"] == "stored"
