package coinledger

import "testing"

func TestNormalizeListQueryCapsPageSize(t *testing.T) {
	query := normalizeListQuery(listQuery{Page: -1, PageSize: 1000, UserKeyword: "  10001  "})
	if query.Page != 1 || query.PageSize != 100 || query.UserKeyword != "10001" {
		t.Fatalf("normalized query mismatch: %+v", query)
	}
}

func TestCoinLedgerWhereUsesTimeWindowAndUserFilter(t *testing.T) {
	query := listQuery{StartAtMS: 100, EndAtMS: 200}
	where, args := coinLedgerWhere("lalu", query, []int64{11, 22})
	if want := "WHERE e.app_code = ? AND e.asset_type = ? AND e.created_at_ms >= ? AND e.created_at_ms < ? AND e.user_id IN (?,?)"; where != want {
		t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
	}
	if len(args) != 6 || args[0] != "lalu" || args[1] != coinAssetType || args[2] != int64(100) || args[3] != int64(200) || args[4] != int64(11) || args[5] != int64(22) {
		t.Fatalf("args mismatch: %#v", args)
	}
}

func TestCoinAdjustmentWhereLimitsManualCreditCoinEntries(t *testing.T) {
	query := listQuery{StartAtMS: 100, EndAtMS: 200}
	where, args := coinAdjustmentWhere("lalu", query, []int64{11})
	if want := "WHERE e.app_code = ? AND e.asset_type = ? AND wt.biz_type = ? AND e.created_at_ms >= ? AND e.created_at_ms < ? AND e.user_id IN (?)"; where != want {
		t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
	}
	if len(args) != 6 || args[0] != "lalu" || args[1] != coinAssetType || args[2] != coinManualCreditBizType || args[3] != int64(100) || args[4] != int64(200) || args[5] != int64(11) {
		t.Fatalf("args mismatch: %#v", args)
	}
}

func TestCoinSellerLedgerWhereUsesExactSellerAndType(t *testing.T) {
	query := coinSellerLedgerQuery{SellerUserID: 3001, LedgerType: coinSellerLedgerTypeSellerTransfer, StartAtMS: 100, EndAtMS: 200}
	where, args, err := coinSellerLedgerWhere("lalu", query, []int64{3001})
	if err != nil {
		t.Fatalf("coin seller ledger where failed: %v", err)
	}
	if want := "WHERE e.app_code = ? AND e.asset_type = ? AND wt.biz_type = ? AND e.created_at_ms >= ? AND e.created_at_ms < ? AND e.user_id IN (?)"; where != want {
		t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
	}
	if len(args) != 6 || args[0] != "lalu" || args[1] != coinSellerAssetType || args[2] != coinSellerTransferBizType || args[3] != int64(100) || args[4] != int64(200) || args[5] != int64(3001) {
		t.Fatalf("args mismatch: %#v", args)
	}
}

func TestCoinSellerLedgerWhereMapsAdminStockCredit(t *testing.T) {
	query := coinSellerLedgerQuery{LedgerType: coinSellerLedgerTypeAdminStockCredit}
	where, args, err := coinSellerLedgerWhere("lalu", query, []int64{3001, 3002})
	if err != nil {
		t.Fatalf("coin seller ledger where failed: %v", err)
	}
	if want := "WHERE e.app_code = ? AND e.asset_type = ? AND wt.biz_type IN (?,?) AND e.user_id IN (?,?)"; where != want {
		t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
	}
	if len(args) != 6 || args[0] != "lalu" || args[1] != coinSellerAssetType || args[2] != coinSellerStockPurchaseBizType || args[3] != coinSellerCoinCompensationBizType || args[4] != int64(3001) || args[5] != int64(3002) {
		t.Fatalf("args mismatch: %#v", args)
	}
}

func TestCoinSellerLedgerWhereEmptyTypeUsesAllPublicTypes(t *testing.T) {
	where, args, err := coinSellerLedgerWhere("lalu", coinSellerLedgerQuery{}, nil)
	if err != nil {
		t.Fatalf("coin seller ledger where failed: %v", err)
	}
	if want := "WHERE e.app_code = ? AND e.asset_type = ? AND wt.biz_type IN (?,?,?,?)"; where != want {
		t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
	}
	if len(args) != 6 || args[0] != "lalu" || args[1] != coinSellerAssetType || args[2] != coinSellerStockPurchaseBizType || args[3] != coinSellerCoinCompensationBizType || args[4] != coinSellerTransferBizType || args[5] != salaryTransferToCoinSellerBizType {
		t.Fatalf("args mismatch: %#v", args)
	}
}

func TestCoinSellerLedgerWhereRejectsInvalidType(t *testing.T) {
	_, _, err := coinSellerLedgerWhere("lalu", coinSellerLedgerQuery{LedgerType: "bad_type"}, nil)
	if err == nil {
		t.Fatalf("expected invalid ledger type error")
	}
}

func TestCoinSellerLedgerReceiverUserIDUsesActualReceiver(t *testing.T) {
	metadata := map[string]any{"target_user_id": float64(4001)}
	if got := coinSellerLedgerReceiverUserID(coinSellerTransferBizType, 3001, 0, metadata); got != 4001 {
		t.Fatalf("seller transfer receiver mismatch: %d", got)
	}
	if got := coinSellerLedgerReceiverUserID(coinSellerStockPurchaseBizType, 3001, 4001, metadata); got != 3001 {
		t.Fatalf("admin stock receiver mismatch: %d", got)
	}
	if got := coinSellerLedgerReceiverUserID(salaryTransferToCoinSellerBizType, 3001, 4001, metadata); got != 3001 {
		t.Fatalf("salary transfer receiver mismatch: %d", got)
	}
}

func TestCoinSellerLedgerOperatorExportFieldsOnlyForAdminStock(t *testing.T) {
	adminItem := coinSellerLedgerDTO{
		LedgerType:     coinSellerLedgerTypeAdminStockCredit,
		OperatorUserID: "7",
		Operator:       coinAdjustmentOperatorDTO{AdminID: "7", Username: "hyappadmin", Name: "Admin"},
	}
	operatorName, operatorID := coinSellerLedgerOperatorExportFields(adminItem)
	if operatorName != "hyappadmin" || operatorID != "7" {
		t.Fatalf("operator fields mismatch: name=%q id=%q", operatorName, operatorID)
	}

	transferItem := coinSellerLedgerDTO{LedgerType: coinSellerLedgerTypeSellerTransfer, OperatorUserID: "7"}
	operatorName, operatorID = coinSellerLedgerOperatorExportFields(transferItem)
	if operatorName != "" || operatorID != "" {
		t.Fatalf("seller transfer should not export operator: name=%q id=%q", operatorName, operatorID)
	}
}

func TestNormalizeCoinSellerLedgerQueryCapsPageSize(t *testing.T) {
	query := normalizeCoinSellerLedgerQuery(coinSellerLedgerQuery{Page: -1, PageSize: 1000, SellerKeyword: "  164425  ", LedgerType: " seller_transfer "})
	if query.Page != 1 || query.PageSize != 100 || query.SellerKeyword != "164425" || query.LedgerType != coinSellerLedgerTypeSellerTransfer {
		t.Fatalf("normalized query mismatch: %+v", query)
	}
}

func TestDirectionAndAmountForDelta(t *testing.T) {
	if directionForDelta(-9) != directionOut || absInt64(-9) != 9 {
		t.Fatalf("expense projection mismatch")
	}
	if directionForDelta(12) != directionIn || absInt64(12) != 12 {
		t.Fatalf("income projection mismatch")
	}
}

func TestParseMetadataJSON(t *testing.T) {
	metadata, err := parseMetadataJSON(`{"gift_name":"Rose","gift_count":2,"target_user_id":1002}`)
	if err != nil {
		t.Fatalf("parse metadata failed: %v", err)
	}
	if metadata["gift_name"] != "Rose" || metadata["gift_count"] != float64(2) || metadata["target_user_id"] != float64(1002) {
		t.Fatalf("metadata mismatch: %#v", metadata)
	}

	empty, err := parseMetadataJSON("null")
	if err != nil {
		t.Fatalf("parse empty metadata failed: %v", err)
	}
	if len(empty) != 0 {
		t.Fatalf("empty metadata mismatch: %#v", empty)
	}
}
