package automation

import (
	"context"
	"encoding/json"
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
	contractconstants "github.com/pingcap-inc/nexuscrm-contract/pkg/constants"
	contractmodels "github.com/pingcap-inc/nexuscrm-contract/pkg/models"
)

func TestConnectorInboundExecutionAuditRowCorrelatesAndRedacts(t *testing.T) {
	ctx := contractconstants.WithConnectorInboundTrace(context.Background(), contractconstants.ConnectorInboundTrace{
		WorkItemID:         "work-1",
		EventID:            "event-1",
		Attempt:            3,
		Namespace:          "crm-pack",
		ObjectKind:         "contacts",
		SourceIDHash:       "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
		HydratedRecordHash: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
		HydratedFieldCount: 3,
		Related: []contractconstants.ConnectorInboundTraceItem{{
			WorkItemID:         "work-2",
			EventID:            "event-2",
			Attempt:            5,
			ObjectKind:         "contacts",
			SourceIDHash:       "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
			HydratedRecordHash: "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
			HydratedFieldCount: 2,
		}},
	})
	row, ok := connectorInboundExecutionAuditRow(ctx, &models.FlowInterview{
		Id:            "flow-execution-1",
		FlowVersionId: "flow-version-1",
	})
	if !ok {
		t.Fatal("connectorInboundExecutionAuditRow() ok = false, want true")
	}
	if row.FlowExecutionId != "flow-execution-1" || row.FlowVersionId != "flow-version-1" || row.ElementType != "ConnectorInboundContext" {
		t.Fatalf("row identity = %#v", row)
	}
	var payload struct {
		Namespace string `json:"namespace"`
		Links     []struct {
			WorkItemID string `json:"workItemId"`
			EventID    string `json:"eventId"`
			Attempt    int    `json:"attempt"`
		} `json:"links"`
	}
	if err := json.Unmarshal([]byte(row.InputJSON), &payload); err != nil {
		t.Fatalf("unmarshal InputJSON: %v", err)
	}
	if payload.Namespace != "crm-pack" || len(payload.Links) != 2 || payload.Links[0].WorkItemID != "work-1" || payload.Links[0].Attempt != 3 || payload.Links[1].EventID != "event-2" || payload.Links[1].Attempt != 5 {
		t.Fatalf("payload = %#v", payload)
	}
	for _, forbidden := range []string{"hubspot-contact-123", "00Q123456789ABC", "Email", "salesforceleadid"} {
		if strings.Contains(row.InputJSON, forbidden) {
			t.Fatalf("InputJSON leaked %q: %s", forbidden, row.InputJSON)
		}
	}
}

func TestConnectorInboundTerminalCauseAuditRowSeparatesPresentationFromCause(t *testing.T) {
	ctx := contractconstants.WithConnectorInboundTrace(context.Background(), contractconstants.ConnectorInboundTrace{
		WorkItemID: "work-1", EventID: "event-1", Attempt: 3, Namespace: "crm-pack",
	})
	row, ok := connectorInboundTerminalCauseAuditRow(ctx, &models.FlowInterview{
		Id: "flow-execution-1", FlowVersionId: "flow-version-1",
	}, &contractmodels.FlowExecutionCause{
		Code:        "DB_LOCK_WAIT_TIMEOUT",
		Category:    contractmodels.FlowExecutionCauseCategoryConcurrency,
		ElementType: "ActionCall",
		ElementName: "ApplyMappings",
		Retriable:   true,
	}, "EMAIL_MALFORMED")
	if !ok || row.ElementType != "ConnectorInboundTerminalCause" || row.Status != "error" {
		t.Fatalf("terminal cause row = (%#v, %v)", row, ok)
	}
	var payload struct {
		PresentedErrorCode string `json:"presentedErrorCode"`
		CauseCode          string `json:"causeCode"`
		CauseCategory      string `json:"causeCategory"`
	}
	if err := json.Unmarshal([]byte(row.ErrorPayload), &payload); err != nil {
		t.Fatalf("unmarshal ErrorPayload: %v", err)
	}
	if payload.PresentedErrorCode != "EMAIL_MALFORMED" || payload.CauseCode != "DB_LOCK_WAIT_TIMEOUT" || payload.CauseCategory != "concurrency" {
		t.Fatalf("terminal payload = %#v", payload)
	}
}

func TestRecordLookupAuditPayloadUsesOnlyHashesAndCounts(t *testing.T) {
	inputJSON := recordLookupAuditInputJSON(`{"Id":{"$eq":"00Q123456789ABC"}}`, 2)
	result := &models.QueryResult{Records: []models.SObject{
		{"Id": "record-uuid-1", "Email": "person@example.test"},
		{"Id": "record-uuid-2", "Email": "other@example.test"},
	}}
	outputJSON := recordLookupAuditOutputJSON(result, "query")
	for _, payload := range []string{inputJSON, outputJSON} {
		for _, forbidden := range []string{"00Q123456789ABC", "record-uuid-1", "record-uuid-2", "person@example.test", "other@example.test"} {
			if strings.Contains(payload, forbidden) {
				t.Fatalf("audit payload leaked %q: %s", forbidden, payload)
			}
		}
	}
	var output struct {
		RowCount       int      `json:"rowCount"`
		RecordIDHashes []string `json:"recordIdHashes"`
		Source         string   `json:"source"`
	}
	if err := json.Unmarshal([]byte(outputJSON), &output); err != nil {
		t.Fatalf("unmarshal output: %v", err)
	}
	if output.RowCount != 2 || len(output.RecordIDHashes) != 2 || output.Source != "query" {
		t.Fatalf("output = %#v", output)
	}
}
