package plan

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func TestCompilerMarksOptionalPassThroughLoads(t *testing.T) {
	artifact, diagnostics, err := Compile([]byte(optionalPassThroughPlan))
	require.NoError(t, err)
	require.False(t, HasErrors(diagnostics), "%#v", diagnostics)
	require.Len(t, artifact.Entry.Bindings, 1)
	require.NotNil(t, artifact.Entry.Bindings[0].Value.Load)
	require.True(t, artifact.Entry.Bindings[0].Value.Load.Optional)
	require.True(t, artifact.Entry.Bindings[0].Value.MayBeAbsent())
	wire, err := artifact.DeterministicJSON()
	require.NoError(t, err)
	require.Contains(t, string(wire), `"optional":true`)
	decoded, err := DecodeArtifact(wire)
	require.NoError(t, err)
	require.True(t, decoded.Entry.Bindings[0].Value.Load.Optional)
}

func TestDecodeRejectsMissingOptionalFlagForOptionalLoad(t *testing.T) {
	artifact, diagnostics, err := Compile([]byte(optionalPassThroughPlan))
	require.NoError(t, err)
	require.False(t, HasErrors(diagnostics), "%#v", diagnostics)

	artifact.Entry.Bindings[0].Value.Load.Optional = false
	artifact.Digest, err = artifact.computeDigest()
	require.NoError(t, err)
	wire, err := artifact.DeterministicJSON()
	require.NoError(t, err)
	_, err = DecodeArtifact(wire)
	require.ErrorContains(t, err, "optional=false, want true")
}

func TestDecodeRejectsOptionalFlagOnGuaranteedLoad(t *testing.T) {
	artifact := compileArtifactForTest(t, validCallbackPlan)
	require.Len(t, artifact.Entry.Bindings, 1)
	require.NotNil(t, artifact.Entry.Bindings[0].Value.Load)
	require.False(t, artifact.Entry.Bindings[0].Value.Load.Optional)

	artifact.Entry.Bindings[0].Value.Load.Optional = true
	var err error
	artifact.Digest, err = artifact.computeDigest()
	require.NoError(t, err)
	wire, err := artifact.DeterministicJSON()
	require.NoError(t, err)
	_, err = DecodeArtifact(wire)
	require.ErrorContains(t, err, "optional=true, want false")
}

const optionalPassThroughPlan = `apiVersion: loop.plan/v1
kind: PlanVersion
input:
  maybe: {type: string}
graph:
  entry: {nodeId: route, port: in}
  nodes:
  - id: route
    type: switch
    input:
      maybe: {type: string}
    switch:
      mode: first_match
      cases:
      - port: missing
        when: 'input.maybe == null'
      - port: present
        isDefault: true
  - id: success
    type: end
    end: {outcome: succeeded}
  - id: failure
    type: end
    end: {outcome: failed, code: unexpected_present_value}
  edges:
  - id: missing
    source: {nodeId: route, port: missing}
    target: {nodeId: success, port: in}
  - id: present
    source: {nodeId: route, port: present}
    target: {nodeId: failure, port: in}
`
