package bootstrap

import (
	"encoding/json"
	"os"
	"testing"
)

func TestFieldNameToLabel(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// snake_case
		{"user_first_name", "User First Name"},
		{"expires_at", "Expires At"},
		{"is_deleted", "Is Deleted"},
		// camelCase / PascalCase
		{"ExpiresAt", "Expires At"},
		{"FirstName", "First Name"},
		{"IsDeleted", "Is Deleted"},
		{"LastModifiedDate", "Last Modified Date"},
		{"SystemModstamp", "System Modstamp"},
		// Reference fields — trailing Id stripped
		{"AccountId", "Account"},
		{"OwnerId", "Owner"},
		// Custom field suffix __c
		{"Partner_Margin__c", "Partner Margin"},
		{"Net_Fees__c", "Net Fees"},
		{"my_custom_field__c", "My Custom Field"},
		// Single word
		{"Name", "Name"},
		{"name", "Name"},
		// Short names — Id not stripped when name is too short
		{"Id", "Id"},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := fieldNameToLabel(tt.input)
			if got != tt.expected {
				t.Errorf("fieldNameToLabel(%q) = %q, want %q", tt.input, got, tt.expected)
			}
		})
	}
}

func TestSystemTablesAllFieldsHaveLabels(t *testing.T) {
	data, err := os.ReadFile("system_tables.json")
	if err != nil {
		t.Fatalf("Failed to read system_tables.json: %v", err)
	}

	var tables []struct {
		TableName string `json:"tableName"`
		Columns   []struct {
			Name  string `json:"name"`
			Label string `json:"label"`
		} `json:"columns"`
	}
	if err := json.Unmarshal(data, &tables); err != nil {
		t.Fatalf("Failed to parse system_tables.json: %v", err)
	}

	missingCount := 0
	for _, table := range tables {
		for _, col := range table.Columns {
			if col.Label == "" {
				missingCount++
				if missingCount <= 10 {
					t.Errorf("%s.%s: missing label property", table.TableName, col.Name)
				}
			}
		}
	}
	if missingCount > 10 {
		t.Errorf("... and %d more fields missing labels (total: %d)", missingCount-10, missingCount)
	}
	if missingCount > 0 {
		t.Fatalf("TOTAL: %d fields in system_tables.json are missing the 'label' property", missingCount)
	}
}

func TestCampaignMemberStatusCreatedByLabel(t *testing.T) {
	data, err := os.ReadFile("system_tables.json")
	if err != nil {
		t.Fatalf("Failed to read system_tables.json: %v", err)
	}

	var tables []struct {
		TableName string `json:"tableName"`
		Columns   []struct {
			Name             string   `json:"name"`
			Label            string   `json:"label"`
			LogicalType      string   `json:"logicalType"`
			ReferenceTo      []string `json:"referenceTo"`
			RelationshipName string   `json:"relationshipName"`
		} `json:"columns"`
	}
	if err := json.Unmarshal(data, &tables); err != nil {
		t.Fatalf("Failed to parse system_tables.json: %v", err)
	}

	for _, table := range tables {
		if table.TableName != "CampaignMemberStatus" {
			continue
		}
		for _, col := range table.Columns {
			if col.Name != "CreatedById" {
				continue
			}
			if col.Label != "Created By" {
				t.Fatalf("CampaignMemberStatus.CreatedById label = %q, want %q", col.Label, "Created By")
			}
			if col.LogicalType != "reference" {
				t.Fatalf("CampaignMemberStatus.CreatedById logicalType = %q, want %q", col.LogicalType, "reference")
			}
			if len(col.ReferenceTo) != 1 || col.ReferenceTo[0] != "User" {
				t.Fatalf("CampaignMemberStatus.CreatedById referenceTo = %#v, want %#v", col.ReferenceTo, []string{"User"})
			}
			if col.RelationshipName != "CreatedBy" {
				t.Fatalf("CampaignMemberStatus.CreatedById relationshipName = %q, want %q", col.RelationshipName, "CreatedBy")
			}
			return
		}
		t.Fatal("CampaignMemberStatus.CreatedById not found in system_tables.json")
	}

	t.Fatal("CampaignMemberStatus not found in system_tables.json")
}

// TestSystemTableEffectiveLabelFitsObjectLabelColumn fails closed when any
// table in system_tables.json would overflow _System_Object.Label (VARCHAR(255))
// at phase2_object_metadata insert time. schema.go registers each table with
// `Label = def.Label (if set) else def.Description else def.TableName`, so a
// table with an empty Label and an explanation > 255 chars silently becomes
// a 1406 "Data too long" at apply time — blocking setup. Each new table must
// either (a) keep Description ≤ 255 chars, or (b) declare an explicit short
// "label" that fits the column. This test enforces (a)|(b) at CI time rather
// than at staging deploy time.
func TestSystemTableEffectiveLabelFitsObjectLabelColumn(t *testing.T) {
	const objectLabelColumnMax = 255 // _System_Object.Label type is VARCHAR(255)

	data, err := os.ReadFile("system_tables.json")
	if err != nil {
		t.Fatalf("Failed to read system_tables.json: %v", err)
	}

	var tables []struct {
		TableName   string `json:"tableName"`
		Label       string `json:"label"`
		Description string `json:"description"`
	}
	if err := json.Unmarshal(data, &tables); err != nil {
		t.Fatalf("Failed to parse system_tables.json: %v", err)
	}

	// Mirror the fallback chain in schema.go:1246-1252 so this test tracks
	// the exact bytes that land in _System_Object.Label.
	effectiveLabel := func(label, description, tableName string) string {
		if label != "" {
			return label
		}
		if description != "" {
			return description
		}
		return tableName
	}

	for _, table := range tables {
		eff := effectiveLabel(table.Label, table.Description, table.TableName)
		if len(eff) > objectLabelColumnMax {
			t.Errorf("%s: effective label length %d exceeds _System_Object.Label cap %d — add an explicit short \"label\" or shorten the description",
				table.TableName, len(eff), objectLabelColumnMax)
		}
	}
}

func TestDashboardObjectLabels(t *testing.T) {
	data, err := os.ReadFile("system_tables.json")
	if err != nil {
		t.Fatalf("Failed to read system_tables.json: %v", err)
	}

	var tables []struct {
		TableName string `json:"tableName"`
		Label     string `json:"label"`
	}
	if err := json.Unmarshal(data, &tables); err != nil {
		t.Fatalf("Failed to parse system_tables.json: %v", err)
	}

	expected := map[string]string{
		"Dashboard":          "Dashboard",
		"DashboardComponent": "Dashboard Component",
	}

	for tableName, wantLabel := range expected {
		found := false
		for _, table := range tables {
			if table.TableName != tableName {
				continue
			}
			found = true
			if table.Label != wantLabel {
				t.Fatalf("%s label = %q, want %q", tableName, table.Label, wantLabel)
			}
			break
		}
		if !found {
			t.Fatalf("%s not found in system_tables.json", tableName)
		}
	}
}
