package sfcrmappservice

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"

	navigationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationnavigationv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

func TestApplicationDefinitionsHaveIndependentAuthority(t *testing.T) {
	for _, category := range []commonsemantic.FailureCategory{commonsemantic.FailureForbidden, commonsemantic.FailureUnavailable} {
		t.Run(string(category), func(t *testing.T) {
			view := uiAppsTestView(t, uiAppsTestNavigation)
			permissions := uiAppsTestPermissions(view)
			service := uiAppsTestService(t, view, permissions)
			calls := 0
			service.uiApps.resolveNavigation = func(context.Context, string, navigationsemantic.ResolveRequest) (navigationsemantic.ResolveResponse, error) {
				calls++
				return navigationsemantic.ResolveResponse{}, &commonsemantic.Failure{Category: category}
			}
			definitions := httptest.NewRecorder()
			service.ServeHTTP(definitions, uiAppsRequest(http.MethodGet, "/services/data/v63.0/metadata/applications"))
			if definitions.Code != http.StatusOK || calls != 0 {
				t.Fatalf("definitions depend on navigation: status=%d calls=%d body=%s", definitions.Code, calls, definitions.Body)
			}
			var body struct {
				Apps []applicationDefinition `json:"apps"`
			}
			if json.Unmarshal(definitions.Body.Bytes(), &body) != nil || len(body.Apps) != 2 || body.Apps[0].Label != "Sales" ||
				strings.Contains(definitions.Body.String(), "navigationItems") || strings.Contains(definitions.Body.String(), "Docs") {
				t.Fatalf("wrong administration projection: %s", definitions.Body)
			}
			serving := httptest.NewRecorder()
			service.ServeHTTP(serving, uiAppsRequest(http.MethodGet, "/services/data/v63.0/ui-api/apps"))
			if serving.Code == http.StatusOK || calls != 1 {
				t.Fatalf("serving navigation did not fail closed: %d calls=%d", serving.Code, calls)
			}
		})
	}
	view := uiAppsTestView(t, uiAppsTestNavigation)
	permissions := uiAppsTestPermissions(view)
	permissions.SystemPermissions = nil
	service := uiAppsTestService(t, view, permissions)
	for _, method := range []string{http.MethodGet, http.MethodPost} {
		response := httptest.NewRecorder()
		service.ServeHTTP(response, uiAppsRequest(method, "/services/data/v63.0/metadata/applications"))
		want := http.StatusForbidden
		if method == http.MethodPost {
			want = http.StatusMethodNotAllowed
		}
		if response.Code != want {
			t.Fatalf("method=%s status=%d want=%d", method, response.Code, want)
		}
	}
}

// The AppService integration runner starts this fixture to exercise real HTTP discovery
// and Studio composition. Only the upstream failure and immutable Catalog input
// are supplied here; no L1 handler, projection or browser API is replaced.
func TestStudioApplicationDefinitionsHTTPFixture(t *testing.T) {
	if os.Getenv("NEXUSCRM_STUDIO_HTTP_FIXTURE") != "1" {
		t.Skip("started by application-administration.test.tsx")
	}
	servers := map[string]string{}
	for _, status := range []int{403, 503} {
		view := uiAppsTestView(t, uiAppsTestNavigation)
		service := uiAppsTestService(t, view, uiAppsTestPermissions(view))
		category := commonsemantic.FailureForbidden
		if status == 503 {
			category = commonsemantic.FailureUnavailable
		}
		service.uiApps.resolveNavigation = func(context.Context, string, navigationsemantic.ResolveRequest) (navigationsemantic.ResolveResponse, error) {
			return navigationsemantic.ResolveResponse{}, &commonsemantic.Failure{Category: category}
		}
		var err error
		service.applicationNavigation, err = NewRuntimeApplicationNavigationClient(
			func(context.Context, string, navigationsemantic.ReadRequest) (navigationsemantic.ReadResponse, error) {
				return navigationsemantic.ReadResponse{}, &commonsemantic.Failure{Category: category}
			}, service.uiApps.resolveNavigation,
			func(context.Context, string, navigationsemantic.SaveRequest) (navigationsemantic.SaveResponse, error) {
				t.Error("navigation save must remain disabled after a failed read")
				return navigationsemantic.SaveResponse{}, &commonsemantic.Failure{Category: category}
			},
		)
		if err != nil {
			t.Fatal(err)
		}
		server := httptest.NewServer(service)
		t.Cleanup(server.Close)
		servers[fmt.Sprint(status)] = server.URL
	}
	encoded, _ := json.Marshal(servers)
	fmt.Printf("STUDIO_HTTP_FIXTURE=%s\n", encoded)
	if _, err := io.Copy(io.Discard, os.Stdin); err != nil {
		t.Fatal(err)
	}
}
