package jobs

import (
	"encoding/json"
	"testing"

	"github.com/treytartt/honeydue-api/internal/models"
)

// --- BuildDigestMessage ---

func TestBuildDigestMessage_BothCounts(t *testing.T) {
	title, body := BuildDigestMessage(3, 5)
	if title != "Daily Task Summary" {
		t.Errorf("title = %q, want %q", title, "Daily Task Summary")
	}
	want := "You have 3 overdue task(s) and 5 task(s) due this week"
	if body != want {
		t.Errorf("body = %q, want %q", body, want)
	}
}

func TestBuildDigestMessage_OnlyOverdue(t *testing.T) {
	_, body := BuildDigestMessage(2, 0)
	want := "You have 2 overdue task(s) that need attention"
	if body != want {
		t.Errorf("body = %q, want %q", body, want)
	}
}

func TestBuildDigestMessage_OnlyDueSoon(t *testing.T) {
	_, body := BuildDigestMessage(0, 4)
	want := "You have 4 task(s) due this week"
	if body != want {
		t.Errorf("body = %q, want %q", body, want)
	}
}

func TestBuildDigestMessage_Title_AlwaysDailyTaskSummary(t *testing.T) {
	cases := [][2]int{{1, 1}, {0, 1}, {1, 0}}
	for _, c := range cases {
		title, _ := BuildDigestMessage(c[0], c[1])
		if title != "Daily Task Summary" {
			t.Errorf("BuildDigestMessage(%d,%d) title = %q", c[0], c[1], title)
		}
	}
}

// --- IsOverdueStage ---

func TestIsOverdueStage_Overdue1_True(t *testing.T) {
	if !IsOverdueStage("overdue_1") {
		t.Error("expected true for overdue_1")
	}
}

func TestIsOverdueStage_Overdue14_True(t *testing.T) {
	if !IsOverdueStage("overdue_14") {
		t.Error("expected true for overdue_14")
	}
}

func TestIsOverdueStage_Reminder7d_False(t *testing.T) {
	if IsOverdueStage("reminder_7d") {
		t.Error("expected false for reminder_7d")
	}
}

func TestIsOverdueStage_DayOf_False(t *testing.T) {
	if IsOverdueStage("day_of") {
		t.Error("expected false for day_of")
	}
}

func TestIsOverdueStage_Empty_False(t *testing.T) {
	if IsOverdueStage("") {
		t.Error("expected false for empty string")
	}
}

// --- ExtractFrequencyDays ---

func TestExtractFrequencyDays_WithFrequency(t *testing.T) {
	days := 7
	task := &models.Task{
		Frequency: &models.TaskFrequency{Days: &days},
	}
	got := ExtractFrequencyDays(task)
	if got == nil || *got != 7 {
		t.Errorf("got %v, want 7", got)
	}
}

func TestExtractFrequencyDays_WithCustomInterval(t *testing.T) {
	custom := 14
	task := &models.Task{
		CustomIntervalDays: &custom,
	}
	got := ExtractFrequencyDays(task)
	if got == nil || *got != 14 {
		t.Errorf("got %v, want 14", got)
	}
}

func TestExtractFrequencyDays_NilFrequency(t *testing.T) {
	task := &models.Task{}
	got := ExtractFrequencyDays(task)
	if got != nil {
		t.Errorf("got %v, want nil", got)
	}
}

func TestExtractFrequencyDays_NilDays(t *testing.T) {
	task := &models.Task{
		Frequency: &models.TaskFrequency{},
	}
	got := ExtractFrequencyDays(task)
	if got != nil {
		t.Errorf("got %v, want nil", got)
	}
}

// --- Email payload tests ---

func TestEmailPayload_Unmarshal_Valid(t *testing.T) {
	data := []byte(`{"to":"a@b.com","subject":"hi","html_body":"<b>hi</b>","text_body":"hi"}`)
	var p EmailPayload
	if err := json.Unmarshal(data, &p); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if p.To != "a@b.com" || p.Subject != "hi" {
		t.Errorf("got %+v", p)
	}
}

func TestEmailPayload_Unmarshal_Invalid(t *testing.T) {
	var p EmailPayload
	if err := json.Unmarshal([]byte(`{invalid}`), &p); err == nil {
		t.Error("expected error for invalid JSON")
	}
}

// --- NewSendEmailTask ---

func TestNewSendEmailTask_ReturnsTask(t *testing.T) {
	task, err := NewSendEmailTask("a@b.com", "Subject", "<b>hi</b>", "hi")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if task.Type() != TypeSendEmail {
		t.Errorf("task type = %q, want %q", task.Type(), TypeSendEmail)
	}
}

func TestNewSendEmailTask_PayloadFields(t *testing.T) {
	task, err := NewSendEmailTask("user@example.com", "Welcome", "<h1>Hello</h1>", "Hello")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	var p EmailPayload
	if err := json.Unmarshal(task.Payload(), &p); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if p.To != "user@example.com" {
		t.Errorf("To = %q, want %q", p.To, "user@example.com")
	}
	if p.Subject != "Welcome" {
		t.Errorf("Subject = %q, want %q", p.Subject, "Welcome")
	}
	if p.HTMLBody != "<h1>Hello</h1>" {
		t.Errorf("HTMLBody = %q, want %q", p.HTMLBody, "<h1>Hello</h1>")
	}
	if p.TextBody != "Hello" {
		t.Errorf("TextBody = %q, want %q", p.TextBody, "Hello")
	}
}

// --- NewSendPushTask ---

func TestNewSendPushTask_ReturnsTask(t *testing.T) {
	task, err := NewSendPushTask(42, "Title", "Body", map[string]string{"key": "val"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if task.Type() != TypeSendPush {
		t.Errorf("task type = %q, want %q", task.Type(), TypeSendPush)
	}
}

func TestNewSendPushTask_PayloadFields(t *testing.T) {
	data := map[string]string{"action": "open", "id": "123"}
	task, err := NewSendPushTask(7, "Alert", "Something happened", data)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	var p PushPayload
	if err := json.Unmarshal(task.Payload(), &p); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if p.UserID != 7 {
		t.Errorf("UserID = %d, want 7", p.UserID)
	}
	if p.Title != "Alert" {
		t.Errorf("Title = %q, want %q", p.Title, "Alert")
	}
	if p.Message != "Something happened" {
		t.Errorf("Message = %q, want %q", p.Message, "Something happened")
	}
	if p.Data["action"] != "open" || p.Data["id"] != "123" {
		t.Errorf("Data = %v, want map with action=open, id=123", p.Data)
	}
}

func TestNewSendPushTask_NilData(t *testing.T) {
	task, err := NewSendPushTask(1, "T", "M", nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	var p PushPayload
	if err := json.Unmarshal(task.Payload(), &p); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if p.Data != nil {
		t.Errorf("Data = %v, want nil", p.Data)
	}
}
