package models

import (
	"go/ast"
	"go/parser"
	"go/token"
	"reflect"
	"testing"
)

func TestQueryResultIsNotWireDTO(t *testing.T) {
	assertNoWireTags(t, reflect.TypeOf(QueryResult{}))
}

func TestSearchResultIsNotWireDTO(t *testing.T) {
	assertNoWireTags(t, reflect.TypeOf(SearchResult{}))
}

func TestAnalyticsQueryIsNotWireDTO(t *testing.T) {
	assertNoWireTags(t, reflect.TypeOf(AnalyticsQuery{}))
}

func TestReportExecutionModelsAreNotWireDTOs(t *testing.T) {
	for _, typ := range []reflect.Type{
		reflect.TypeOf(ReportDefinition{}),
		reflect.TypeOf(ReportDateFilter{}),
		reflect.TypeOf(ReportFilter{}),
		reflect.TypeOf(ReportAggregate{}),
		reflect.TypeOf(ReportResult{}),
	} {
		assertNoWireTags(t, typ)
	}
}

func TestReportDefinitionDoesNotExposeLegacyReportFormatAlias(t *testing.T) {
	if _, ok := reflect.TypeOf(ReportDefinition{}).FieldByName("ReportFormat"); ok {
		t.Fatalf("ReportDefinition still exposes legacy ReportFormat alias; use DisplayMode and map Salesforce reportFormat in dialect code")
	}
}

func TestReportDefinitionDoesNotExposeLegacyReportTypeAlias(t *testing.T) {
	if _, ok := reflect.TypeOf(ReportDefinition{}).FieldByName("ReportType"); ok {
		t.Fatalf("ReportDefinition still exposes legacy ReportType alias; use SourceRef and map Salesforce reportType in dialect code")
	}
}

func TestReportDefinitionDoesNotExposeLegacyBooleanFilterAlias(t *testing.T) {
	if _, ok := reflect.TypeOf(ReportDefinition{}).FieldByName("BooleanFilter"); ok {
		t.Fatalf("ReportDefinition still exposes legacy BooleanFilter alias; use FilterLogic and map Salesforce booleanFilter in dialect code")
	}
}

func TestReportDefinitionDoesNotExposeLegacyStandardDateFilterAlias(t *testing.T) {
	if _, ok := reflect.TypeOf(ReportDefinition{}).FieldByName("StandardDateFilter"); ok {
		t.Fatalf("ReportDefinition still exposes legacy StandardDateFilter alias; use DateFilter and map Salesforce standardDateFilter in dialect code")
	}
}

func TestSharedModelsDoNotExportStandardDateFilterAlias(t *testing.T) {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "analytics.go", nil, 0)
	if err != nil {
		t.Fatalf("parse analytics.go: %v", err)
	}
	for _, decl := range file.Decls {
		genDecl, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, spec := range genDecl.Specs {
			typeSpec, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}
			if typeSpec.Name.Name == "StandardDateFilter" {
				t.Fatalf("shared models still export StandardDateFilter; use ReportDateFilter and keep Salesforce standardDateFilter vocabulary in dialect code")
			}
		}
	}
}

func TestQueryRequestCarriersAreNotWireDTOs(t *testing.T) {
	for _, typ := range []reflect.Type{
		reflect.TypeOf(QueryRequest{}),
		reflect.TypeOf(SubQueryRequest{}),
		reflect.TypeOf(QueryCriterion{}),
		reflect.TypeOf(SortCriterion{}),
	} {
		assertNoWireTags(t, typ)
	}
}

func assertNoWireTags(t *testing.T, typ reflect.Type) {
	t.Helper()
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		if tag := field.Tag.Get("json"); tag != "" {
			t.Fatalf("%s.%s has json tag %q; dialect mappers own query wire shape", typ.Name(), field.Name, tag)
		}
		if tag := field.Tag.Get("binding"); tag != "" {
			t.Fatalf("%s.%s has binding tag %q; wire binding belongs in API DTOs or dialect mappers", typ.Name(), field.Name, tag)
		}
	}
}
