package memllm

import (
	"testing"

	adapters "github.com/memohai/memoh/internal/memory/adapters"
)

func TestParseJSONStringArray_Valid(t *testing.T) {
	t.Parallel()
	result := parseJSONStringArray(`["fact one", "fact two"]`)
	if len(result) != 2 {
		t.Fatalf("expected 2 facts, got %d", len(result))
	}
	if result[0] != "fact one" || result[1] != "fact two" {
		t.Fatalf("unexpected facts: %v", result)
	}
}

func TestParseJSONStringArray_Empty(t *testing.T) {
	t.Parallel()
	result := parseJSONStringArray(`[]`)
	if len(result) != 0 {
		t.Fatalf("expected 0 facts, got %d", len(result))
	}
}

func TestParseJSONStringArray_CodeFence(t *testing.T) {
	t.Parallel()
	input := "```json\n[\"hello\", \"world\"]\n```"
	result := parseJSONStringArray(input)
	if len(result) != 2 {
		t.Fatalf("expected 2 facts from code fence, got %d", len(result))
	}
}

func TestParseJSONStringArray_PrefixText(t *testing.T) {
	t.Parallel()
	input := "Here are the facts:\n[\"a\", \"b\"]"
	result := parseJSONStringArray(input)
	if len(result) != 2 {
		t.Fatalf("expected 2 facts with prefix text, got %d", len(result))
	}
}

func TestParseJSONStringArray_Garbage(t *testing.T) {
	t.Parallel()
	result := parseJSONStringArray("this is not json at all")
	if result != nil {
		t.Fatalf("expected nil for garbage input, got %v", result)
	}
}

func TestParseJSONStringArray_FiltersBlanks(t *testing.T) {
	t.Parallel()
	result := parseJSONStringArray(`["fact one", "", "  ", "fact two"]`)
	if len(result) != 2 {
		t.Fatalf("expected 2 non-empty facts, got %d", len(result))
	}
}

func TestParseExtractResponse_FactsWrapper(t *testing.T) {
	t.Parallel()
	input := `{"facts": ["Name is John", "Is a Software engineer"]}`
	result := parseExtractResponse(input)
	if len(result) != 2 {
		t.Fatalf("expected 2 facts, got %d", len(result))
	}
	if result[0] != "Name is John" {
		t.Fatalf("unexpected first fact: %q", result[0])
	}
}

func TestParseExtractResponse_BareArray(t *testing.T) {
	t.Parallel()
	input := `["fact one", "fact two"]`
	result := parseExtractResponse(input)
	if len(result) != 2 {
		t.Fatalf("expected 2 facts from bare array, got %d", len(result))
	}
}

func TestParseExtractResponse_EmptyFacts(t *testing.T) {
	t.Parallel()
	result := parseExtractResponse(`{"facts": []}`)
	if len(result) != 0 {
		t.Fatalf("expected 0 facts, got %d", len(result))
	}
}

func TestParseUpdateResponse_Mem0Format(t *testing.T) {
	t.Parallel()
	input := `{"memory": [
		{"id": "0", "text": "User is a software engineer", "event": "NONE"},
		{"id": "1", "text": "Name is John", "event": "ADD"},
		{"id": "2", "text": "Loves cheese pizza", "event": "DELETE"},
		{"id": "3", "text": "Moved to Berlin", "event": "UPDATE", "old_memory": "Lives in Tokyo"}
	]}`
	result := parseUpdateResponse(input)
	if len(result) != 4 {
		t.Fatalf("expected 4 actions, got %d", len(result))
	}
	if result[0].Event != "NOOP" {
		t.Fatalf("expected NONE mapped to NOOP, got %q", result[0].Event)
	}
	if result[1].Event != "ADD" || result[1].Text != "Name is John" {
		t.Fatalf("unexpected ADD action: %+v", result[1])
	}
	if result[2].Event != "DELETE" || result[2].ID != "2" {
		t.Fatalf("unexpected DELETE action: %+v", result[2])
	}
	if result[3].Event != "UPDATE" || result[3].OldMemory != "Lives in Tokyo" {
		t.Fatalf("unexpected UPDATE action: %+v", result[3])
	}
}

func TestParseUpdateResponse_FlatArrayFallback(t *testing.T) {
	t.Parallel()
	input := `[{"event":"ADD","text":"User likes tea"},{"event":"NOOP"},{"event":"DELETE","id":"bot-1:mem_123"}]`
	result := parseUpdateResponse(input)
	if len(result) != 3 {
		t.Fatalf("expected 3 actions, got %d", len(result))
	}
}

func TestParseUpdateResponse_WithCodeFence(t *testing.T) {
	t.Parallel()
	input := "```json\n{\"memory\": [{\"event\":\"ADD\",\"text\":\"foo\",\"id\":\"1\"}]}\n```"
	result := parseUpdateResponse(input)
	if len(result) != 1 {
		t.Fatalf("expected 1 action from code fence, got %d", len(result))
	}
}

func TestParseUpdateResponse_EmptyMemory(t *testing.T) {
	t.Parallel()
	result := parseUpdateResponse(`{"memory": []}`)
	if result != nil {
		t.Fatalf("expected nil for empty memory array, got %v", result)
	}
}

func TestParseUpdateResponse_Garbage(t *testing.T) {
	t.Parallel()
	result := parseUpdateResponse("not json")
	if result != nil {
		t.Fatalf("expected nil for garbage, got %v", result)
	}
}

func TestExtractJSONBlock_NoFence(t *testing.T) {
	t.Parallel()
	got := extractJSONBlock(`["a"]`)
	if got != `["a"]` {
		t.Fatalf("expected raw pass-through, got %q", got)
	}
}

func TestExtractJSONBlock_JSONFence(t *testing.T) {
	t.Parallel()
	got := extractJSONBlock("```json\n[\"a\"]\n```")
	if got != `["a"]` {
		t.Fatalf("expected extracted content, got %q", got)
	}
}

func TestExtractJSONBlock_PlainFence(t *testing.T) {
	t.Parallel()
	got := extractJSONBlock("```\n[\"a\"]\n```")
	if got != `["a"]` {
		t.Fatalf("expected extracted content, got %q", got)
	}
}

var _ adapters.LLM = (*Client)(nil)
