package metadata

import (
	"context"
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// leadPostConvertSchema returns a minimal Lead ObjectMetadata for post-convert
// validation rule tests.
func leadPostConvertSchema() *models.ObjectMetadata {
	return &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "IsConverted", Type: constants.FieldTypeBoolean},
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference},
			{APIName: "ConvertedContactId", Type: constants.FieldTypeReference},
			{APIName: "ConvertedOpportunityId", Type: constants.FieldTypeReference},
			{APIName: "ConvertedDate", Type: constants.FieldTypeDate},
			{APIName: "Description", Type: constants.FieldTypeTextarea},
			{APIName: "Company", Type: constants.FieldTypeString},
		},
	}
}

// umbrellaRule is vrule_lead_post_convert_requires_view_edit_permission.
func umbrellaRule() *models.ValidationRule {
	return &models.ValidationRule{
		Name:         "Lead_Post_Convert_Requires_AllowViewEditConvertedLeads",
		Active:       true,
		Condition:    "PRIORVALUE(IsConverted) = TRUE && $Permission.AllowViewEditConvertedLeads != true",
		ErrorMessage: "Editing a converted Lead requires the View and Edit Converted Leads permission.",
	}
}

// anchorRule is one of the per-anchor ISCHANGED rules (representative sample).
func anchorConvertedAccountRule() *models.ValidationRule {
	return &models.ValidationRule{
		Name:         "Lead_ConvertedAccountId_Immutable_Post_Convert",
		Active:       true,
		Condition:    "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(ConvertedAccountId)",
		ErrorMessage: "ConvertedAccountId cannot be changed after lead conversion.",
	}
}

// withViewEditPermission builds a userCtx that grants AllowViewEditConvertedLeads.
func withViewEditPermission() map[string]interface{} {
	return map[string]interface{}{
		constants.ContextKeySystemPermissions: map[string]bool{
			"AllowViewEditConvertedLeads": true,
		},
	}
}

// TestLeadPostConvertUmbrellaBlocksUserWithoutPermission verifies that a user
// without AllowViewEditConvertedLeads cannot edit any field on a converted Lead.
func TestLeadPostConvertUmbrellaBlocksUserWithoutPermission(t *testing.T) {
	vs := NewValidationService(formula.NewEngine())
	schema := leadPostConvertSchema()

	oldRecord := models.SObject{"IsConverted": true, "Description": "original"}
	record := models.SObject{"IsConverted": true, "Description": "changed"}

	rules := []*models.ValidationRule{umbrellaRule()}

	err := vs.ValidateRecord(context.Background(), record, schema, rules, &oldRecord, nil)
	if err == nil {
		t.Fatal("ValidateRecord post-convert without permission: got nil, want rejection")
	}
	if !strings.Contains(err.Error(), "View and Edit Converted Leads permission") {
		t.Fatalf("ValidateRecord post-convert without permission: error = %v, want umbrella message", err)
	}
}

// TestLeadPostConvertUmbrellaAllowsUserWithPermission verifies that a user with
// AllowViewEditConvertedLeads can edit non-anchor fields on a converted Lead.
func TestLeadPostConvertUmbrellaAllowsUserWithPermission(t *testing.T) {
	vs := NewValidationService(formula.NewEngine())
	schema := leadPostConvertSchema()

	oldRecord := models.SObject{"IsConverted": true, "Description": "original"}
	record := models.SObject{"IsConverted": true, "Description": "changed"}

	rules := []*models.ValidationRule{umbrellaRule()}
	userCtx := withViewEditPermission()

	err := vs.ValidateRecord(context.Background(), record, schema, rules, &oldRecord, userCtx)
	if err != nil {
		t.Fatalf("ValidateRecord post-convert with permission: got %v, want nil", err)
	}
}

// TestLeadPostConvertAnchorLockedRegardlessOfPermission verifies that anchor
// fields remain immutable even for users who have AllowViewEditConvertedLeads.
func TestLeadPostConvertAnchorLockedRegardlessOfPermission(t *testing.T) {
	vs := NewValidationService(formula.NewEngine())
	schema := leadPostConvertSchema()

	oldRecord := models.SObject{"IsConverted": true, "ConvertedAccountId": "acct-001"}
	record := models.SObject{"IsConverted": true, "ConvertedAccountId": "acct-002"}

	// umbrella passes (user has permission); anchor rule must still fire.
	rules := []*models.ValidationRule{umbrellaRule(), anchorConvertedAccountRule()}
	userCtx := withViewEditPermission()

	err := vs.ValidateRecord(context.Background(), record, schema, rules, &oldRecord, userCtx)
	if err == nil {
		t.Fatal("ValidateRecord anchor change with permission: got nil, want anchor rule rejection")
	}
	if !strings.Contains(err.Error(), "ConvertedAccountId cannot be changed") {
		t.Fatalf("ValidateRecord anchor change with permission: error = %v, want anchor message", err)
	}
}

// TestLeadConvertTransitionSkipsUmbrellaRule verifies that the convert
// transition itself (IsConverted false→true) does not fire the umbrella rule,
// because PRIORVALUE(IsConverted) is FALSE at convert-time.
func TestLeadConvertTransitionSkipsUmbrellaRule(t *testing.T) {
	vs := NewValidationService(formula.NewEngine())
	schema := leadPostConvertSchema()

	oldRecord := models.SObject{"IsConverted": false}
	record := models.SObject{"IsConverted": true}

	rules := []*models.ValidationRule{umbrellaRule()}

	err := vs.ValidateRecord(context.Background(), record, schema, rules, &oldRecord, nil)
	if err != nil {
		t.Fatalf("ValidateRecord convert transition: got %v, want nil (umbrella must not fire on convert itself)", err)
	}
}

// TestLeadPostConvertUmbrellaBlocksUnconvertAttemptForUserWithoutPermission
// verifies that a user without AllowViewEditConvertedLeads who attempts to
// PATCH IsConverted=false (un-convert) on a converted Lead sees the umbrella
// permission error, NOT the anchor immutability error.
//
// Rule ordering matters: the umbrella rule must be at index 0 in the rule
// array so that it fires before the anchor rule. If a future change reorders
// the rules so the anchor fires first, this test will fail and explain why.
func TestLeadPostConvertUmbrellaBlocksUnconvertAttemptForUserWithoutPermission(t *testing.T) {
	vs := NewValidationService(formula.NewEngine())
	schema := leadPostConvertSchema()

	// Simulates PATCH IsConverted=false on a currently-converted Lead.
	oldRecord := models.SObject{"IsConverted": true}
	record := models.SObject{"IsConverted": false}

	// Umbrella is at index 0; anchor is at index 1.
	// Without the permission, the umbrella must fire first.
	rules := []*models.ValidationRule{umbrellaRule(), anchorConvertedAccountRule()}

	err := vs.ValidateRecord(context.Background(), record, schema, rules, &oldRecord, nil)
	if err == nil {
		t.Fatal("ValidateRecord un-convert without permission: got nil, want rejection")
	}
	const umbrellaMsg = "Editing a converted Lead requires the View and Edit Converted Leads permission."
	if !strings.Contains(err.Error(), umbrellaMsg) {
		t.Fatalf("ValidateRecord un-convert without permission: error = %v,\nwant umbrella message %q (NOT anchor message)", err, umbrellaMsg)
	}
}
