package backend

import (
	"context"
	"errors"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/mem9-ai/drive9/pkg/metrics"
)

func TestRecordTenantQuotaSnapshotDoesNotExposeExtractionQuotaMetrics(t *testing.T) {
	const tenantID = "tenant-quota-internal-extraction-limits"
	t.Cleanup(func() { metrics.DeleteTenantCounters(tenantID) })

	recordTenantQuotaSnapshot(tenantID, "org-internal-extraction-limits", &QuotaUsageView{
		StorageBytes: 10, MediaFileCount: 2, VideoFileCount: 1,
	}, &QuotaConfigView{
		MaxStorageBytes: 100, MaxMediaLLMFiles: 20, MaxVideoLLMFiles: 10,
	})

	rec := httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	text := rec.Body.String()
	for _, metric := range []string{
		"drive9_tenant_media_files",
		"drive9_tenant_video_files",
	} {
		if strings.Contains(text, metric) {
			t.Fatalf("internal extraction quota metric %s is exported:\n%s", metric, text)
		}
	}
}

func TestRecordTenantQuotaSnapshotReplacesOldLimitsAndOrgLabels(t *testing.T) {
	const tenantID = "tenant-quota-snapshot-replace"

	recordTenantQuotaSnapshot(tenantID, "org-old", &QuotaUsageView{
		StorageBytes: 10,
	}, &QuotaConfigView{
		MaxStorageBytes: 100,
	})
	recordTenantQuotaSnapshot(tenantID, "org-new", &QuotaUsageView{
		StorageBytes: 11,
	}, &QuotaConfigView{})

	rec := httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	text := rec.Body.String()
	for _, line := range strings.Split(text, "\n") {
		if !strings.Contains(line, `tenant_id="`+tenantID+`"`) {
			continue
		}
		if strings.Contains(line, `tidbcloud_org_id="org-old"`) {
			t.Fatalf("old org quota series remains exported: %s", line)
		}
		if strings.Contains(line, `state="limit"`) {
			t.Fatalf("removed quota limit remains exported: %s", line)
		}
	}
	if !strings.Contains(text, `drive9_tenant_storage_bytes{state="confirmed",storage_type="all",tenant_id="`+tenantID+`",tidbcloud_org_id="org-new"} 11.000000`) {
		t.Fatalf("new quota usage snapshot missing:\n%s", text)
	}
}

func TestRecordTenantQuotaSnapshotUsesSplitStorageOnlyAfterExplicitReadiness(t *testing.T) {
	const tenantID = "tenant-quota-ready-split"
	const orgID = "org-quota-ready-split"
	dbBytes := int64(4)
	s3Bytes := int64(6)
	t.Cleanup(func() { metrics.DeleteTenantCounters(tenantID) })

	usage := &QuotaUsageView{
		StorageBytes: 10, DBStorageBytes: &dbBytes, S3StorageBytes: &s3Bytes,
	}
	recordTenantQuotaSnapshot(tenantID, orgID, usage, &QuotaConfigView{MaxStorageBytes: 100})
	rec := httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	if !strings.Contains(rec.Body.String(), `drive9_tenant_storage_bytes{state="confirmed",storage_type="all",tenant_id="`+tenantID+`"`) {
		t.Fatalf("counters without explicit readiness were split:\n%s", rec.Body.String())
	}

	usage.StorageTierReady = true
	recordTenantQuotaSnapshot(tenantID, orgID, usage, &QuotaConfigView{MaxStorageBytes: 100})
	rec = httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	text := rec.Body.String()
	if strings.Contains(text, `drive9_tenant_storage_bytes{state="confirmed",storage_type="all",tenant_id="`+tenantID+`"`) {
		t.Fatalf("confirmed/all remained after explicit readiness:\n%s", text)
	}
	for _, want := range []string{
		`drive9_tenant_storage_bytes{state="confirmed",storage_type="db9",tenant_id="` + tenantID + `",tidbcloud_org_id="` + orgID + `"} 4.000000`,
		`drive9_tenant_storage_bytes{state="confirmed",storage_type="s3",tenant_id="` + tenantID + `",tidbcloud_org_id="` + orgID + `"} 6.000000`,
	} {
		if !strings.Contains(text, want) {
			t.Fatalf("missing ready storage series %q:\n%s", want, text)
		}
	}
}

func TestObserveTenantFileCountsRefreshesFileCountGauge(t *testing.T) {
	const tenantID = "tenant-file-count-observe"
	const orgID = "org-file-count-observe"
	t.Cleanup(func() { metrics.DeleteTenantCounters(tenantID) })

	fake := newFakeMetaQuotaStore()
	dbFiles := int64(4)
	s3Files := int64(2)
	fake.tierFileCounts[tenantID] = &TierFileCountView{
		FileCount: 6, DBFileCount: &dbFiles, S3FileCount: &s3Files, TierReady: true,
	}
	b := &Dat9Backend{}
	b.metaStore = fake
	b.SetTenantMetricScope(tenantID, orgID)

	b.ObserveTenantFileCounts(context.Background())

	rec := httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	text := rec.Body.String()
	for _, want := range []string{
		`drive9_tenant_file_count{state="confirmed",storage_type="db9",tenant_id="` + tenantID + `",tidbcloud_org_id="` + orgID + `"} 4.000000`,
		`drive9_tenant_file_count{state="confirmed",storage_type="s3",tenant_id="` + tenantID + `",tidbcloud_org_id="` + orgID + `"} 2.000000`,
	} {
		if !strings.Contains(text, want) {
			t.Fatalf("missing file count series %q:\n%s", want, text)
		}
	}

	// A failing store must not churn the previously exported series.
	fake.getTierFileCountsErr = errors.New("boom")
	b.ObserveTenantFileCounts(context.Background())
	rec = httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	if !strings.Contains(rec.Body.String(), `drive9_tenant_file_count{state="confirmed",storage_type="db9",tenant_id="`+tenantID+`"`) {
		t.Fatalf("file count series disappeared after observe failure:\n%s", rec.Body.String())
	}
}

func TestRecordTenantQuotaSnapshotPreservesKnownLimitsWhenConfigUnavailable(t *testing.T) {
	const (
		tenantID = "tenant-quota-snapshot-config-unavailable"
		orgID    = "org-quota-snapshot-config-unavailable"
	)
	recordTenantQuotaSnapshot(tenantID, orgID, &QuotaUsageView{
		StorageBytes: 10,
	}, &QuotaConfigView{
		MaxStorageBytes: 100,
	})
	recordTenantQuotaSnapshot(tenantID, orgID, &QuotaUsageView{
		StorageBytes: 11,
	}, nil)

	rec := httptest.NewRecorder()
	metrics.WritePrometheus(rec)
	text := rec.Body.String()
	want := `drive9_tenant_storage_bytes{state="limit",storage_type="all",tenant_id="` + tenantID + `",tidbcloud_org_id="` + orgID + `"} 100.000000`
	if !strings.Contains(text, want) {
		t.Fatalf("known quota limit disappeared while config was unavailable; missing %q:\n%s", want, text)
	}
}
