package dialectsfcrm

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

	"github.com/nexuscrm/shared/pkg/dialect"
)

func TestCompositeNeutralHandler_DispatchesRelativeURLWithMountedVersionAndReferenceResolution(t *testing.T) {
	var captured []dialect.DispatchRequest
	deps := dialect.Deps{
		Dispatch: func(_ context.Context, req dialect.DispatchRequest) (*dialect.DispatchResponse, error) {
			captured = append(captured, req)
			switch len(captured) {
			case 1:
				return &dialect.DispatchResponse{
					Status: http.StatusCreated,
					Header: map[string]string{"Location": "/services/data/v62.0/sobjects/Account/001xx"},
					Body:   []byte(`{"id":"001xx","success":true}`),
				}, nil
			case 2:
				return &dialect.DispatchResponse{
					Status: http.StatusOK,
					Body:   []byte(`{"Id":"001xx","Name":"Acme"}`),
				}, nil
			default:
				t.Fatalf("unexpected dispatch #%d", len(captured))
				return nil, nil
			}
		},
	}
	req := &dialect.Request{
		Version: "v62.0",
		Header: map[string][]string{
			"Authorization": {"Bearer token"},
			"Origin":        {"https://crm.example"},
			"Referer":       {"https://crm.example/app"},
			"X-CSRF-Token":  {"csrf"},
		},
		Body: []byte(`{"allOrNone":true,"compositeRequest":[` +
			`{"method":"POST","url":"sobjects/Account","referenceId":"NewAccount","body":{"Name":"Acme"},"httpHeaders":{"authorization":"Bearer child","origin":"https://override.example"}} ,` +
			`{"method":"GET","url":"sobjects/Account/@{NewAccount.id}","referenceId":"FetchAccount"}` +
			`]}`),
	}

	resp, err := CompositeNeutralHandler(context.Background(), req, deps)
	if err != nil {
		t.Fatalf("CompositeNeutralHandler returned error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200", resp.Status)
	}
	if len(captured) != 2 {
		t.Fatalf("dispatch count = %d, want 2", len(captured))
	}
	if captured[0].Path != "/services/data/v62.0/sobjects/Account" {
		t.Fatalf("first dispatch path = %q", captured[0].Path)
	}
	if captured[1].Path != "/services/data/v62.0/sobjects/Account/001xx" {
		t.Fatalf("second dispatch path = %q", captured[1].Path)
	}
	if captured[0].Header["Authorization"] != "Bearer child" ||
		captured[0].Header["Origin"] != "https://override.example" ||
		captured[0].Header["Referer"] != "https://crm.example/app" ||
		captured[0].Header["X-Csrf-Token"] != "csrf" {
		t.Fatalf("forwarded headers = %#v", captured[0].Header)
	}
	if _, ok := captured[0].Header["authorization"]; ok {
		t.Fatalf("captured lowercase authorization duplicate: %#v", captured[0].Header)
	}

	body, ok := resp.Body.(compositeResponse)
	if !ok {
		t.Fatalf("body type = %T, want compositeResponse", resp.Body)
	}
	if got := body.CompositeResponse[0].HttpHeaders["Location"]; got != "/services/data/v62.0/sobjects/Account/001xx" {
		t.Fatalf("Location header = %q", got)
	}
	second, ok := body.CompositeResponse[1].Body.(map[string]interface{})
	if !ok {
		t.Fatalf("second body = %#v", body.CompositeResponse[1].Body)
	}
	if second["Name"] != "Acme" {
		t.Fatalf("second body Name = %#v", second["Name"])
	}
}

func TestCompositeNeutralHandler_BlocksCompositeRecursionWithoutDispatch(t *testing.T) {
	called := false
	deps := dialect.Deps{
		Dispatch: func(context.Context, dialect.DispatchRequest) (*dialect.DispatchResponse, error) {
			called = true
			return nil, nil
		},
	}
	req := &dialect.Request{
		Version: "v63.0",
		Body:    []byte(`{"allOrNone":true,"compositeRequest":[{"method":"POST","url":"/services/data/v63.0/composite","referenceId":"loop"}]}`),
	}

	resp, err := CompositeNeutralHandler(context.Background(), req, deps)
	if err != nil {
		t.Fatalf("CompositeNeutralHandler returned error: %v", err)
	}
	if called {
		t.Fatal("recursive composite subrequest reached dispatch")
	}
	body := resp.Body.(compositeResponse)
	if got := body.CompositeResponse[0].HttpStatusCode; got != http.StatusBadRequest {
		t.Fatalf("subresponse status = %d, want 400", got)
	}
}

func TestCompositeBatchNeutralHandler_UsesMountedVersionAndForwardsAuthOnly(t *testing.T) {
	var captured dialect.DispatchRequest
	deps := dialect.Deps{
		Dispatch: func(_ context.Context, req dialect.DispatchRequest) (*dialect.DispatchResponse, error) {
			captured = req
			return &dialect.DispatchResponse{Status: http.StatusOK, Header: map[string]string{"X-Test": "ok"}, Body: []byte(`{"done":true}`)}, nil
		},
	}
	req := &dialect.Request{
		Version: "v61.0",
		Header: map[string][]string{
			"Authorization": {"Bearer token"},
			"Origin":        {"https://crm.example"},
		},
		Body: []byte(`{"batchRequests":[{"method":"GET","url":"sobjects/Account","referenceId":"one","httpHeaders":{"authorization":"Bearer child","Sforce-Query-Options":"batchSize=1"}}]}`),
	}

	resp, err := CompositeBatchNeutralHandler(context.Background(), req, deps)
	if err != nil {
		t.Fatalf("CompositeBatchNeutralHandler returned error: %v", err)
	}
	if captured.Path != "/services/data/v61.0/sobjects/Account" {
		t.Fatalf("dispatch path = %q", captured.Path)
	}
	wantHeaders := map[string]string{
		"Authorization":        "Bearer child",
		"Content-Type":         "application/json",
		"Sforce-Query-Options": "batchSize=1",
	}
	for k, want := range wantHeaders {
		if got := captured.Header[k]; got != want {
			t.Fatalf("header %s = %q, want %q; all=%#v", k, got, want, captured.Header)
		}
	}
	if _, ok := captured.Header["Origin"]; ok {
		t.Fatalf("batch dispatch forwarded Origin header: %#v", captured.Header)
	}
	body := resp.Body.(compositeBatchResponse)
	if body.HasErrors {
		t.Fatal("HasErrors = true, want false")
	}
	if got := body.Results[0].HttpHeaders; !reflect.DeepEqual(got, map[string]string{"X-Test": "ok"}) {
		t.Fatalf("subresponse headers = %#v", got)
	}
}

func TestCompositeBatchNeutralHandler_HaltOnErrorLeavesRemainingSlotsZeroValued(t *testing.T) {
	calls := 0
	deps := dialect.Deps{
		Dispatch: func(context.Context, dialect.DispatchRequest) (*dialect.DispatchResponse, error) {
			calls++
			return &dialect.DispatchResponse{Status: http.StatusNotFound, Body: []byte(`{"error":"missing"}`)}, nil
		},
	}
	req := &dialect.Request{
		Version: "v63.0",
		Body: []byte(`{"haltOnError":true,"batchRequests":[` +
			`{"method":"GET","url":"sobjects/Account/001","referenceId":"one"},` +
			`{"method":"GET","url":"sobjects/Account/002","referenceId":"two"}` +
			`]}`),
	}

	resp, err := CompositeBatchNeutralHandler(context.Background(), req, deps)
	if err != nil {
		t.Fatalf("CompositeBatchNeutralHandler returned error: %v", err)
	}
	if calls != 1 {
		t.Fatalf("dispatch calls = %d, want 1", calls)
	}
	body := resp.Body.(compositeBatchResponse)
	if !body.HasErrors {
		t.Fatal("HasErrors = false, want true")
	}
	if len(body.Results) != 2 {
		t.Fatalf("results length = %d, want 2", len(body.Results))
	}
	if body.Results[1].StatusCode != 0 || body.Results[1].ReferenceID != "" || body.Results[1].Result != nil {
		encoded, _ := json.Marshal(body.Results[1])
		t.Fatalf("second slot = %s, want zero-valued halted slot", encoded)
	}
}
