package dialectsfcrm

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

type queryDependencyStatusError int

func (e queryDependencyStatusError) Error() string   { return "private diagnostic" }
func (e queryDependencyStatusError) HTTPStatus() int { return int(e) }

type dateDependencyMetadata struct {
	contract.Metadata
	stage string
	err   error
	calls int
}

func (m *dateDependencyMetadata) Describe(_ context.Context, object string) (*models.ObjectMetadata, error) {
	m.calls++
	if (m.stage == "field" && object == "Item") || (m.stage == "parent" && object == "Parent") || (m.stage == "semijoin" && object == "Child") {
		return nil, m.err
	}
	relationship := "Parent"
	return &models.ObjectMetadata{APIName: object, Fields: []models.FieldMetadata{
		{APIName: "Due", Type: "Date"},
		{APIName: "ParentId", Type: "reference", ReferenceTo: []string{"Parent"}, RelationshipName: &relationship},
	}}, nil
}
func (m *dateDependencyMetadata) ListObjects(context.Context) ([]models.ObjectMetadata, error) {
	m.calls++
	return nil, m.err
}

type dateDependencyQuery struct {
	queryFailureStub
	stage                      string
	err                        error
	calendarCalls, recordCalls int
}

func (q *dateDependencyQuery) Query(_ context.Context, spec queryspec.QuerySpec, _ *models.UserSession) (*contract.QueryResult, error) {
	if spec.ObjectAPIName == "User" {
		q.calendarCalls++
		if q.stage == "calendar" {
			return nil, q.err
		}
		return &contract.QueryResult{Done: true, Records: []contract.Record{{"TimeZoneSidKey": "UTC"}}}, nil
	}
	q.recordCalls++
	return nil, q.err
}

// Inject failures at each dependency boundary, then assert the public response
// and that date preparation failures never reach the requested record query.
func TestQueryDependencyFailuresThroughPublicRoutes(t *testing.T) {
	for _, failure := range []struct {
		name   string
		err    error
		status int
		code   string
	}{
		{"unknown", errors.New("private diagnostic"), 500, "INTERNAL_ERROR"},
		{"wrapped unknown", fmt.Errorf("private diagnostic: %w", errors.New("storage failure")), 500, "INTERNAL_ERROR"},
		{"canceled", fmt.Errorf("private diagnostic: %w", context.Canceled), 499, "UNKNOWN_EXCEPTION"},
		{"deadline", fmt.Errorf("private diagnostic: %w", context.DeadlineExceeded), 504, "UNKNOWN_EXCEPTION"},
		{"unavailable", queryDependencyStatusError(503), 503, "UNKNOWN_EXCEPTION"},
		{"internal", queryDependencyStatusError(500), 500, "INTERNAL_ERROR"},
		{"unrecognized status", queryDependencyStatusError(502), 500, "INTERNAL_ERROR"},
		{"forbidden", queryDependencyStatusError(403), 403, "FORBIDDEN"},
		{"missing", queryDependencyStatusError(404), 404, "NOT_FOUND"},
		{"invalid", queryDependencyStatusError(400), 400, "INVALID_QUERY"},
		{"unauthenticated", queryDependencyStatusError(401), 401, "INVALID_SESSION_ID"},
		{"conflict", queryDependencyStatusError(409), 409, "CONFLICT"},
		{"exhausted", queryDependencyStatusError(429), 429, "LIMIT_EXCEEDED"},
		{"status deadline", queryDependencyStatusError(504), 504, "UNKNOWN_EXCEPTION"},
		{"status canceled", queryDependencyStatusError(499), 499, "UNKNOWN_EXCEPTION"},
	} {
		for _, stage := range []struct {
			name, raw     string
			metadataCalls int
		}{
			{"calendar", "SELECT Id FROM Item WHERE Due = TODAY", 0},
			{"field", "SELECT Id FROM Item WHERE Due = TODAY", 1},
			{"parent", "SELECT Id FROM Item WHERE Parent.Due = TODAY", 2},
			{"child", "SELECT Id, (SELECT Id FROM Children WHERE Due = TODAY) FROM Item", 1},
			{"semijoin", "SELECT Id FROM Item WHERE Id IN (SELECT ParentId FROM Child WHERE Due = TODAY)", 1},
			{"records", "SELECT Id FROM Item WHERE Due = TODAY", 1},
			{"ordinary records", "SELECT Id FROM Item", 0},
		} {
			for _, route := range []string{"query", "queryAll", "queryMore"} {
				if stage.name == "calendar" && route == "queryMore" {
					continue
				} // A continuation reuses its calendar.
				t.Run(failure.name+"/"+stage.name+"/"+route, func(t *testing.T) {
					metadata := &dateDependencyMetadata{stage: stage.name, err: failure.err}
					query := &dateDependencyQuery{stage: stage.name, err: failure.err}
					req := &dialect.Request{Version: "v63.0", User: &models.UserSession{ID: "reader"}, Query: map[string][]string{"q": {stage.raw}}}
					handler := QueryNeutralHandler
					calendarCalls := 1
					if stage.name == "ordinary records" {
						calendarCalls = 0
					}
					if route == "queryAll" {
						handler = QueryAllNeutralHandler
					}
					if route == "queryMore" {
						handler = QueryMoreNeutralHandler
						calendarCalls = 0
						data, err := json.Marshal(map[string]any{"q": url.QueryEscape(stage.raw), "limit": 200, "offset": 200, "dateAnchor": map[string]string{"instant": "2026-09-08T00:30:00Z", "timeZone": "UTC"}})
						if err != nil {
							t.Fatal(err)
						}
						req.Params = map[string]string{"locator": base64.URLEncoding.EncodeToString(data)}
						req.Query = nil
					}
					response, err := handler(context.Background(), req, dialect.Deps{Contracts: contract.Deps{Metadata: metadata, Query: query}})
					if err != nil {
						t.Fatal(err)
					}
					body, err := json.Marshal(response.Body)
					if err != nil {
						t.Fatal(err)
					}
					if response.Status != failure.status || !strings.Contains(string(body), `"errorCode":"`+failure.code+`"`) || strings.Contains(string(body), "private diagnostic") {
						t.Fatalf("status=%d want=%d body=%s", response.Status, failure.status, body)
					}
					recordCalls := 0
					if stage.name == "records" || stage.name == "ordinary records" {
						recordCalls = 1
					}
					if query.calendarCalls != calendarCalls || query.recordCalls != recordCalls || metadata.calls != stage.metadataCalls {
						t.Fatalf("calendar=%d records=%d metadata=%d", query.calendarCalls, query.recordCalls, metadata.calls)
					}
				})
			}
		}
	}
}
