package dialectsfcrm

import (
	"reflect"
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/contract"
)

func TestValidateConnectorInboundRemoteIdentityRecoverySourceAllowsActiveCRMRecords(t *testing.T) {
	tests := []struct {
		name       string
		objectName string
		record     contract.Record
		want       ConnectorInboundRemoteIdentityRecoveryMirrorState
	}{
		{
			name:       "Lead synced mirror",
			objectName: connectorRecoveryLeadObject,
			record: contract.Record{
				"Id": "lead-1", "IsDeleted": false, "IsConverted": false, "HubSpot_Id__c": "remote-1",
			},
			want: ConnectorInboundRemoteIdentityRecoveryMirrorSynced,
		},
		{
			name:       "Lead blank mirror",
			objectName: connectorRecoveryLeadObject,
			record: contract.Record{
				"Id": "lead-1", "IsDeleted": false, "IsConverted": false, "HubSpot_Id__c": "  ",
			},
			want: ConnectorInboundRemoteIdentityRecoveryMirrorDrifted,
		},
		{
			name:       "Contact null mirror",
			objectName: connectorRecoveryContactObject,
			record: contract.Record{
				"Id": "lead-1", "IsDeleted": false, "HubSpot_Id__c": nil,
			},
			want: ConnectorInboundRemoteIdentityRecoveryMirrorDrifted,
		},
		{
			name:       "Contact absent mirror",
			objectName: connectorRecoveryContactObject,
			record: contract.Record{
				"Id": "lead-1", "IsDeleted": false,
			},
			want: ConnectorInboundRemoteIdentityRecoveryMirrorDrifted,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			before := cloneConnectorRecoveryRecord(test.record)
			got, err := ValidateConnectorInboundRemoteIdentityRecoverySource(ConnectorInboundRemoteIdentityRecoverySourceInput{
				ObjectName:    test.objectName,
				RecordID:      "lead-1",
				Record:        test.record,
				RemoteIDField: "HubSpot_Id__c",
				RemoteID:      "remote-1",
			})
			if err != nil {
				t.Fatalf("ValidateConnectorInboundRemoteIdentityRecoverySource() error = %v", err)
			}
			if got != test.want {
				t.Fatalf("mirror state = %q, want %q", got, test.want)
			}
			if !reflect.DeepEqual(test.record, before) {
				t.Fatalf("validator mutated locked record: got %#v, want %#v", test.record, before)
			}
		})
	}
}

func TestValidateConnectorInboundRemoteIdentityRecoverySourceFailsClosed(t *testing.T) {
	valid := func() ConnectorInboundRemoteIdentityRecoverySourceInput {
		return ConnectorInboundRemoteIdentityRecoverySourceInput{
			ObjectName:    connectorRecoveryLeadObject,
			RecordID:      "lead-1",
			Record:        contract.Record{"Id": "lead-1", "IsDeleted": false, "IsConverted": false, "HubSpot_Id__c": "remote-1"},
			RemoteIDField: "HubSpot_Id__c",
			RemoteID:      "remote-1",
		}
	}
	tests := []struct {
		name    string
		mutate  func(*ConnectorInboundRemoteIdentityRecoverySourceInput)
		wantErr string
	}{
		{name: "unsupported object", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.ObjectName = "Account" }, wantErr: "not supported"},
		{name: "deleted Lead", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["IsDeleted"] = true }, wantErr: "is deleted"},
		{name: "converted Lead", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["IsConverted"] = true }, wantErr: "is converted"},
		{name: "missing deleted marker", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { delete(in.Record, "IsDeleted") }, wantErr: `field "IsDeleted" is missing`},
		{name: "nonboolean deleted marker", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["IsDeleted"] = 0 }, wantErr: `field "IsDeleted" must be boolean`},
		{name: "missing converted marker", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { delete(in.Record, "IsConverted") }, wantErr: `field "IsConverted" is missing`},
		{name: "nonboolean converted marker", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["IsConverted"] = "false" }, wantErr: `field "IsConverted" must be boolean`},
		{name: "missing locked id", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { delete(in.Record, "Id") }, wantErr: "ID does not match"},
		{name: "wrong locked id", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["Id"] = "lead-2" }, wantErr: "ID does not match"},
		{name: "typed mirror", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["HubSpot_Id__c"] = 123 }, wantErr: "must be a string or null"},
		{name: "different mirror", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record["HubSpot_Id__c"] = "remote-2" }, wantErr: "different remote ID"},
		{name: "nil record", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.Record = nil }, wantErr: "source record is required"},
		{name: "unclean object", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.ObjectName = " Lead " }, wantErr: "source object must be canonical"},
		{name: "blank record id", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.RecordID = "" }, wantErr: "record ID must be canonical"},
		{name: "unclean field", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.RemoteIDField = " HubSpot_Id__c" }, wantErr: "remote ID field must be canonical"},
		{name: "unclean remote id", mutate: func(in *ConnectorInboundRemoteIdentityRecoverySourceInput) { in.RemoteID = "remote-1 " }, wantErr: "remote ID must be canonical"},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			input := valid()
			test.mutate(&input)
			got, err := ValidateConnectorInboundRemoteIdentityRecoverySource(input)
			if err == nil || !strings.Contains(err.Error(), test.wantErr) {
				t.Fatalf("ValidateConnectorInboundRemoteIdentityRecoverySource() = (%q, %v), want error containing %q", got, err, test.wantErr)
			}
			if got != "" {
				t.Fatalf("mirror state = %q on error, want blank", got)
			}
		})
	}
}

func TestValidateConnectorInboundRemoteIdentityRecoverySourceContactDoesNotRequireLeadConversionField(t *testing.T) {
	got, err := ValidateConnectorInboundRemoteIdentityRecoverySource(ConnectorInboundRemoteIdentityRecoverySourceInput{
		ObjectName:    connectorRecoveryContactObject,
		RecordID:      "contact-1",
		Record:        contract.Record{"Id": "contact-1", "IsDeleted": false, "HubSpot_Id__c": "remote-1"},
		RemoteIDField: "HubSpot_Id__c",
		RemoteID:      "remote-1",
	})
	if err != nil {
		t.Fatalf("ValidateConnectorInboundRemoteIdentityRecoverySource() error = %v", err)
	}
	if got != ConnectorInboundRemoteIdentityRecoveryMirrorSynced {
		t.Fatalf("mirror state = %q, want %q", got, ConnectorInboundRemoteIdentityRecoveryMirrorSynced)
	}
}

func cloneConnectorRecoveryRecord(record contract.Record) contract.Record {
	cloned := make(contract.Record, len(record))
	for key, value := range record {
		cloned[key] = value
	}
	return cloned
}
