package commerce

import (
	"testing"
	"time"
)

func TestRenewPreview(t *testing.T) {
	now := time.Date(2026, 7, 9, 12, 0, 0, 0, time.UTC)
	expires := now.AddDate(0, 0, 10)
	got, err := renewPreview(Instance{
		ID:              7,
		Status:          "active",
		Cycle:           "monthly",
		UnitPrice:       1000,
		NextRenewAmount: 1200,
		ExpiresAt:       &expires,
	}, 3000, 2, now)
	if err != nil {
		t.Fatal(err)
	}
	if got.TotalAmount != 2400 || !got.EnoughBalance {
		t.Fatalf("renew preview = %#v", got)
	}
	wantExpires := expires.AddDate(0, 0, 60)
	if got.NewExpiresAt == nil || !got.NewExpiresAt.Equal(wantExpires) {
		t.Fatalf("new expires = %v, want %v", got.NewExpiresAt, wantExpires)
	}
}

func TestProratedRefund(t *testing.T) {
	now := time.Date(2026, 7, 9, 12, 0, 0, 0, time.UTC)
	expires := now.AddDate(0, 0, 15)
	got := proratedRefund(Order{TotalAmount: 3000}, []Instance{{
		Cycle:     "monthly",
		UnitPrice: 3000,
		ExpiresAt: &expires,
	}}, now)
	if got != 1500 {
		t.Fatalf("prorated refund = %d, want 1500", got)
	}
}

func TestOSLabel(t *testing.T) {
	raw := "1|Ubuntu 22.04"
	got := osLabel(&raw)
	if got == nil || *got != "Ubuntu 22.04" {
		t.Fatalf("os label = %v", got)
	}
}

func TestInstanceVisibilityStatusRules(t *testing.T) {
	for _, status := range []string{"failed", "provision_failed", "terminated", "deleted", "cancelled", "canceled"} {
		if isPublicVisibleInstanceStatus(status) {
			t.Fatalf("status %q should be hidden from public instance lists", status)
		}
		if isTicketAttachableInstanceStatus(status) {
			t.Fatalf("status %q should not be attachable to tickets", status)
		}
	}
	for _, status := range []string{"active", "suspended"} {
		if !isPublicVisibleInstanceStatus(status) {
			t.Fatalf("status %q should be public visible", status)
		}
		if !isTicketAttachableInstanceStatus(status) {
			t.Fatalf("status %q should be attachable to tickets", status)
		}
	}
	if !isPublicVisibleInstanceStatus("provisioning") {
		t.Fatal("provisioning should remain visible in the normal instance list")
	}
	if isTicketAttachableInstanceStatus("provisioning") {
		t.Fatal("provisioning should not be attachable to tickets")
	}
}

func TestPointRewardStatusLabel(t *testing.T) {
	if got := pointRewardStatusLabel("claimable"); got != "可领取" {
		t.Fatalf("label = %q", got)
	}
}

func TestServiceOrderTimelineIncludesActionsAndRewards(t *testing.T) {
	created := time.Date(2026, 7, 9, 10, 0, 0, 0, time.UTC)
	updated := created.Add(2 * time.Hour)
	refunded := created.Add(3 * time.Hour)
	order := Order{ID: 3, OutOrderNo: "ORD3", Status: "paid", CreatedAt: created}
	refund := &OrderRefund{ID: 9, Status: "success", RefundAmount: 500, CreatedAt: refunded}
	actions := []map[string]any{{
		"id":           int64(7),
		"action_type":  "instance_delete",
		"status":       "submitted",
		"remark":       "delete submitted",
		"created_at":   updated,
		"status_label": "已提交",
	}}
	rewards := []map[string]any{{
		"id":          int64(8),
		"status":      "claimable",
		"points":      30,
		"instance_id": int64(4),
		"created_at":  created.Add(time.Hour),
	}}

	got := (&Service{}).serviceOrderTimeline(order, []Instance{{ID: 4, Status: "active", UpdatedAt: updated}}, refund, actions, rewards)
	if len(got) != 5 {
		t.Fatalf("timeline length = %d, want 5: %#v", len(got), got)
	}
	if got[0]["type"] != "refund" {
		t.Fatalf("first timeline item = %#v, want refund first", got[0])
	}
	foundAction := false
	foundReward := false
	for _, item := range got {
		foundAction = foundAction || item["type"] == "upstream_action"
		foundReward = foundReward || item["type"] == "point_reward"
		if item["key"] == nil || item["kind"] == nil || item["description"] == nil || item["at"] == nil || item["ref_id"] == nil {
			t.Fatalf("timeline compatibility fields missing: %#v", item)
		}
	}
	if !foundAction || !foundReward {
		t.Fatalf("timeline missing action/reward: %#v", got)
	}
}
