package databi

import (
	"context"
	"errors"
	"reflect"
	"testing"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/repository"
)

func TestListAppsPreservesLegacyLogoURL(t *testing.T) {
	service := NewService(nil, nil, nil, nil, WithLegacyApps(LegacyAppDescriptor{
		AppCode: "Aslan",
		AppName: "Aslan",
		LogoURL: "https://media.example.com/aslan.png",
	}))

	apps, err := service.listApps(context.Background())
	if err != nil {
		t.Fatalf("list apps failed: %v", err)
	}
	if len(apps) != 1 || apps[0].AppCode != "aslan" || apps[0].LogoURL != "https://media.example.com/aslan.png" {
		t.Fatalf("legacy app logo mismatch: %+v", apps)
	}
}

func TestFilterAppTrackingFunnelAppsUsesRegistryKind(t *testing.T) {
	registered := AppInfo{AppCode: "new-social-product", AppName: "New Social Product", Kind: appKindHyapp}
	legacy := AppInfo{AppCode: "external-product", AppName: "External Product", Kind: appKindLegacy}

	got := filterAppTrackingFunnelApps([]AppInfo{legacy, registered})
	if !reflect.DeepEqual(got, []AppInfo{registered}) {
		t.Fatalf("expected every registered hyapp and no legacy app, got %+v", got)
	}
}

func testAccess() repository.MoneyAccess {
	return repository.MoneyAccess{
		UserID: 7,
		Scopes: []model.UserMoneyScope{
			{UserID: 7, AppCode: "lalu", RegionID: 0},
			{UserID: 7, AppCode: "aslan", RegionID: 11},
			{UserID: 7, AppCode: "aslan", RegionID: 12},
		},
	}
}

func mustLocation(t *testing.T, name string) *time.Location {
	t.Helper()
	location, err := time.LoadLocation(name)
	if err != nil {
		t.Fatalf("load location %s: %v", name, err)
	}
	return location
}

func TestRegionDisplay(t *testing.T) {
	regions := []RegionInfo{{AppCode: "aslan", RegionID: 11, RegionCode: "MENA", RegionName: "中东"}}
	if _, name := regionDisplay(regions, 0); name != "全部区域" {
		t.Fatalf("expected 全部区域 for region 0, got %s", name)
	}
	if code, name := regionDisplay(regions, 11); code != "MENA" || name != "中东" {
		t.Fatalf("unexpected display for region 11: %s %s", code, name)
	}
	if _, name := regionDisplay(regions, 99); name != "区域 99" {
		t.Fatalf("expected fallback name for unknown region, got %s", name)
	}
}

func TestTopLevelMetricsStripsStructuralKeys(t *testing.T) {
	day1Rate := 0.5
	out := topLevelMetrics(map[string]any{
		"recharge_usd_minor":      int64(5),
		"daily_series":            []any{"x"},
		"country_breakdown":       []any{"y"},
		"daily_country_breakdown": []any{"z"},
		"report_metric_sources":   []any{},
		"retention": map[string]any{
			"day1_base_users": int64(6),
			"day1_rate":       day1Rate,
			"day1_users":      int64(3),
		},
	})
	if _, ok := out["daily_series"]; ok {
		t.Fatalf("expected daily_series stripped")
	}
	if out["recharge_usd_minor"] != int64(5) {
		t.Fatalf("expected metric kept, got %v", out["recharge_usd_minor"])
	}
	if out["d1_retention_rate"] != day1Rate || out["d1_retention_users"] != int64(3) || out["d1_retention_base_users"] != int64(6) {
		t.Fatalf("expected nested retention flattened, got %#v", out)
	}
}

// legacy 雪花区域 ID 超出 JS 安全整数，历史 scope 里存的是 float64 圆整值；
// 归一与展示必须能容差还原（线上「区域 20453...600」事故的回归测试）。
func TestRegionDisplayToleratesRoundedIDs(t *testing.T) {
	exact := int64(2049084769873498113)
	rounded := int64(float64(exact))
	if exact == rounded {
		t.Fatalf("fixture should lose precision")
	}
	regions := []RegionInfo{{AppCode: "aslan", RegionID: exact, RegionCode: "AR", RegionName: "阿语区"}}
	if _, name := regionDisplay(regions, rounded); name != "阿语区" {
		t.Fatalf("expected rounded id to resolve region name, got %s", name)
	}
	if got := normalizeRegionID(regions, rounded); got != exact {
		t.Fatalf("expected normalize to exact id, got %d", got)
	}
	if !regionInCatalog(regions, rounded) {
		t.Fatalf("expected rounded id to match catalog")
	}
}

func TestMergeAndEnrichCountryDirectory(t *testing.T) {
	laluDirectory := map[int64]countryDirectoryEntry{}
	mergeCountryDirectory(laluDirectory, "lalu", []*userclient.Country{
		{CountryID: 158, CountryCode: "PK", CountryName: "Pakistan", CountryDisplayName: "巴基斯坦", Flag: "🇵🇰", Enabled: true},
		// disabled 国家仍可能出现在历史统计中，必须进入目录。
		{CountryID: 157, CountryCode: "BS", CountryName: "Philippines", CountryDisplayName: "菲律宾", Enabled: false},
		// country_id=0 是未知维度，不允许目录把它伪装成真实国家。
		{CountryID: 0, CountryCode: "UNKNOWN", CountryName: "Unknown", Enabled: true},
	})
	famiDirectory := map[int64]countryDirectoryEntry{}
	mergeCountryDirectory(famiDirectory, "fami", []*userclient.Country{
		{CountryID: 1163, CountryCode: "BD", CountryName: "Bangladesh", CountryDisplayName: "孟加拉国", Enabled: true},
	})
	huwaaDirectory := map[int64]countryDirectoryEntry{}
	mergeCountryDirectory(huwaaDirectory, "huwaa", []*userclient.Country{
		{CountryID: 953, CountryCode: "IN", CountryName: "India", CountryDisplayName: "印度", Enabled: true},
	})

	if len(laluDirectory) != 2 {
		t.Fatalf("expected two lalu country ids, got %#v", laluDirectory)
	}
	if country := laluDirectory[158]; country.AppCode != "lalu" || country.Name != "Pakistan" {
		t.Fatalf("expected lalu country retained, got %+v", country)
	}
	if country := laluDirectory[157]; country.Enabled {
		t.Fatalf("expected disabled country retained, got %+v", country)
	}

	sourceRows := []map[string]any{
		{"country_id": int64(158)},
		{"country_id": int64(1163)},
		{"country_id": int64(953)},
		{"country_id": int64(0)},
		{"country_id": int64(9999)},
	}
	rows := enrichCountryRows(sourceRows, laluDirectory, false)
	if got := rowString(rows[0], "country_name"); got != "巴基斯坦" {
		t.Fatalf("expected display name to win for country 158, got %q", got)
	}
	for _, index := range []int{1, 2, 3, 4} {
		if got := rowString(rows[index], "country_name"); got != unknownCountryName {
			t.Fatalf("expected row %d to remain unknown in lalu scope, got %q", index, got)
		}
		if got := rowString(rows[index], "country_display_name"); got != unknownCountryName {
			t.Fatalf("expected row %d unknown display name, got %q", index, got)
		}
	}
	if got := rowString(enrichCountryRows([]map[string]any{{"country_id": int64(1163)}}, famiDirectory, false)[0], "country_name"); got != "孟加拉国" {
		t.Fatalf("expected fami country resolved only in fami scope, got %q", got)
	}
	if got := rowString(enrichCountryRows([]map[string]any{{"country_id": int64(953)}}, huwaaDirectory, false)[0], "country_name"); got != "印度" {
		t.Fatalf("expected huwaa country resolved only in huwaa scope, got %q", got)
	}
	if _, mutated := sourceRows[0]["country_name"]; mutated {
		t.Fatalf("expected enrichment to copy shared statistics rows")
	}

	legacyRows := enrichCountryRows([]map[string]any{
		{
			"country_id":   int64(158),
			"country_code": "LEGACY-PK",
			"country_name": "Legacy Pakistan",
		},
		{"country_id": int64(158), "country": "Legacy country field"},
		{"country_id": int64(158), "country_code": "LEGACY-CODE"},
		{"country_id": int64(158)},
	}, laluDirectory, true)
	if got := rowString(legacyRows[0], "country_name"); got != "Legacy Pakistan" {
		t.Fatalf("expected legacy source name preserved, got %q", got)
	}
	if got := rowString(legacyRows[0], "country_code"); got != "LEGACY-PK" {
		t.Fatalf("expected legacy source code preserved, got %q", got)
	}
	if got := rowString(legacyRows[1], "country_name"); got != "Legacy country field" {
		t.Fatalf("expected legacy country field preserved, got %q", got)
	}
	if got := rowString(legacyRows[2], "country_name"); got != "LEGACY-CODE" {
		t.Fatalf("expected legacy code used instead of colliding HyApp id, got %q", got)
	}
	if got := rowString(legacyRows[3], "country_name"); got != unknownCountryName {
		t.Fatalf("legacy numeric id must not borrow HyApp country name, got %q", got)
	}
}

type countryDirectoryClientStub struct {
	userclient.Client
	countries map[string][]*userclient.Country
	errors    map[string]error
	calls     []string
	filtered  bool
}

func (c *countryDirectoryClientStub) ListCountries(ctx context.Context, req userclient.ListCountriesRequest) ([]*userclient.Country, error) {
	appCode := appctx.FromContext(ctx)
	c.calls = append(c.calls, appCode)
	if req.Enabled != nil {
		c.filtered = true
	}
	if err := c.errors[appCode]; err != nil {
		return nil, err
	}
	return c.countries[appCode], nil
}

func TestCountryDirectoriesQueryEachAllowedHyappAndContinueAfterFailure(t *testing.T) {
	client := &countryDirectoryClientStub{
		countries: map[string][]*userclient.Country{
			"fami": {{CountryID: 1163, CountryCode: "BD", CountryName: "Bangladesh", CountryDisplayName: "孟加拉国", Enabled: true}},
			"lalu": {{CountryID: 158, CountryCode: "PK", CountryName: "Pakistan", CountryDisplayName: "巴基斯坦", Enabled: true}},
		},
		errors: map[string]error{"huwaa": errors.New("temporary user-service failure")},
	}
	service := NewService(nil, nil, nil, client)
	directories := service.countryDirectories(context.Background(), []AppInfo{
		{AppCode: "lalu", Kind: appKindHyapp},
		{AppCode: "aslan", Kind: appKindLegacy},
		{AppCode: "huwaa", Kind: appKindHyapp},
		{AppCode: "fami", Kind: appKindHyapp},
	})

	if want := []string{"fami", "huwaa", "lalu"}; !reflect.DeepEqual(client.calls, want) {
		t.Fatalf("expected each HyApp queried with its own context, got %v want %v", client.calls, want)
	}
	if client.filtered {
		t.Fatalf("expected enabled filter omitted so disabled history can resolve")
	}
	if _, ok := directories["fami"][1163]; !ok {
		t.Fatalf("expected successful app before failure retained")
	}
	if _, ok := directories["lalu"][158]; !ok {
		t.Fatalf("expected apps after failure still queried")
	}
	if _, ok := directories["huwaa"]; ok {
		t.Fatalf("failed app must not receive another app's directory: %#v", directories)
	}
	if len(directories) != 2 {
		t.Fatalf("unexpected directories after one app failure: %#v", directories)
	}
}
