package rest

import (
	"encoding/json"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/nexuscrm/shared/pkg/api"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// TestAPIContractDrift ensures that the Go DTOs match the api_contracts.json definitions.
// This prevents "Contract Drift" where the single source of truth is updated but codegen is skipped.
func TestAPIContractDrift(t *testing.T) {
	// 1. Read api_contracts.json from valid relative path
	// Assuming running from backend/internal/interfaces/rest
	path := "../../bootstrap/api_contracts.json"
	byteValue, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("Failed to read api_contracts.json: %v", err)
	}

	var contract struct {
		Definitions []struct {
			Name   string `json:"name"`
			Fields []struct {
				Name string `json:"name"`
			} `json:"fields"`
		} `json:"definitions"`
	}
	if err := json.Unmarshal(byteValue, &contract); err != nil {
		t.Fatalf("Failed to parse api_contracts.json: %v", err)
	}

	// 2. Registry of DTOs to check
	registry := map[string]interface{}{
		"QueryRequest":   api.QueryRequest{},
		"LoginResponse":  api.LoginResponse{},
		"UserDTO":        api.UserDTO{},
		"SearchResult":   api.SearchResult{},
		"AnalyticsQuery": api.AnalyticsQuery{},
	}

	// 3. Verify
	for _, def := range contract.Definitions {
		dto, ok := registry[def.Name]
		if !ok {
			// Skip definitions not in our manual registry
			continue
		}

		val := reflect.TypeOf(dto)
		for _, f := range def.Fields {
			found := false
			for i := 0; i < val.NumField(); i++ {
				field := val.Field(i)
				// Extract JSON tag name (handle "name,omitempty")
				jsonTag := field.Tag.Get("json")
				if len(jsonTag) > len(f.Name) && jsonTag[len(f.Name)] == ',' {
					jsonTag = jsonTag[:len(f.Name)]
				}

				if jsonTag == f.Name {
					found = true
					break
				}
			}
			if !found {
				t.Errorf("⚠️ Contract Drift: Definition '%s' has field '%s' in JSON but not in Go Struct", def.Name, f.Name)
			}
		}
	}
}

// TestStrictInputValidation verifies the private validateInput method in SObjectHandler
func TestStrictInputValidation(t *testing.T) {
	// Setup Handler (nil Manager is fine for this pure logic test)
	h := &SObjectHandler{}

	// Setup Schema
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "Name", Type: constants.FieldTypeString},
			{APIName: "Industry", Type: constants.FieldTypePicklist},
		},
	}

	tests := []struct {
		name      string
		input     map[string]interface{}
		expectErr bool
	}{
		{
			name: "Valid Input",
			input: map[string]interface{}{
				"Name":     "Acme Corp",
				"Industry": "Tech",
			},
			expectErr: false,
		},
		{
			name: "Unknown Field",
			input: map[string]interface{}{
				"Name":         "Acme Corp",
				"UnknownField": "Value",
			},
			expectErr: true,
		},
		{
			name: "System Field (Id)",
			input: map[string]interface{}{
				"Name": "Acme Corp",
				"id":   "123",
			},
			expectErr: true,
		},
		{
			name: "System Field (CreatedDate)",
			input: map[string]interface{}{
				"Name":         "Acme Corp",
				"created_date": time.Now(),
			},
			expectErr: true,
		},
		{
			name: "System Field (SystemModstamp)",
			input: map[string]interface{}{
				"Name":           "Acme Corp",
				"SystemModstamp": "123",
			},
			expectErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := h.validateInput(schema, tt.input)
			if tt.expectErr && err == nil {
				t.Error("Expected error, got nil")
			}
			if !tt.expectErr && err != nil {
				t.Errorf("Expected success, got error: %v", err)
			}
		})
	}
}
