package dialectsfcrm

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"
	"testing"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestEffectivePermissionsProjectsFieldPermissions(t *testing.T) {
	admin := &fakePermissionAdmin{
		effective: &contract.EffectivePermissionsResult{
			SystemPermissions: map[string]bool{"ViewSetup": true},
			CustomPermissions: map[string]bool{"CanExport": true},
			ObjectPermissions: []contract.ObjectAccessProjection{{
				ResourceType: "Account",
				CanRead:      true,
			}},
			FieldPermissions: []contract.FieldAccessProjection{{
				ResourceType: "Account",
				FieldName:    "Secret__c",
				CanRead:      false,
				CanEdit:      false,
			}},
		},
	}
	resp, err := EffectivePermissionsNeutralHandler(context.Background(), &dialect.Request{User: testUser()}, permissionDeps(admin))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	body := marshalJSON(t, resp.Body)
	if !strings.Contains(body, `"Field":"Account.Secret__c"`) {
		t.Fatalf("body missing qualified field permission: %s", body)
	}
	if !strings.Contains(body, `"customPermissions":{"CanExport":true}`) {
		t.Fatalf("body missing customPermissions: %s", body)
	}
}

func TestEffectivePermissionsForbiddenUsesV63Envelope(t *testing.T) {
	admin := &fakePermissionAdmin{
		effectiveErr: &contract.PermissionAdminError{
			Kind:    contract.PermissionAdminErrorForbidden,
			Message: "You do not have permission to view another user's effective permissions.",
		},
	}
	resp, err := EffectivePermissionsNeutralHandler(context.Background(), &dialect.Request{User: testUser(), Query: map[string][]string{"userId": {"user-2"}}}, permissionDeps(admin))
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	assertSFError(t, resp, http.StatusForbidden, wire.SFCodeForbidden)
}

func TestPermissionAdminMissingContractFailsClosed(t *testing.T) {
	resp, err := EffectivePermissionsNeutralHandler(context.Background(), &dialect.Request{}, dialect.Deps{})
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	assertSFError(t, resp, http.StatusInternalServerError, wire.SFCodeServerError)
}

func permissionDeps(admin contract.PermissionAdmin) dialect.Deps {
	return dialect.Deps{Contracts: contract.Deps{PermissionAdmin: admin}}
}

func testUser() *models.UserSession {
	return &models.UserSession{ID: "user-1", ProfileID: "profile-1"}
}

func marshalJSON(t *testing.T, v any) string {
	t.Helper()
	data, err := json.Marshal(v)
	if err != nil {
		t.Fatalf("marshal body: %v", err)
	}
	return string(data)
}

func assertSFError(t *testing.T, resp *dialect.Response, status int, code string) {
	t.Helper()
	if resp.Status != status {
		t.Fatalf("status = %d, want %d; body=%#v", resp.Status, status, resp.Body)
	}
	errs, ok := resp.Body.([]wire.SFError)
	if !ok {
		t.Fatalf("body type = %T, want []wire.SFError", resp.Body)
	}
	if len(errs) != 1 || errs[0].ErrorCode != code {
		t.Fatalf("errors = %+v, want one %s", errs, code)
	}
}

type fakePermissionAdmin struct {
	effective         *contract.EffectivePermissionsResult
	effectiveErr      error
	saveRecordTypeErr error

	// Golden-only wire fixtures. These let the byte-golden test drive each
	// list/catalog/grant getter with representative non-trivial DTOs without
	// touching production code. Zero values preserve the historical
	// "return nil, nil" behavior for the existing error-path tests.
	objectPermissions []contract.ObjectAccessProjection
	systemCatalog     []contract.CapabilityCatalogEntry
	recordTypeCatalog []contract.PermissionRecordTypeCatalogEntry
	recordTypeGrants  []contract.RecordTypeVisibilityGrant
	customCatalog     []contract.CustomCapabilityCatalogEntry
	customPermGrants  []contract.CustomCapabilityGrant
	systemGrants      map[string]bool
}

func (f *fakePermissionAdmin) ListObjectPermissions(context.Context, *models.UserSession) ([]contract.ObjectAccessProjection, error) {
	return f.objectPermissions, nil
}
func (f *fakePermissionAdmin) ListSystemPermissionCatalog(context.Context) ([]contract.CapabilityCatalogEntry, error) {
	return f.systemCatalog, nil
}
func (f *fakePermissionAdmin) GetPermissionSetSystemGrants(context.Context, string) (map[string]bool, error) {
	return f.systemGrants, nil
}
func (f *fakePermissionAdmin) SavePermissionSetSystemGrants(context.Context, string, map[string]bool, *models.UserSession) error {
	return nil
}
func (f *fakePermissionAdmin) EffectivePermissions(context.Context, *models.UserSession, string) (*contract.EffectivePermissionsResult, error) {
	if f.effectiveErr != nil {
		return nil, f.effectiveErr
	}
	return f.effective, nil
}
func (f *fakePermissionAdmin) ListRecordTypeCatalog(context.Context) ([]contract.PermissionRecordTypeCatalogEntry, error) {
	return f.recordTypeCatalog, nil
}
func (f *fakePermissionAdmin) GetPermissionSetRecordTypeGrants(context.Context, string) ([]contract.RecordTypeVisibilityGrant, error) {
	return f.recordTypeGrants, nil
}
func (f *fakePermissionAdmin) SavePermissionSetRecordTypeGrants(context.Context, string, map[string]contract.PermissionRecordTypeGrantChange) error {
	return f.saveRecordTypeErr
}
func (f *fakePermissionAdmin) ListCustomPermissionCatalog(context.Context) ([]contract.CustomCapabilityCatalogEntry, error) {
	return f.customCatalog, nil
}
func (f *fakePermissionAdmin) GetPermissionSetCustomPermissionGrants(context.Context, string) ([]contract.CustomCapabilityGrant, error) {
	return f.customPermGrants, nil
}
func (f *fakePermissionAdmin) SavePermissionSetCustomPermissionGrants(context.Context, string, map[string]bool) error {
	return nil
}
