package openai

import (
	"context"
	"encoding/json"
	"errors"
	"reflect"
	"strings"
	"testing"

	edgeservice "iop/apps/edge/internal/service"
)

type planController struct {
	envelopes     []edgeservice.SingleRequestEnvelope
	kind          edgeservice.SingleRequestArtifactKind
	content       []byte
	writeAttempts int
	envelopeErr   error
	writeErr      error
}

func (c *planController) RequestID() string                          { return "request-1" }
func (c *planController) Binding() *edgeservice.SingleRequestBinding { return nil }
func (c *planController) Context() context.Context                   { return context.Background() }
func (c *planController) State() edgeservice.SingleRequestState {
	return edgeservice.SingleRequestStateAccepted
}
func (c *planController) ReadInternalArtifact(context.Context, edgeservice.SingleRequestArtifactKind) ([]byte, error) {
	return nil, errors.New("unused")
}
func (c *planController) SubmitEnvelope(e edgeservice.SingleRequestEnvelope) error {
	c.envelopes = append(c.envelopes, e)
	return c.envelopeErr
}
func (c *planController) WriteInternalArtifact(_ context.Context, k edgeservice.SingleRequestArtifactKind, b []byte) error {
	c.writeAttempts++
	if c.writeErr != nil {
		return c.writeErr
	}
	c.kind = k
	c.content = append([]byte(nil), b...)
	return nil
}

func validPlanStageRequest() singleRequestPlanStageRequest {
	return singleRequestPlanStageRequest{
		RequestID:        "request-1",
		Task:             "Fix immutable task",
		StageBinding:     validStageBinding(),
		Limits:           validLimits(),
		NodeRef:          "node",
		SessionID:        "session",
		UsageAttribution: "principal",
		Sequence:         1,
	}
}

func TestSingleRequestPlanStageWritesArtifact(t *testing.T) {
	d := matchingDispatch()
	tunnel := &mockTunnel{frames: framesFor(successBodyWithThoughtSignature(`{"plan":"Inspect the target.","verification":"Run focused tests."}`))}
	var captured edgeservice.ProviderPoolDispatchRequest
	provider := newSingleRequestProviderStage(&mockService{submit: func(_ context.Context, r edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
		captured = r
		return &edgeservice.ProviderPoolDispatchResult{Path: edgeservice.ProviderPoolPathTunnel, Tunnel: tunnel, DispatchInfo: d}, nil
	}})
	ctrl := &planController{}
	request := validPlanStageRequest()
	request.StageBinding.Options["response_format"] = map[string]any{"type": "caller_override_ignored"}
	content, err := newSingleRequestPlanStage(provider).run(context.Background(), request, ctrl)
	if err != nil {
		t.Fatal(err)
	}
	expected := "# Plan\n\nInspect the target.\n\n## Verification\n\nRun focused tests.\n"
	if got := string(content); got != expected {
		t.Fatalf("content=%q", got)
	}
	if ctrl.kind != edgeservice.SingleRequestArtifactPlan || len(ctrl.envelopes) != 1 || ctrl.envelopes[0].Stage != edgeservice.SingleRequestStatePlanning {
		t.Fatalf("controller=%+v", ctrl)
	}
	body, _ := captured.Tunnel.BuildBody("gemini-3.6-flash")
	if !containsAll(string(body), "Fix immutable task", "Produce exactly one JSON object", "reasoning_effort", "high") {
		t.Fatalf("body=%s", body)
	}
	var decoded map[string]any
	if err := json.Unmarshal(body, &decoded); err != nil {
		t.Fatal(err)
	}
	wantResponseFormat := map[string]any{
		"type": "json_schema",
		"json_schema": map[string]any{
			"name":   "single_request_plan",
			"strict": true,
			"schema": map[string]any{
				"type": "object",
				"properties": map[string]any{
					"plan":         map[string]any{"type": "string", "description": "A concise execution plan for the task."},
					"verification": map[string]any{"type": "string", "description": "A concise verification procedure for the plan."},
				},
				"required":             []any{"plan", "verification"},
				"additionalProperties": false,
			},
		},
	}
	if !reflect.DeepEqual(decoded["response_format"], wantResponseFormat) {
		t.Fatalf("response_format=%#v, want %#v", decoded["response_format"], wantResponseFormat)
	}
}

func TestSingleRequestPlanStageFailsClosed(t *testing.T) {
	jsonTests := []struct {
		name string
		raw  string
	}{
		{"empty-object", "{}"},
		{"missing-verification", `{"plan":"x"}`},
		{"missing-plan", `{"verification":"y"}`},
		{"empty-plan-string", `{"plan":"","verification":"y"}`},
		{"empty-verification-string", `{"plan":"x","verification":""}`},
		{"whitespace-plan-string", `{"plan":"   ","verification":"y"}`},
		{"whitespace-verification-string", `{"plan":"x","verification":"   "}`},
		{"unknown-field", `{"plan":"x","verification":"y","unknown":1}`},
		{"duplicate-plan-key", `{"plan":"A","plan":"B","verification":"V"}`},
		{"duplicate-verification-key", `{"plan":"P","verification":"V1","verification":"V2"}`},
		{"case-variant-plan-key", `{"Plan":"Inspect.","verification":"Verify."}`},
		{"case-folded-duplicate-plan-key", `{"plan":"Inspect.","Plan":"Inspect2.","verification":"Verify."}`},
		{"case-variant-verification-key", `{"plan":"Inspect.","Verification":"Verify."}`},
		{"case-folded-duplicate-verification-key", `{"plan":"Inspect.","verification":"Verify.","Verification":"Verify2."}`},
		{"trailing-json", `{"plan":"x","verification":"y"} {}`},
		{"not-json", "not json"},
	}

	for _, tt := range jsonTests {
		t.Run(tt.name, func(t *testing.T) {
			d := matchingDispatch()
			tunnel := &mockTunnel{frames: framesFor(successBody(tt.raw))}
			provider := newSingleRequestProviderStage(&mockService{submit: func(context.Context, edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
				return &edgeservice.ProviderPoolDispatchResult{Path: edgeservice.ProviderPoolPathTunnel, Tunnel: tunnel, DispatchInfo: d}, nil
			}})
			ctrl := &planController{}
			_, err := newSingleRequestPlanStage(provider).run(context.Background(), validPlanStageRequest(), ctrl)
			if !errors.Is(err, errSingleRequestPlanStage) {
				t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
			}
			if ctrl.writeAttempts != 0 || len(ctrl.content) > 0 {
				t.Fatalf("artifact wrote content on failure: attempts=%d content=%s", ctrl.writeAttempts, ctrl.content)
			}
		})
	}

	t.Run("render-size-exact-boundary-passes", func(t *testing.T) {
		raw := `{"plan":"A","verification":"B"}`
		rendered := "# Plan\n\nA\n\n## Verification\n\nB\n"
		content, err := renderSingleRequestPlan(raw, len(rendered))
		if err != nil {
			t.Fatalf("unexpected error on exact render boundary: %v", err)
		}
		if string(content) != rendered {
			t.Fatalf("content mismatch: got %q, want %q", content, rendered)
		}
	})

	t.Run("render-size-exceeded-boundary-fails", func(t *testing.T) {
		raw := `{"plan":"A","verification":"B"}`
		rendered := "# Plan\n\nA\n\n## Verification\n\nB\n"
		_, err := renderSingleRequestPlan(raw, len(rendered)-1)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
	})

	t.Run("provider-failure-rejects", func(t *testing.T) {
		provider := newSingleRequestProviderStage(&mockService{submit: func(context.Context, edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
			return nil, errors.New("provider failure")
		}})
		ctrl := &planController{}
		_, err := newSingleRequestPlanStage(provider).run(context.Background(), validPlanStageRequest(), ctrl)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
		if len(ctrl.content) > 0 {
			t.Fatalf("artifact wrote content on provider failure")
		}
	})

	t.Run("context-cancelled-rejects", func(t *testing.T) {
		ctx, cancel := context.WithCancel(context.Background())
		cancel()
		provider := newSingleRequestProviderStage(&mockService{submit: func(ctx context.Context, _ edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
			return nil, ctx.Err()
		}})
		ctrl := &planController{}
		_, err := newSingleRequestPlanStage(provider).run(ctx, validPlanStageRequest(), ctrl)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
		if len(ctrl.content) > 0 {
			t.Fatalf("artifact wrote content on context cancel")
		}
	})

	t.Run("envelope-rejection-prevents-provider-call-and-artifact", func(t *testing.T) {
		providerCalled := false
		provider := newSingleRequestProviderStage(&mockService{submit: func(context.Context, edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
			providerCalled = true
			return nil, nil
		}})
		ctrl := &planController{envelopeErr: errors.New("envelope error")}
		_, err := newSingleRequestPlanStage(provider).run(context.Background(), validPlanStageRequest(), ctrl)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
		if providerCalled {
			t.Fatalf("provider was called after envelope rejection")
		}
		if len(ctrl.content) > 0 {
			t.Fatalf("artifact wrote content on envelope rejection")
		}
	})

	t.Run("artifact-write-failure-rejects", func(t *testing.T) {
		d := matchingDispatch()
		tunnel := &mockTunnel{frames: framesFor(successBody(`{"plan":"Plan text","verification":"Verification text"}`))}
		provider := newSingleRequestProviderStage(&mockService{submit: func(context.Context, edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
			return &edgeservice.ProviderPoolDispatchResult{Path: edgeservice.ProviderPoolPathTunnel, Tunnel: tunnel, DispatchInfo: d}, nil
		}})
		ctrl := &planController{writeErr: errors.New("write failure")}
		_, err := newSingleRequestPlanStage(provider).run(context.Background(), validPlanStageRequest(), ctrl)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
		if ctrl.writeAttempts != 1 || len(ctrl.content) > 0 {
			t.Fatalf("expected writeAttempts=1 and len(content)==0, got attempts=%d len=%d", ctrl.writeAttempts, len(ctrl.content))
		}
	})

	invalidRequests := []struct {
		name   string
		mutate func(*singleRequestPlanStageRequest)
	}{
		{"empty-request-id", func(r *singleRequestPlanStageRequest) { r.RequestID = "" }},
		{"empty-task", func(r *singleRequestPlanStageRequest) { r.Task = "" }},
		{"zero-sequence", func(r *singleRequestPlanStageRequest) { r.Sequence = 0 }},
	}

	for _, tt := range invalidRequests {
		t.Run(tt.name, func(t *testing.T) {
			providerCalled := false
			provider := newSingleRequestProviderStage(&mockService{submit: func(context.Context, edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
				providerCalled = true
				return nil, nil
			}})
			req := validPlanStageRequest()
			tt.mutate(&req)
			ctrl := &planController{}
			_, err := newSingleRequestPlanStage(provider).run(context.Background(), req, ctrl)
			if !errors.Is(err, errSingleRequestPlanStage) {
				t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
			}
			if providerCalled {
				t.Fatalf("provider called on invalid request input")
			}
			if len(ctrl.envelopes) > 0 || len(ctrl.content) > 0 {
				t.Fatalf("controller invoked on invalid request input")
			}
		})
	}

	t.Run("nil-provider-rejects", func(t *testing.T) {
		ctrl := &planController{}
		_, err := newSingleRequestPlanStage(nil).run(context.Background(), validPlanStageRequest(), ctrl)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
	})

	t.Run("nil-controller-rejects", func(t *testing.T) {
		provider := newSingleRequestProviderStage(&mockService{})
		_, err := newSingleRequestPlanStage(provider).run(context.Background(), validPlanStageRequest(), nil)
		if !errors.Is(err, errSingleRequestPlanStage) {
			t.Fatalf("expected errSingleRequestPlanStage, got %v", err)
		}
	})
}

func containsAll(s string, parts ...string) bool {
	for _, p := range parts {
		if !strings.Contains(s, p) {
			return false
		}
	}
	return true
}
