package errors

import (
	"context"
	"errors"
	"testing"
)

type appErrorShape interface {
	HTTPStatus() int
	Code() string
}

func TestDomainWireErrorStaysTransportNeutral(t *testing.T) {
	var err error = &DomainWireError{Kind: WireInvalidField, Message: "x"}
	if _, ok := err.(appErrorShape); ok {
		t.Fatal("DomainWireError must not carry transport status or dialect error code methods")
	}
}

func TestDomainWireErrorWrapsCause(t *testing.T) {
	err := NewInvalidFieldError("bad field", "Metadata").WithCause(context.DeadlineExceeded)
	if !errors.Is(err, context.DeadlineExceeded) {
		t.Fatalf("errors.Is did not unwrap cause from %#v", err)
	}
	if err.Error() != "bad field: context deadline exceeded" {
		t.Fatalf("Error() = %q", err.Error())
	}
}

type testFieldWriteDenied struct{}

func (testFieldWriteDenied) Error() string { return "denied" }
func (testFieldWriteDenied) FieldWriteDeniedFields() []string {
	return []string{"Amount", "Status"}
}
func (testFieldWriteDenied) FieldWriteDeniedMessage() string {
	return "Unable to create/update fields: Amount, Status."
}

type testValidationRule struct{}

func (testValidationRule) Error() string { return "validation rule 'R': Project is required" }
func (testValidationRule) ValidationRuleMessage() string {
	return "Project is required"
}

type testValidationRules struct{}

func (testValidationRules) Error() string { return "several validation rules failed" }
func (testValidationRules) ValidationRuleFailures() []ValidationRuleFailure {
	return []ValidationRuleFailure{
		{RuleName: "Require_Project", Message: "Project is required", Field: "Project__c"},
		{RuleName: "Require_Region", Message: "Region is required", Field: "Region__c"},
	}
}

func TestNeutralWriteConditionClassifiers(t *testing.T) {
	fields, message, ok := FieldWriteDeniedDetails(testFieldWriteDenied{})
	if !ok {
		t.Fatal("FieldWriteDeniedDetails ok = false, want true")
	}
	if len(fields) != 2 || fields[0] != "Amount" || fields[1] != "Status" {
		t.Fatalf("fields = %#v", fields)
	}
	if message != "Unable to create/update fields: Amount, Status." {
		t.Fatalf("message = %q", message)
	}

	ruleMessage, ok := ValidationRuleMessage(testValidationRule{})
	if !ok {
		t.Fatal("ValidationRuleMessage ok = false, want true")
	}
	if ruleMessage != "Project is required" {
		t.Fatalf("ruleMessage = %q", ruleMessage)
	}

	ruleFailures, ok := ValidationRuleFailures(testValidationRules{})
	if !ok || len(ruleFailures) != 2 {
		t.Fatalf("ValidationRuleFailures = %#v, %t", ruleFailures, ok)
	}
	if ruleFailures[0].Field != "Project__c" || ruleFailures[1].Message != "Region is required" {
		t.Fatalf("rule failures = %#v", ruleFailures)
	}
}
