package errs

import (
	"errors"
	"reflect"
	"testing"

	"backend/pkg/library/errors/code"
)

// fakeLogger as before
type fakeLogger struct {
	calls       []string
	lastMsg     string
	fieldsStack [][]LogField
	callerSkips []int
}

func (l *fakeLogger) WithCallerSkip(n int) Logger { l.callerSkips = append(l.callerSkips, n); return l }
func (l *fakeLogger) WithFields(fields ...LogField) Logger {
	cp := make([]LogField, len(fields))
	copy(cp, fields)
	l.fieldsStack = append(l.fieldsStack, cp)
	return l
}
func (l *fakeLogger) Error(msg string) { l.calls = append(l.calls, "ERROR"); l.lastMsg = msg }
func (l *fakeLogger) Warn(msg string)  { l.calls = append(l.calls, "WARN"); l.lastMsg = msg }
func (l *fakeLogger) Info(msg string)  { l.calls = append(l.calls, "INFO"); l.lastMsg = msg }
func (l *fakeLogger) reset() {
	l.calls, l.lastMsg, l.fieldsStack, l.callerSkips = nil, "", nil, nil
}

func init() { Scope = code.Gateway }

func TestJoinMsg(t *testing.T) {
	tests := []struct {
		name string
		in   []string
		want string
	}{
		{"nil", nil, ""},
		{"empty", []string{}, ""},
		{"single", []string{"a"}, "a"},
		{"multi", []string{"a", "b", "c"}, "a b c"},
		{"with spaces", []string{"hello", "world"}, "hello world"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := joinMsg(tt.in); got != tt.want {
				t.Errorf("joinMsg() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestLogErr(t *testing.T) {
	tests := []struct {
		name           string
		l              Logger
		fields         []LogField
		e              *Error
		wantCall       string
		wantFields     bool
		wantCallerSkip int
	}{
		{"nil logger", nil, nil, New(10, 101, 0, "err"), "", false, 0},
		{"nil error", &fakeLogger{}, nil, nil, "", false, 0},
		{"basic log", &fakeLogger{}, nil, New(10, 101, 0, "err"), "ERROR", false, 1},
		{"with fields", &fakeLogger{}, []LogField{{Key: "k", Val: "v"}}, New(10, 101, 0, "err"), "ERROR", true, 1},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var fl *fakeLogger
			if tt.l != nil {
				var ok bool
				fl, ok = tt.l.(*fakeLogger)
				if !ok {
					t.Fatalf("logger is not *fakeLogger")
				}
				fl.reset()
			}
			logErr(tt.l, tt.fields, tt.e)
			if fl == nil {
				if tt.wantCall != "" {
					t.Errorf("expected log but logger is nil")
				}
				return
			}
			if tt.wantCall == "" && len(fl.calls) > 0 {
				t.Errorf("unexpected log call")
			}
			if tt.wantCall != "" && (len(fl.calls) == 0 || fl.calls[0] != tt.wantCall) {
				t.Errorf("expected call %q, got %v", tt.wantCall, fl.calls)
			}
			if tt.wantFields && (len(fl.fieldsStack) == 0 || !reflect.DeepEqual(fl.fieldsStack[0], tt.fields)) {
				t.Errorf("fields mismatch: got %v, want %v", fl.fieldsStack, tt.fields)
			}
			if tt.wantCallerSkip != 0 && (len(fl.callerSkips) == 0 || fl.callerSkips[0] != tt.wantCallerSkip) {
				t.Errorf("callerSkip = %v, want %d", fl.callerSkips, tt.wantCallerSkip)
			}
		})
	}
}

func TestEL(t *testing.T) {
	tests := []struct {
		name    string
		cat     code.Category
		det     code.Detail
		s       []string
		wantCat uint32
		wantDet uint32
		wantMsg string
		wantLog bool
	}{
		{"basic", code.ResNotFound, 123, []string{"not found"}, uint32(code.ResNotFound), 123, "not found", true},
		{"nil logger", code.ResNotFound, 0, []string{}, uint32(code.ResNotFound), 0, "", false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			l := &fakeLogger{}
			e := EL(l, nil, tt.cat, tt.det, tt.s...)
			if e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != tt.wantMsg {
				t.Errorf("EL = cat=%d det=%d msg=%q, want %d %d %q", e.Category(), e.Detail(), e.Error(), tt.wantCat, tt.wantDet, tt.wantMsg)
			}
			if tt.wantLog && len(l.calls) == 0 {
				t.Errorf("expected log")
			}
		})
	}
}

func TestELWrap(t *testing.T) {
	tests := []struct {
		name       string
		cat        code.Category
		det        code.Detail
		cause      error
		s          []string
		wantCat    uint32
		wantDet    uint32
		wantMsg    string
		wantUnwrap string
		wantLog    bool
	}{
		{"basic", code.SysInternal, 456, errors.New("internal"), []string{"sys err"}, uint32(code.SysInternal), 456, "sys err", "internal", true},
		{"no log", code.SysInternal, 0, nil, []string{}, uint32(code.SysInternal), 0, "", "", false}, // nil cause ok
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			l := &fakeLogger{}
			e := ELWrap(l, nil, tt.cat, tt.det, tt.cause, tt.s...)
			if e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != tt.wantMsg {
				t.Errorf("ELWrap = cat=%d det=%d msg=%q, want %d %d %q", e.Category(), e.Detail(), e.Error(), tt.wantCat, tt.wantDet, tt.wantMsg)
			}
			unw := e.Unwrap()
			gotUnwrap := ""
			if unw != nil {
				gotUnwrap = unw.Error()
			}
			if gotUnwrap != tt.wantUnwrap {
				t.Errorf("Unwrap = %q, want %q", gotUnwrap, tt.wantUnwrap)
			}
			if tt.wantLog && len(l.calls) == 0 {
				t.Errorf("expected log")
			}
		})
	}
}

// Expand TestBaseConstructors with all base funcs
func TestBaseConstructors(t *testing.T) {
	tests := []struct {
		name    string
		fn      func(...string) *Error
		wantCat uint32
		wantDet uint32
		wantMsg string
	}{
		{"InputInvalidFormatError", InputInvalidFormatError, uint32(code.InputInvalidFormat), 0, "test msg"},
		{"InputNotValidImplementationError", InputNotValidImplementationError, uint32(code.InputNotValidImplementation), 0, "test msg"},
		{"InputInvalidRangeError", InputInvalidRangeError, uint32(code.InputInvalidRange), 0, "test msg"},
		{"DBErrorError", DBErrorError, uint32(code.DBError), 0, "test msg"},
		{"DBDataConvertError", DBDataConvertError, uint32(code.DBDataConvert), 0, "test msg"},
		{"DBDuplicateError", DBDuplicateError, uint32(code.DBDuplicate), 0, "test msg"},
		{"ResNotFoundError", ResNotFoundError, uint32(code.ResNotFound), 0, "test msg"},
		{"ResInvalidFormatError", ResInvalidFormatError, uint32(code.ResInvalidFormat), 0, "test msg"},
		{"ResAlreadyExistError", ResAlreadyExistError, uint32(code.ResAlreadyExist), 0, "test msg"},
		{"ResInsufficientError", ResInsufficientError, uint32(code.ResInsufficient), 0, "test msg"},
		{"ResInsufficientPermError", ResInsufficientPermError, uint32(code.ResInsufficientPerm), 0, "test msg"},
		{"ResInvalidMeasureIDError", ResInvalidMeasureIDError, uint32(code.ResInvalidMeasureID), 0, "test msg"},
		{"ResExpiredError", ResExpiredError, uint32(code.ResExpired), 0, "test msg"},
		{"ResMigratedError", ResMigratedError, uint32(code.ResMigrated), 0, "test msg"},
		{"ResInvalidStateError", ResInvalidStateError, uint32(code.ResInvalidState), 0, "test msg"},
		{"ResInsufficientQuotaError", ResInsufficientQuotaError, uint32(code.ResInsufficientQuota), 0, "test msg"},
		{"ResMultiOwnerError", ResMultiOwnerError, uint32(code.ResMultiOwner), 0, "test msg"},
		{"AuthUnauthorizedError", AuthUnauthorizedError, uint32(code.AuthUnauthorized), 0, "test msg"},
		{"AuthExpiredError", AuthExpiredError, uint32(code.AuthExpired), 0, "test msg"},
		{"AuthInvalidPosixTimeError", AuthInvalidPosixTimeError, uint32(code.AuthInvalidPosixTime), 0, "test msg"},
		{"AuthSigPayloadMismatchError", AuthSigPayloadMismatchError, uint32(code.AuthSigPayloadMismatch), 0, "test msg"},
		{"AuthForbiddenError", AuthForbiddenError, uint32(code.AuthForbidden), 0, "test msg"},
		{"SysInternalError", SysInternalError, uint32(code.SysInternal), 0, "test msg"},
		{"SysMaintainError", SysMaintainError, uint32(code.SysMaintain), 0, "test msg"},
		{"SysTimeoutError", SysTimeoutError, uint32(code.SysTimeout), 0, "test msg"},
		{"SysTooManyRequestError", SysTooManyRequestError, uint32(code.SysTooManyRequest), 0, "test msg"},
		{"PSuPublishError", PSuPublishError, uint32(code.PSuPublish), 0, "test msg"},
		{"PSuConsumeError", PSuConsumeError, uint32(code.PSuConsume), 0, "test msg"},
		{"PSuTooLargeError", PSuTooLargeError, uint32(code.PSuTooLarge), 0, "test msg"},
		{"SvcInternalError", SvcInternalError, uint32(code.SvcInternal), 0, "test msg"},
		{"SvcThirdPartyError", SvcThirdPartyError, uint32(code.SvcThirdParty), 0, "test msg"},
		{"SvcHTTP400Error", SvcHTTP400Error, uint32(code.SvcHTTP400), 0, "test msg"},
		{"SvcMaintenanceError", SvcMaintenanceError, uint32(code.SvcMaintenance), 0, "test msg"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			e := tt.fn("test", "msg")
			if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
				t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
			}
		})
	}
}

// Expand TestLConstructors with all L funcs
func TestLConstructors(t *testing.T) {
	tests := []struct {
		name    string
		fn      func(Logger, []LogField, ...string) *Error
		wantCat uint32
		wantDet uint32
		wantMsg string
		wantLog bool
	}{
		{"InputInvalidFormatErrorL", InputInvalidFormatErrorL, uint32(code.InputInvalidFormat), 0, "test msg", true},
		{"InputNotValidImplementationErrorL", InputNotValidImplementationErrorL, uint32(code.InputNotValidImplementation), 0, "test msg", true},
		{"InputInvalidRangeErrorL", InputInvalidRangeErrorL, uint32(code.InputInvalidRange), 0, "test msg", true},
		{"DBErrorErrorL", DBErrorErrorL, uint32(code.DBError), 0, "test msg", true},
		{"DBDataConvertErrorL", DBDataConvertErrorL, uint32(code.DBDataConvert), 0, "test msg", true},
		{"DBDuplicateErrorL", DBDuplicateErrorL, uint32(code.DBDuplicate), 0, "test msg", true},
		{"ResNotFoundErrorL", ResNotFoundErrorL, uint32(code.ResNotFound), 0, "test msg", true},
		{"ResInvalidFormatErrorL", ResInvalidFormatErrorL, uint32(code.ResInvalidFormat), 0, "test msg", true},
		{"ResAlreadyExistErrorL", ResAlreadyExistErrorL, uint32(code.ResAlreadyExist), 0, "test msg", true},
		{"ResInsufficientErrorL", ResInsufficientErrorL, uint32(code.ResInsufficient), 0, "test msg", true},
		{"ResInsufficientPermErrorL", ResInsufficientPermErrorL, uint32(code.ResInsufficientPerm), 0, "test msg", true},
		{"ResInvalidMeasureIDErrorL", ResInvalidMeasureIDErrorL, uint32(code.ResInvalidMeasureID), 0, "test msg", true},
		{"ResExpiredErrorL", ResExpiredErrorL, uint32(code.ResExpired), 0, "test msg", true},
		{"ResMigratedErrorL", ResMigratedErrorL, uint32(code.ResMigrated), 0, "test msg", true},
		{"ResInvalidStateErrorL", ResInvalidStateErrorL, uint32(code.ResInvalidState), 0, "test msg", true},
		{"ResInsufficientQuotaErrorL", ResInsufficientQuotaErrorL, uint32(code.ResInsufficientQuota), 0, "test msg", true},
		{"ResMultiOwnerErrorL", ResMultiOwnerErrorL, uint32(code.ResMultiOwner), 0, "test msg", true},
		{"AuthUnauthorizedErrorL", AuthUnauthorizedErrorL, uint32(code.AuthUnauthorized), 0, "test msg", true},
		{"AuthExpiredErrorL", AuthExpiredErrorL, uint32(code.AuthExpired), 0, "test msg", true},
		{"AuthInvalidPosixTimeErrorL", AuthInvalidPosixTimeErrorL, uint32(code.AuthInvalidPosixTime), 0, "test msg", true},
		{"AuthSigPayloadMismatchErrorL", AuthSigPayloadMismatchErrorL, uint32(code.AuthSigPayloadMismatch), 0, "test msg", true},
		{"AuthForbiddenErrorL", AuthForbiddenErrorL, uint32(code.AuthForbidden), 0, "test msg", true},
		{"SysInternalErrorL", SysInternalErrorL, uint32(code.SysInternal), 0, "test msg", true},
		{"SysMaintainErrorL", SysMaintainErrorL, uint32(code.SysMaintain), 0, "test msg", true},
		{"SysTimeoutErrorL", SysTimeoutErrorL, uint32(code.SysTimeout), 0, "test msg", true},
		{"SysTooManyRequestErrorL", SysTooManyRequestErrorL, uint32(code.SysTooManyRequest), 0, "test msg", true},
		{"PSuPublishErrorL", PSuPublishErrorL, uint32(code.PSuPublish), 0, "test msg", true},
		{"PSuConsumeErrorL", PSuConsumeErrorL, uint32(code.PSuConsume), 0, "test msg", true},
		{"PSuTooLargeErrorL", PSuTooLargeErrorL, uint32(code.PSuTooLarge), 0, "test msg", true},
		{"SvcInternalErrorL", SvcInternalErrorL, uint32(code.SvcInternal), 0, "test msg", true},
		{"SvcThirdPartyErrorL", SvcThirdPartyErrorL, uint32(code.SvcThirdParty), 0, "test msg", true},
		{"SvcHTTP400ErrorL", SvcHTTP400ErrorL, uint32(code.SvcHTTP400), 0, "test msg", true},
		{"SvcMaintenanceErrorL", SvcMaintenanceErrorL, uint32(code.SvcMaintenance), 0, "test msg", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			l := &fakeLogger{}
			fields := []LogField{}
			e := tt.fn(l, fields, "test", "msg")
			if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
				t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
			}
			if tt.wantLog && len(l.calls) == 0 {
				t.Errorf("expected log call")
			}
		})
	}
}

// Add TestWrapLConstructors similarly
func TestWrapLConstructors(t *testing.T) {
	tests := []struct {
		name       string
		fn         func(Logger, []LogField, error, ...string) *Error
		wantCat    uint32
		wantDet    uint32
		wantMsg    string
		wantUnwrap string
		wantLog    bool
	}{
		{"InputInvalidFormatErrorWrapL", InputInvalidFormatErrorWrapL, uint32(code.InputInvalidFormat), 0, "test msg", "cause err", true},
		{"InputNotValidImplementationErrorWrapL", InputNotValidImplementationErrorWrapL, uint32(code.InputNotValidImplementation), 0, "test msg", "cause err", true},
		{"InputInvalidRangeErrorWrapL", InputInvalidRangeErrorWrapL, uint32(code.InputInvalidRange), 0, "test msg", "cause err", true},
		{"DBErrorErrorWrapL", DBErrorErrorWrapL, uint32(code.DBError), 0, "test msg", "cause err", true},
		{"DBDataConvertErrorWrapL", DBDataConvertErrorWrapL, uint32(code.DBDataConvert), 0, "test msg", "cause err", true},
		{"DBDuplicateErrorWrapL", DBDuplicateErrorWrapL, uint32(code.DBDuplicate), 0, "test msg", "cause err", true},
		{"ResNotFoundErrorWrapL", ResNotFoundErrorWrapL, uint32(code.ResNotFound), 0, "test msg", "cause err", true},
		{"ResInvalidFormatErrorWrapL", ResInvalidFormatErrorWrapL, uint32(code.ResInvalidFormat), 0, "test msg", "cause err", true},
		{"ResAlreadyExistErrorWrapL", ResAlreadyExistErrorWrapL, uint32(code.ResAlreadyExist), 0, "test msg", "cause err", true},
		{"ResInsufficientErrorWrapL", ResInsufficientErrorWrapL, uint32(code.ResInsufficient), 0, "test msg", "cause err", true},
		{"ResInsufficientPermErrorWrapL", ResInsufficientPermErrorWrapL, uint32(code.ResInsufficientPerm), 0, "test msg", "cause err", true},
		{"ResInvalidMeasureIDErrorWrapL", ResInvalidMeasureIDErrorWrapL, uint32(code.ResInvalidMeasureID), 0, "test msg", "cause err", true},
		{"ResExpiredErrorWrapL", ResExpiredErrorWrapL, uint32(code.ResExpired), 0, "test msg", "cause err", true},
		{"ResMigratedErrorWrapL", ResMigratedErrorWrapL, uint32(code.ResMigrated), 0, "test msg", "cause err", true},
		{"ResInvalidStateErrorWrapL", ResInvalidStateErrorWrapL, uint32(code.ResInvalidState), 0, "test msg", "cause err", true},
		{"ResInsufficientQuotaErrorWrapL", ResInsufficientQuotaErrorWrapL, uint32(code.ResInsufficientQuota), 0, "test msg", "cause err", true},
		{"ResMultiOwnerErrorWrapL", ResMultiOwnerErrorWrapL, uint32(code.ResMultiOwner), 0, "test msg", "cause err", true},
		{"AuthUnauthorizedErrorWrapL", AuthUnauthorizedErrorWrapL, uint32(code.AuthUnauthorized), 0, "test msg", "cause err", true},
		{"AuthExpiredErrorWrapL", AuthExpiredErrorWrapL, uint32(code.AuthExpired), 0, "test msg", "cause err", true},
		{"AuthInvalidPosixTimeErrorWrapL", AuthInvalidPosixTimeErrorWrapL, uint32(code.AuthInvalidPosixTime), 0, "test msg", "cause err", true},
		{"AuthSigPayloadMismatchErrorWrapL", AuthSigPayloadMismatchErrorWrapL, uint32(code.AuthSigPayloadMismatch), 0, "test msg", "cause err", true},
		{"AuthForbiddenErrorWrapL", AuthForbiddenErrorWrapL, uint32(code.AuthForbidden), 0, "test msg", "cause err", true},
		{"SysInternalErrorWrapL", SysInternalErrorWrapL, uint32(code.SysInternal), 0, "test msg", "cause err", true},
		{"SysMaintainErrorWrapL", SysMaintainErrorWrapL, uint32(code.SysMaintain), 0, "test msg", "cause err", true},
		{"SysTimeoutErrorWrapL", SysTimeoutErrorWrapL, uint32(code.SysTimeout), 0, "test msg", "cause err", true},
		{"SysTooManyRequestErrorWrapL", SysTooManyRequestErrorWrapL, uint32(code.SysTooManyRequest), 0, "test msg", "cause err", true},
		{"PSuPublishErrorWrapL", PSuPublishErrorWrapL, uint32(code.PSuPublish), 0, "test msg", "cause err", true},
		{"PSuConsumeErrorWrapL", PSuConsumeErrorWrapL, uint32(code.PSuConsume), 0, "test msg", "cause err", true},
		{"PSuTooLargeErrorWrapL", PSuTooLargeErrorWrapL, uint32(code.PSuTooLarge), 0, "test msg", "cause err", true},
		{"SvcInternalErrorWrapL", SvcInternalErrorWrapL, uint32(code.SvcInternal), 0, "test msg", "cause err", true},
		{"SvcThirdPartyErrorWrapL", SvcThirdPartyErrorWrapL, uint32(code.SvcThirdParty), 0, "test msg", "cause err", true},
		{"SvcHTTP400ErrorWrapL", SvcHTTP400ErrorWrapL, uint32(code.SvcHTTP400), 0, "test msg", "cause err", true},
		{"SvcMaintenanceErrorWrapL", SvcMaintenanceErrorWrapL, uint32(code.SvcMaintenance), 0, "test msg", "cause err", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			l := &fakeLogger{}
			fields := []LogField{}
			cause := errors.New("cause err")
			e := tt.fn(l, fields, cause, "test", "msg")
			if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
				t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
			}
			if tt.wantUnwrap != "" && e.Unwrap().Error() != tt.wantUnwrap {
				t.Errorf("Unwrap() = %q, want %q", e.Unwrap().Error(), tt.wantUnwrap)
			}
			if tt.wantLog && len(l.calls) == 0 {
				t.Errorf("expected log call")
			}
		})
	}
}

// ... Add more tests for edge cases, like empty strings, multiple args in joinMsg, etc.
