// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0

package validate

import (
	"errors"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

var (
	errOne        = errors.New("one Error")
	errAnother    = errors.New("another Error")
	errNew        = errors.New("new Error")
	errAdditional = errors.New("additional Error")
	errImportant  = errors.New("IMPORTANT!Another Error")

	errOneWarning       = errors.New("one Warning")
	errNewWarning       = errors.New("new Warning")
	errImportantWarning = errors.New("IMPORTANT!Another Warning")
)

// Test AddError() uniqueness.
func TestResult_AddError(t *testing.T) {
	r := Result{}
	r.AddErrors(errOne)
	r.AddErrors(errAnother)
	r.AddErrors(errOne)
	r.AddErrors(errOne)
	r.AddErrors(errOne)
	r.AddErrors(errOne, errAnother)

	assert.Len(t, r.Errors, 2)
	assert.Contains(t, r.Errors, errOne)
	assert.Contains(t, r.Errors, errAnother)
}

func TestResult_AddNilError(t *testing.T) {
	r := Result{}
	r.AddErrors(nil)
	assert.Empty(t, r.Errors)

	errArray := []error{errOne, nil, errAnother}
	r.AddErrors(errArray...)
	assert.Len(t, r.Errors, 2)
}

func TestResult_AddWarnings(t *testing.T) {
	r := Result{}
	r.AddErrors(errOne)
	assert.Len(t, r.Errors, 1)
	assert.Empty(t, r.Warnings)

	r.AddWarnings(errOneWarning)
	assert.Len(t, r.Errors, 1)
	assert.Len(t, r.Warnings, 1)
}

func TestResult_Merge(t *testing.T) {
	r := Result{}
	r.AddErrors(errOne)
	r.AddWarnings(errOneWarning)
	r.Inc()
	assert.Len(t, r.Errors, 1)
	assert.Len(t, r.Warnings, 1)
	assert.EqualT(t, 1, r.MatchCount)

	// Merge with same
	r2 := Result{}
	r2.AddErrors(errOne)
	r2.AddWarnings(errOneWarning)
	r2.Inc()

	r.Merge(&r2)

	assert.Len(t, r.Errors, 1)
	assert.Len(t, r.Warnings, 1)
	assert.EqualT(t, 2, r.MatchCount)

	// Merge with new
	r3 := Result{}
	r3.AddErrors(errNew)
	r3.AddWarnings(errNewWarning)
	r3.Inc()

	r.Merge(&r3)

	assert.Len(t, r.Errors, 2)
	assert.Len(t, r.Warnings, 2)
	assert.EqualT(t, 3, r.MatchCount)
}

func errorFixture() (Result, Result, Result) {
	r := Result{}
	r.AddErrors(errOne)
	r.AddWarnings(errOneWarning)
	r.Inc()

	// same
	r2 := Result{}
	r2.AddErrors(errOne)
	r2.AddWarnings(errOneWarning)
	r2.Inc()

	// new
	r3 := Result{}
	r3.AddErrors(errNew)
	r3.AddWarnings(errNewWarning)
	r3.Inc()
	return r, r2, r3
}

func TestResult_MergeAsErrors(t *testing.T) {
	r, r2, r3 := errorFixture()
	assert.Len(t, r.Errors, 1)
	assert.Len(t, r.Warnings, 1)
	assert.EqualT(t, 1, r.MatchCount)

	r.MergeAsErrors(&r2, &r3)

	assert.Len(t, r.Errors, 4) // One Warning added to Errors
	assert.Len(t, r.Warnings, 1)
	assert.EqualT(t, 3, r.MatchCount)
}

func TestResult_MergeAsWarnings(t *testing.T) {
	r, r2, r3 := errorFixture()
	assert.Len(t, r.Errors, 1)
	assert.Len(t, r.Warnings, 1)
	assert.EqualT(t, 1, r.MatchCount)

	r.MergeAsWarnings(&r2, &r3)

	assert.Len(t, r.Errors, 1) // One Warning added to Errors
	assert.Len(t, r.Warnings, 4)
	assert.EqualT(t, 3, r.MatchCount)
}

func TestResult_IsValid(t *testing.T) {
	r := Result{}

	assert.TrueT(t, r.IsValid())
	assert.FalseT(t, r.HasErrors())

	r.AddWarnings(errOneWarning)
	assert.TrueT(t, r.IsValid())
	assert.FalseT(t, r.HasErrors())

	r.AddErrors(errOne)
	assert.FalseT(t, r.IsValid())
	assert.TrueT(t, r.HasErrors())
}

func TestResult_HasWarnings(t *testing.T) {
	r := Result{}

	assert.FalseT(t, r.HasWarnings())

	r.AddErrors(errOne)
	assert.FalseT(t, r.HasWarnings())

	r.AddWarnings(errOneWarning)
	assert.TrueT(t, r.HasWarnings())
}

func TestResult_HasErrorsOrWarnings(t *testing.T) {
	r := Result{}
	r2 := Result{}

	assert.FalseT(t, r.HasErrorsOrWarnings())

	r.AddErrors(errOne)
	assert.TrueT(t, r.HasErrorsOrWarnings())

	r2.AddWarnings(errOneWarning)
	assert.TrueT(t, r2.HasErrorsOrWarnings())

	r.Merge(&r2)
	assert.TrueT(t, r.HasErrorsOrWarnings())
}

func TestResult_keepRelevantErrors(t *testing.T) {
	r := Result{}
	r.AddErrors(errOne)
	r.AddErrors(errImportant)
	r.AddWarnings(errOneWarning)
	r.AddWarnings(errImportantWarning)
	assert.Len(t, r.keepRelevantErrors().Errors, 1)
	assert.Len(t, r.keepRelevantErrors().Warnings, 1)
}

func TestResult_AsError(t *testing.T) {
	r := Result{}
	require.NoError(t, r.AsError())
	r.AddErrors(errOne)
	r.AddErrors(errAdditional)
	res := r.AsError()
	require.Error(t, res)

	assert.StringContainsT(t, res.Error(), "validation failure list:") // Expected from pkg errors
	assert.StringContainsT(t, res.Error(), "one Error")                // Expected from pkg errors
	assert.StringContainsT(t, res.Error(), "additional Error")         // Expected from pkg errors
}

// Test methods which suppport a call on a nil instance.
func TestResult_NilInstance(t *testing.T) {
	var r *Result
	assert.TrueT(t, r.IsValid())
	assert.FalseT(t, r.HasErrors())
	assert.FalseT(t, r.HasWarnings())
	assert.FalseT(t, r.HasErrorsOrWarnings())
}
