package dialectsfcrm

import (
	"context"
	"errors"
	"strings"
	"testing"

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestApprovalProcessManagedPreflightAllowsCreateAndOwnerUpdate(t *testing.T) {
	request := approvalProcessManagedRequestForTest(t)
	tests := []struct {
		name string
		rows []cp.ComponentRow
	}{
		{name: "create"},
		{name: "owner update", rows: []cp.ComponentRow{{
			"Id": "definition-1", "DeveloperName": "quote_approval", "OwnerPack": "sales", "TableEnumOrId": "quote",
		}}},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			reader := &approvalProcessManagedTestReader{rows: test.rows}
			if err := preflightApprovalProcessManagedComponent(context.Background(), reader, request); err != nil {
				t.Fatal(err)
			}
			want := cp.ComponentRowQuery{
				Table:   approvalProcessDefinitionTable,
				Columns: []string{"Id", "DeveloperName", "OwnerPack", "TableEnumOrId"},
				Equal:   cp.ComponentRow{"DeveloperName": "Quote_Approval"},
			}
			if reader.calls != 1 || !approvalProcessManagedQueriesEqual(reader.query, want) {
				t.Fatalf("ReadRows calls/query = %d/%#v, want 1/%#v", reader.calls, reader.query, want)
			}
		})
	}
}

func TestApprovalProcessManagedPreflightRejectsCollisionAndOwnershipViolations(t *testing.T) {
	request := approvalProcessManagedRequestForTest(t)
	tests := []struct {
		name string
		rows []cp.ComponentRow
		want string
	}{
		{
			name: "case-variant global developer name collision",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "quote_approval", "OwnerPack": "sales", "TableEnumOrId": "Order"}},
			want: "globally unique",
		},
		{
			name: "owner mismatch",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": "admin", "TableEnumOrId": "Quote"}},
			want: `OwnerPack = "admin"`,
		},
		{
			name: "padded owner",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": " sales ", "TableEnumOrId": "Quote"}},
			want: `OwnerPack = " sales "`,
		},
		{
			name: "padded object",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": "sales", "TableEnumOrId": " Quote "}},
			want: "TableEnumOrId",
		},
		{
			name: "unowned row",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": nil, "TableEnumOrId": "Quote"}},
			want: `OwnerPack = ""`,
		},
		{
			name: "returned identity mismatch",
			rows: []cp.ComponentRow{{"Id": "definition-1", "DeveloperName": "Other_Approval", "OwnerPack": "sales", "TableEnumOrId": "Quote"}},
			want: "does not match requested developer name",
		},
		{
			name: "host leaked driver value",
			rows: []cp.ComponentRow{{"Id": []byte("definition-1"), "DeveloperName": "Quote_Approval", "OwnerPack": "sales", "TableEnumOrId": "Quote"}},
			want: "unsupported value type",
		},
		{
			name: "duplicate live identity",
			rows: []cp.ComponentRow{
				{"Id": "definition-1", "DeveloperName": "Quote_Approval", "OwnerPack": "sales", "TableEnumOrId": "Quote"},
				{"Id": "definition-2", "DeveloperName": "quote_approval", "OwnerPack": "sales", "TableEnumOrId": "Quote"},
			},
			want: "resolves to 2",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			err := preflightApprovalProcessManagedComponent(context.Background(), &approvalProcessManagedTestReader{rows: test.rows}, request)
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestApprovalProcessManagedPreflightRejectsInvalidDependencies(t *testing.T) {
	request := approvalProcessManagedRequestForTest(t)
	if err := preflightApprovalProcessManagedComponent(context.Background(), nil, request); err == nil || !strings.Contains(err.Error(), "reader is required") {
		t.Fatalf("nil reader error = %v", err)
	}

	request.PackName = " sales"
	reader := &approvalProcessManagedTestReader{}
	if err := preflightApprovalProcessManagedComponent(context.Background(), reader, request); err == nil || !strings.Contains(err.Error(), "not canonical") {
		t.Fatalf("invalid request error = %v", err)
	}
	if reader.calls != 0 {
		t.Fatalf("invalid request performed %d reads", reader.calls)
	}

	readErr := errors.New("read failed")
	request = approvalProcessManagedRequestForTest(t)
	if err := preflightApprovalProcessManagedComponent(context.Background(), &approvalProcessManagedTestReader{err: readErr}, request); !errors.Is(err, readErr) {
		t.Fatalf("read error = %v, want wrapping %v", err, readErr)
	}
}

type approvalProcessManagedTestReader struct {
	rows  []cp.ComponentRow
	err   error
	query cp.ComponentRowQuery
	calls int
}

func (r *approvalProcessManagedTestReader) ReadRows(_ context.Context, query cp.ComponentRowQuery) ([]cp.ComponentRow, error) {
	r.calls++
	r.query = query
	return r.rows, r.err
}

func approvalProcessManagedQueriesEqual(left, right cp.ComponentRowQuery) bool {
	if left.Table != right.Table || len(left.Columns) != len(right.Columns) || len(left.Equal) != len(right.Equal) {
		return false
	}
	for index := range left.Columns {
		if left.Columns[index] != right.Columns[index] {
			return false
		}
	}
	for key, value := range left.Equal {
		if right.Equal[key] != value {
			return false
		}
	}
	return true
}
