package oci

import (
	"errors"
	"fmt"
	"testing"
)

// fakeServiceError 模拟 oci-go-sdk 的 common.ServiceError，
// Error() 按 SDK 真实格式输出长文以验证截断。
type fakeServiceError struct {
	status  int
	code    string
	message string
}

func (e fakeServiceError) Error() string {
	return fmt.Sprintf(
		"Error returned by Compute Service. Http Status Code: %d. Error Code: %s. Message: %s. Troubleshooting Tips: See https://docs.oracle.com/ ...",
		e.status, e.code, e.message)
}
func (e fakeServiceError) GetHTTPStatusCode() int  { return e.status }
func (e fakeServiceError) GetMessage() string      { return e.message }
func (e fakeServiceError) GetCode() string         { return e.code }
func (e fakeServiceError) GetOpcRequestID() string { return "req-1" }

func TestCompactError(t *testing.T) {
	capacity := fakeServiceError{status: 500, code: "InternalError", message: "Out of host capacity."}
	tests := []struct {
		name string
		err  error
		want string
	}{
		{
			name: "非 OCI 错误原样返回",
			err:  errors.New("parse payload: unexpected end of JSON input"),
			want: "parse payload: unexpected end of JSON input",
		},
		{
			name: "带操作前缀的 OCI 错误保留前缀并剥掉长文",
			err:  fmt.Errorf("launch instance: %w", capacity),
			want: "launch instance: Out of host capacity.",
		},
		{
			name: "多层包装保留完整前缀链",
			err:  fmt.Errorf("snatch-1: %w", fmt.Errorf("launch instance: %w", capacity)),
			want: "snatch-1: launch instance: Out of host capacity.",
		},
		{
			name: "无前缀时只输出服务端消息",
			err:  error(capacity),
			want: "Out of host capacity.",
		},
		{
			name: "消息中的长 OCID 压缩为类型加尾位",
			err: fmt.Errorf("update instance: %w", fakeServiceError{
				status: 500, code: "InternalError",
				message: "instance ocid1.instance.oc1.phx.anyhqljtp537sbqcr2f5ed2jwkglwyd3g6dfbmgtwdp4xdarxum5q7tdcyla: Out of host capacity.",
			}),
			want: "update instance: instance ocid1.instance…tdcyla: Out of host capacity.",
		},
		{
			name: "非 OCI 错误里的 OCID 同样压缩",
			err:  errors.New("get instance ocid1.instance.oc1..aaaaaaaabbbbbbbbccccccccdddddddd failed"),
			want: "get instance ocid1.instance…dddddd failed",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := CompactError(tt.err); got != tt.want {
				t.Errorf("CompactError() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestErrorHint(t *testing.T) {
	tests := []struct {
		name string
		err  error
		want string
	}{
		{
			name: "非 OCI 错误无提示",
			err:  errors.New("db locked"),
			want: "",
		},
		{
			name: "已知错误码给出中文提示",
			err:  fmt.Errorf("delete vcn: %w", fakeServiceError{409, "IncorrectState", "vcn in use"}),
			want: ociErrorHints["IncorrectState"],
		},
		{
			name: "InternalError 但消息为容量不足时归到容量提示",
			err:  fmt.Errorf("launch: %w", fakeServiceError{500, "InternalError", "Out of host capacity."}),
			want: ociErrorHints["OutOfHostCapacity"],
		},
		{
			name: "未知错误码返回空串",
			err:  fmt.Errorf("x: %w", fakeServiceError{400, "SomethingNew", "boom"}),
			want: "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := ErrorHint(tt.err); got != tt.want {
				t.Errorf("ErrorHint() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestIsOnDemandUnsupported(t *testing.T) {
	ft := fakeServiceError{status: 400, code: "InvalidParameter",
		message: "Not allowed to call finetune base model ocid1.generativeaimodel.oc1.eu-frankfurt-1.tpel5q, use Endpoint: false"}
	tests := []struct {
		name string
		err  error
		want bool
	}{
		{"微调基座 400 命中(含包装链)", fmt.Errorf("genai chat: %w", ft), true},
		{"同消息但非 400 不命中", fakeServiceError{status: 500, code: "InternalError", message: ft.message}, false},
		{"普通 400 不命中", fakeServiceError{status: 400, code: "InvalidParameter", message: "bad request"}, false},
		{"非服务端错误不命中", errors.New("finetune base model"), false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := IsOnDemandUnsupported(tt.err); got != tt.want {
				t.Errorf("IsOnDemandUnsupported() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestIsModelUnavailable(t *testing.T) {
	entity404 := fakeServiceError{status: 404, code: "NotFound",
		message: "Entity with key ocid1.generativeaimodel.oc1.eu-frankfurt-1.2flsfq not found"}
	auth404 := fakeServiceError{status: 404, code: "NotAuthorizedOrNotFound",
		message: "Authorization failed or requested resource not found."}
	ft400 := fakeServiceError{status: 400, code: "InvalidParameter",
		message: "Not allowed to call finetune base model ocid1.generativeaimodel.oc1..x, use Endpoint: false"}
	tests := []struct {
		name string
		err  error
		want bool
	}{
		{"实体不存在 404 命中(含包装链)", fmt.Errorf("genai chat: %w", entity404), true},
		{"鉴权类 404 不命中(仍属租户级)", auth404, false},
		{"微调基座 400 命中", ft400, true},
		{"其他 404 消息不命中", fakeServiceError{status: 404, code: "NotFound", message: "route not found"}, false},
		{"非服务端错误不命中", errors.New("entity with key x not found"), false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := IsModelUnavailable(tt.err); got != tt.want {
				t.Errorf("IsModelUnavailable() = %v, want %v", got, tt.want)
			}
		})
	}
}
