package mxgateway

import (
	"os"
	"path/filepath"
	"testing"

	pb "gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/internal/generated"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

func TestGeneratedGoldenFixturesParse(t *testing.T) {
	tests := []struct {
		name string
		path string
		msg  proto.Message
	}{
		{
			name: "open session reply",
			path: filepath.Join("..", "..", "proto", "fixtures", "golden", "open-session-reply.ok.json"),
			msg:  &pb.OpenSessionReply{},
		},
		{
			name: "register command request",
			path: filepath.Join("..", "..", "proto", "fixtures", "golden", "register-command-request.json"),
			msg:  &pb.MxCommandRequest{},
		},
		{
			name: "on data change event",
			path: filepath.Join("..", "..", "proto", "fixtures", "golden", "on-data-change-event.json"),
			msg:  &pb.MxEvent{},
		},
	}

	unmarshal := protojson.UnmarshalOptions{DiscardUnknown: false}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := os.ReadFile(tt.path)
			if err != nil {
				t.Fatalf("read fixture: %v", err)
			}

			if err := unmarshal.Unmarshal(data, tt.msg); err != nil {
				t.Fatalf("parse fixture: %v", err)
			}
		})
	}
}

// TestCommandReplyValidationFixtures locks the shared reply-validation rules to
// the behavior fixtures: a status entry fails iff its category is not OK (the
// raw success member is diagnostics only), and an HRESULT fails iff it is
// present and negative (S_FALSE and other positive COM success codes pass).
func TestCommandReplyValidationFixtures(t *testing.T) {
	tests := []struct {
		fixture     string
		wantFailure bool
	}{
		{fixture: "register.ok.reply.json", wantFailure: false},
		{fixture: "write.mxaccess-failure.reply.json", wantFailure: true},
		{fixture: "write.status-category-error-success-set.reply.json", wantFailure: true},
		{fixture: "write.status-category-ok-success-zero.reply.json", wantFailure: false},
		{fixture: "write.hresult-s-false.reply.json", wantFailure: false},
		{fixture: "write.hresult-e-fail.reply.json", wantFailure: true},
	}

	for _, tt := range tests {
		t.Run(tt.fixture, func(t *testing.T) {
			data, err := os.ReadFile(filepath.Join(
				"..", "..", "proto", "fixtures", "behavior", "command-replies", tt.fixture))
			if err != nil {
				t.Fatalf("read fixture: %v", err)
			}

			var reply pb.MxCommandReply
			if err := protojson.Unmarshal(data, &reply); err != nil {
				t.Fatalf("parse fixture: %v", err)
			}

			err = EnsureMxAccessSuccess("invoke", &reply)
			if got := err != nil; got != tt.wantFailure {
				t.Fatalf("EnsureMxAccessSuccess() failed = %v (err %v), want %v", got, err, tt.wantFailure)
			}
		})
	}
}

// TestStatusSucceededBranchesOnCategory pins the per-entry rule directly,
// including the two edges the fixtures cannot express: a nil entry is success
// and a present entry with an unspecified category is a failure.
func TestStatusSucceededBranchesOnCategory(t *testing.T) {
	tests := []struct {
		name   string
		status *MxStatusProxy
		want   bool
	}{
		{name: "nil entry", status: nil, want: true},
		{
			name: "ok category with zero success",
			status: &pb.MxStatusProxy{
				Success:  0,
				Category: pb.MxStatusCategory_MX_STATUS_CATEGORY_OK,
			},
			want: true,
		},
		{
			name: "error category with success set",
			status: &pb.MxStatusProxy{
				Success:  1,
				Category: pb.MxStatusCategory_MX_STATUS_CATEGORY_COMMUNICATION_ERROR,
			},
			want: false,
		},
		{
			name: "unspecified category with success set",
			status: &pb.MxStatusProxy{
				Success:  1,
				Category: pb.MxStatusCategory_MX_STATUS_CATEGORY_UNSPECIFIED,
			},
			want: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := StatusSucceeded(tt.status); got != tt.want {
				t.Fatalf("StatusSucceeded() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestOpenSessionFixtureProtocolVersions(t *testing.T) {
	data, err := os.ReadFile(filepath.Join("..", "..", "proto", "fixtures", "golden", "open-session-reply.ok.json"))
	if err != nil {
		t.Fatalf("read fixture: %v", err)
	}

	var reply pb.OpenSessionReply
	if err := protojson.Unmarshal(data, &reply); err != nil {
		t.Fatalf("parse fixture: %v", err)
	}

	if reply.GetGatewayProtocolVersion() != GatewayProtocolVersion {
		t.Fatalf("gateway protocol = %d, want %d", reply.GetGatewayProtocolVersion(), GatewayProtocolVersion)
	}

	if reply.GetWorkerProtocolVersion() != WorkerProtocolVersion {
		t.Fatalf("worker protocol = %d, want %d", reply.GetWorkerProtocolVersion(), WorkerProtocolVersion)
	}
}
