package main

import (
	"encoding/json"
	"fmt"

	module "bijection.dev/module"
	"bijection.dev/module/product"
	"github.com/rafflesia-ai/bijection/algebra"
	aa "github.com/rafflesia-ai/bijection/algebra/action"
	av "github.com/rafflesia-ai/bijection/algebra/value"
	workflow "github.com/rafflesia-ai/bijection/algebra/workflow"
)

const signatureChanged = "onboarding.signature_changed"

func signatureRequestObject() algebra.ObjectDefinition {
	return must(algebra.NewObjectDefinition(algebra.ObjectOptions{Name: "SignatureRequest", PlatformBacked: true, Key: []string{"request_id"}, Description: "One immutable agreement-document signature round for an onboarding case.", Properties: []algebra.ObjectProperty{
		property("request_id", av.TypeString, false, false), property("case_id", av.TypeString, false, false), property("supplier_id", av.TypeString, false, false), property("signer", av.TypeString, false, false),
		signatureFileProperty("document_file_id", false, false), property("document_revision", av.TypeInteger, false, false), property("requested_at", av.TypeTimestamp, false, false), property("reminder_at", av.TypeTimestamp, false, false), property("expires_at", av.TypeTimestamp, false, false),
		property("status", av.TypeString, false, true, "requested", "sending", "sent", "signed", "expired", "cancelled", "activated"), property("delivery_id", av.TypeString, true, true), signatureFileProperty("signature_evidence_file_id", true, true),
	}, Display: algebra.ObjectDisplay{Primary: "request_id", Secondary: "status"}}))
}

func signatureRelationships() []algebra.RelationshipDefinition {
	return []algebra.RelationshipDefinition{
		must(algebra.NewRelationshipDefinition(algebra.RelationshipOptions{Name: "case", From: "SignatureRequest", To: "OnboardingCase", Cardinality: algebra.RelationshipOne, Inverse: "signature_requests", Pairs: []algebra.FieldPair{algebra.NewFieldPair("case_id", "case_id")}})),
		must(algebra.NewRelationshipDefinition(algebra.RelationshipOptions{Name: "signature_requests", From: "OnboardingCase", To: "SignatureRequest", Cardinality: algebra.RelationshipMany, Inverse: "case", Pairs: []algebra.FieldPair{algebra.NewFieldPair("case_id", "case_id")}})),
	}
}

func signatureFunctions() []algebra.FunctionDefinition {
	parameter := must(algebra.NewParameterDefinition(algebra.ParameterOptions{Name: "request_id", Type: av.TypeString}))
	requestColumn := must(av.NewColumn(av.ColumnOptions{Name: "request_id", Type: av.TypeString, ObjectType: "SignatureRequest"}))
	evidenceContext := must(av.NewFileContext("request_id", "signature_evidence_file_id"))
	evidenceColumn := must(av.NewColumn(av.ColumnOptions{Name: "signature_evidence_file_id", Type: av.TypeString, Nullable: true, FileContext: &evidenceContext}))
	stateSchema := must(av.NewSchema([]av.Column{
		requestColumn, column("case_id", av.TypeString, false), column("supplier_id", av.TypeString, false), column("document_file_id", av.TypeString, false), column("document_revision", av.TypeInteger, false),
		column("reminder_at", av.TypeTimestamp, false), column("expires_at", av.TypeTimestamp, false), column("status", av.TypeString, false), evidenceColumn,
	}, nil, nil))
	stateBody := must(algebra.SQLRelation(algebra.SQLRelationOptions{Dialect: algebra.DialectPostgres, Text: `SELECT request_id, case_id, supplier_id, document_file_id, document_revision, reminder_at, expires_at, status, signature_evidence_file_id FROM "input.requests" WHERE request_id = $1 LIMIT 1`, Inputs: []algebra.SQLInput{{Name: "requests", Relation: algebra.SourceRelation("SignatureRequest", "request")}}, Arguments: []algebra.Expr{algebra.Param("request_id")}, Schema: stateSchema}))
	state := must(algebra.NewFunctionDefinition(algebra.FunctionOptions{Name: "signature_request_state", Parameters: []algebra.Parameter{parameter}, Cardinality: algebra.CardinalityOne, ResultKind: "onboarding.signature.state", Body: stateBody, Doc: "Read the current state of one exact signature round."}))
	terminalSchema := must(av.NewSchema([]av.Column{column("complete", av.TypeBoolean, false)}, nil, nil))
	terminalBody := must(algebra.SQLRelation(algebra.SQLRelationOptions{Dialect: algebra.DialectPostgres, Text: `SELECT status IN ('signed', 'expired', 'cancelled', 'activated') AS complete FROM "input.requests" WHERE request_id = $1 LIMIT 1`, Inputs: []algebra.SQLInput{{Name: "requests", Relation: algebra.SourceRelation("SignatureRequest", "request")}}, Arguments: []algebra.Expr{algebra.Param("request_id")}, Schema: terminalSchema}))
	terminal := must(algebra.NewFunctionDefinition(algebra.FunctionOptions{Name: "signature_request_terminal", Parameters: []algebra.Parameter{parameter}, Cardinality: algebra.CardinalityOne, ResultKind: "onboarding.signature.terminal", Body: terminalBody, Doc: "Closed source check for a terminal signature round."}))
	listSchema := must(av.NewSchema([]av.Column{requestColumn, column("case_id", av.TypeString, false), column("supplier_id", av.TypeString, false), column("document_revision", av.TypeInteger, false), column("requested_at", av.TypeTimestamp, false), column("reminder_at", av.TypeTimestamp, false), column("expires_at", av.TypeTimestamp, false), column("status", av.TypeString, false), column("delivery_id", av.TypeString, true)}, []av.Order{must(av.NewOrder("requested_at", av.Descending, av.NullsLast)), must(av.NewOrder("request_id", av.Ascending, av.NullsLast))}, nil))
	listBody := must(algebra.SQLRelation(algebra.SQLRelationOptions{Dialect: algebra.DialectPostgres, Text: `SELECT request_id, case_id, supplier_id, document_revision, requested_at, reminder_at, expires_at, status, delivery_id FROM "input.requests" ORDER BY requested_at DESC, request_id ASC LIMIT 200`, Inputs: []algebra.SQLInput{{Name: "requests", Relation: algebra.SourceRelation("SignatureRequest", "request")}}, Schema: listSchema}))
	list := must(algebra.NewFunctionDefinition(algebra.FunctionOptions{Name: "signature_requests", Cardinality: algebra.CardinalityMany, ResultKind: "onboarding.signature.requests", Body: listBody, Doc: "Visible signature rounds with their business deadlines, delivery evidence and terminal state."}))
	return []algebra.FunctionDefinition{state, terminal, list}
}

func signatureFileProperty(name string, nullable, writable bool) algebra.ObjectProperty {
	return must(algebra.NewObjectProperty(algebra.ObjectPropertyOptions{Column: column(name, av.TypeString, nullable), Type: algebra.FilePropertyType(), Writable: writable}))
}

func signatureConnection() product.Connection {
	fields := []product.OperationInputField{{Name: "request_id", Input: product.OperationInput{Type: "string", Required: true, MinimumLength: 1, MaximumLength: 255}}, {Name: "signer", Input: product.OperationInput{Type: "string", Required: true, MinimumLength: 3, MaximumLength: 320}}, {Name: "document_revision", Input: product.OperationInput{Type: "integer", Required: true}}, {Name: "kind", Input: product.OperationInput{Type: "string", Required: true, MinimumLength: 4, MaximumLength: 16}}}
	operation := product.NewConnectionOperation(product.ConnectionOperation{Name: "send_signature_request", Method: "POST", Path: "/v1/signature-requests", Input: product.OperationInput{Type: "object", Required: true, Fields: fields}, Delivery: product.DeliveryContract{Kind: "keyed", TokenKind: "header", TokenName: "Idempotency-Key", ProviderWindowSeconds: 2678400}, SuccessStatuses: []int{200, 201, 202}, Acknowledgement: []product.AcknowledgementField{{Name: "delivery_id", Path: "/id", Type: "string", Required: true}, {Name: "status", Path: "/status", Type: "string", Required: true}}})
	return product.NewConnection(product.Connection{Name: "signature_delivery", Kind: "http", Direction: "destination", Description: "The agreement-signature provider used only through governed Action delivery.", Secrets: map[string]string{"URL": "signature_delivery_url", "TOKEN": "signature_delivery_token"}, Auth: &product.ConnectionAuth{Kind: "bearer"}, Operations: []product.ConnectionOperation{operation}})
}

func signatureActions() []product.Action {
	yes, no := policy(algebra.True()), policy(algebra.False())
	pending := algebra.In(algebra.FieldRef("target", "status"), algebra.Literal(text("requested")), algebra.Literal(text("sending")), algebra.Literal(text("sent")))
	requestAllowed := policy(algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "status"), algebra.Literal(text("approved"))))
	return []product.Action{
		product.NewAction(product.Action{Name: "RequestSignature", Label: "Request agreement signature", Target: "OnboardingCase", Inputs: []product.ActionInput{{Name: "request_id", Type: "string", MinimumLength: 1}, {Name: "document", Type: "file"}, {Name: "document_revision", Type: "integer", Minimum: intPointer(1)}, {Name: "signer", Type: "string", MinimumLength: 3}, {Name: "requested_at", Type: "timestamp"}, {Name: "reminder_at", Type: "timestamp"}, {Name: "expires_at", Type: "timestamp"}}, Programs: deferred("request_signature"), Ceiling: product.ProgramCeiling{Reads: []product.ProgramRead{read("target", "case_id"), read("target", "supplier_id"), read("target", "status"), read("input", "request_id"), read("input", "document"), read("input", "document_revision"), read("input", "signer"), read("input", "requested_at"), read("input", "reminder_at"), read("input", "expires_at")}, Dependencies: []string{"OnboardingCase", "SignatureRequest"}, Effects: []product.ProgramEffectPermission{permission("create_object", "SignatureRequest", "case_id", "supplier_id", "signer", "document_file_id", "document_revision", "requested_at", "reminder_at", "expires_at", "status", "delivery_id", "signature_evidence_file_id"), permission("set_properties", "OnboardingCase", "status"), permission("start_workflow", "CollectSignature", "input")}, MaxEffects: 3, MaxValueBytes: 32768}, AllowedFor: yes, AllowedIf: requestAllowed, RequiresReview: yes}),
		product.NewAction(product.Action{Name: "SendSignatureRequest", Label: "Send signature request", Description: "Queue the signature request for delivery. The round becomes sending; only the provider's acknowledgement makes it sent.", Target: "SignatureRequest", Programs: deferred("send_signature_request"), Ceiling: signatureDeliveryCeiling(true), AllowedFor: yes, AllowedIf: policy(algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "status"), algebra.Literal(text("requested")))), RequiresReview: no}),
		product.NewAction(product.Action{Name: "ConfirmSignatureDelivery", Label: "Confirm signature delivery", Description: "Record the signature provider's own acknowledged delivery identity. This is the evidence that turns a sending round into a sent one.", DeniedReason: "Only a round whose delivery is still in flight can be confirmed.", Target: "SignatureRequest", Inputs: []product.ActionInput{{Name: "delivery_id", Type: "string", MinimumLength: 1, Label: "Delivery", Doc: "The identity the signature provider acknowledged for this exact delivery."}}, Programs: deferred("confirm_signature_delivery"), Ceiling: product.ProgramCeiling{Reads: []product.ProgramRead{read("target", "request_id"), read("target", "status"), read("input", "delivery_id")}, Dependencies: []string{"SignatureRequest"}, Effects: []product.ProgramEffectPermission{permission("set_properties", "SignatureRequest", "status", "delivery_id")}, MaxEffects: 1, MaxValueBytes: 8192}, AllowedFor: yes, AllowedIf: policy(algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "status"), algebra.Literal(text("sending")))), RequiresReview: no}),
		product.NewAction(product.Action{Name: "SendSignatureReminder", Label: "Send signature reminder", Target: "SignatureRequest", Programs: deferred("send_signature_reminder"), Ceiling: signatureDeliveryCeiling(false), AllowedFor: yes, AllowedIf: policy(pending), RequiresReview: no}),
		product.NewAction(product.Action{Name: "AcceptSignature", Label: "Accept signature evidence", Target: "SignatureRequest", Inputs: []product.ActionInput{{Name: "document_revision", Type: "integer"}, {Name: "signature_evidence", Type: "file"}}, Programs: deferred("accept_signature"), Ceiling: product.ProgramCeiling{Reads: signatureReads(read("target", "request_id"), read("target", "status"), read("target", "document_revision"), read("target", "expires_at"), read("input", "document_revision"), read("input", "signature_evidence")), Dependencies: []string{"SignatureRequest"}, Effects: []product.ProgramEffectPermission{permission("set_properties", "SignatureRequest", "status", "signature_evidence_file_id"), permission("emit", signatureChanged)}, MaxEffects: 2, MaxValueBytes: 16384}, AllowedFor: yes, AllowedIf: policy(algebra.And(pending, algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "document_revision"), algebra.FieldRef("input", "document_revision")), algebra.Compare(algebra.CompareLT, algebra.BasisTime(), algebra.FieldRef("target", "expires_at")))), RequiresReview: yes}),
		product.NewAction(product.Action{Name: "ExpireSignatureRequest", Label: "Expire unsigned request", Target: "SignatureRequest", Programs: deferred("expire_signature"), Ceiling: product.ProgramCeiling{Reads: signatureReads(read("target", "request_id"), read("target", "case_id"), read("target", "status"), read("target", "expires_at")), Dependencies: []string{"SignatureRequest", "OnboardingCase"}, Effects: []product.ProgramEffectPermission{permission("set_properties", "SignatureRequest", "status"), permission("set_properties", "OnboardingCase", "status"), permission("emit", signatureChanged)}, MaxEffects: 3, MaxValueBytes: 8192}, AllowedFor: yes, AllowedIf: policy(algebra.And(pending, algebra.Compare(algebra.CompareGTE, algebra.BasisTime(), algebra.FieldRef("target", "expires_at")))), RequiresReview: no}),
		product.NewAction(product.Action{Name: "CancelSignatureRequest", Label: "Cancel signature request", Target: "SignatureRequest", Programs: deferred("cancel_signature"), Ceiling: product.ProgramCeiling{Reads: []product.ProgramRead{read("target", "request_id"), read("target", "case_id"), read("target", "status")}, Dependencies: []string{"SignatureRequest", "OnboardingCase"}, Effects: []product.ProgramEffectPermission{permission("set_properties", "SignatureRequest", "status"), permission("set_properties", "OnboardingCase", "status"), permission("emit", signatureChanged)}, MaxEffects: 3, MaxValueBytes: 8192}, AllowedFor: yes, AllowedIf: policy(pending), RequiresReview: yes}),
		product.NewAction(product.Action{Name: "ActivateSignedAgreement", Label: "Activate signed agreement", Target: "SignatureRequest", Inputs: []product.ActionInput{{Name: "document_revision", Type: "integer"}, {Name: "signature_evidence", Type: "file"}}, Programs: deferred("activate_signed_agreement"), Ceiling: product.ProgramCeiling{Reads: []product.ProgramRead{read("target", "request_id"), read("target", "case_id"), read("target", "supplier_id"), read("target", "status"), read("target", "document_revision"), read("target", "signature_evidence_file_id"), read("input", "document_revision"), read("input", "signature_evidence")}, Dependencies: []string{"SignatureRequest", "OnboardingCase", "Supplier"}, Effects: []product.ProgramEffectPermission{permission("set_properties", "SignatureRequest", "status"), permission("set_properties", "OnboardingCase", "status"), permission("set_properties", "Supplier", "is_active")}, MaxEffects: 3, MaxValueBytes: 16384}, AllowedFor: yes, AllowedIf: policy(algebra.And(algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "status"), algebra.Literal(text("signed"))), algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "document_revision"), algebra.FieldRef("input", "document_revision")), algebra.Compare(algebra.CompareEq, algebra.FieldRef("target", "signature_evidence_file_id"), algebra.FieldRef("input", "signature_evidence")))), RequiresReview: no}),
	}
}

func signatureReads(reads ...product.ProgramRead) []product.ProgramRead {
	return append(reads, read("basis", "basis.time"))
}
func intPointer(value int64) *int64 { return &value }

func signatureDeliveryCeiling(changesStatus bool) product.ProgramCeiling {
	reads := []product.ProgramRead{read("target", "request_id"), read("target", "signer"), read("target", "document_revision"), read("target", "status")}
	effects := []product.ProgramEffectPermission{permission("invoke", "signature_delivery.send_signature_request")}
	fields := []string(nil)
	max := 1
	if changesStatus {
		effects = append(effects, permission("set_properties", "SignatureRequest", "status"))
		fields = []string{"SignatureRequest"}
		max = 2
	}
	return product.ProgramCeiling{Reads: reads, Dependencies: append(fields, "signature_delivery.send_signature_request"), Effects: effects, MaxEffects: max, MaxValueBytes: 16384}
}

func requestSignature(_ module.Context, value module.Value) (module.Value, error) {
	f, err := decodeSignatureFacts(value)
	if err != nil {
		return module.Value{}, err
	}
	requested, err := signatureInputTime(f, "requested_at")
	if err != nil {
		return module.Value{}, err
	}
	reminder, err := signatureInputTime(f, "reminder_at")
	if err != nil {
		return module.Value{}, err
	}
	expiry, err := signatureInputTime(f, "expires_at")
	if err != nil {
		return module.Value{}, err
	}
	if !reminder.After(requested) || !expiry.After(reminder) || expiry.After(requested.AddDate(0, 0, 45)) {
		return module.Value{}, fmt.Errorf("signature schedule must progress within 45 calendar days")
	}
	request := expression("input", "request_id")
	created := create("SignatureRequest", "Create immutable agreement signature round", request,
		field("case_id", expression("target", "case_id")), field("supplier_id", expression("target", "supplier_id")), field("signer", expression("input", "signer")), field("document_file_id", expression("input", "document")), field("document_revision", expression("input", "document_revision")),
		field("requested_at", literal(must(av.Timestamp(module.FormatTimestamp(requested))))), field("reminder_at", literal(must(av.Timestamp(module.FormatTimestamp(reminder))))), field("expires_at", literal(must(av.Timestamp(module.FormatTimestamp(expiry))))), field("status", literal(text("requested"))), field("delivery_id", literal(av.Null())), field("signature_evidence_file_id", literal(av.Null())))
	input := must(aa.Object(field("request_id", request), field("reminder_at", literal(must(av.Timestamp(module.FormatTimestamp(reminder))))), field("expires_at", literal(must(av.Timestamp(module.FormatTimestamp(expiry)))))))
	started := must(aa.NewStartWorkflowEffect("CollectSignature", "Admit exact agreement signature round", input))
	caseStatus := set("OnboardingCase", "Await exact agreement signature", expression("target", "key"), field("status", literal(text("awaiting_signature"))))
	return output([]aa.Effect{created, caseStatus, started}, refs([2]string{"target", "case_id"}, [2]string{"target", "supplier_id"}, [2]string{"target", "status"}, [2]string{"input", "request_id"}, [2]string{"input", "document"}, [2]string{"input", "document_revision"}, [2]string{"input", "signer"}, [2]string{"input", "requested_at"}, [2]string{"input", "reminder_at"}, [2]string{"input", "expires_at"}), []string{"OnboardingCase", "SignatureRequest"}, []aa.EffectPermission{perm(aa.EffectCreateObject, "SignatureRequest", "case_id", "supplier_id", "signer", "document_file_id", "document_revision", "requested_at", "reminder_at", "expires_at", "status", "delivery_id", "signature_evidence_file_id"), perm(aa.EffectSetProperties, "OnboardingCase", "status"), perm(aa.EffectStartWorkflow, "CollectSignature", "input")})
}

type signatureFacts struct{ Target, Input, Basis map[string]av.Value }

func decodeSignatureFacts(value module.Value) (signatureFacts, error) {
	var out signatureFacts
	raw, err := value.MarshalJSON()
	if err == nil {
		err = json.Unmarshal(raw, &out)
	}
	return out, err
}
func signatureInputTime(f signatureFacts, name string) (module.Timestamp, error) {
	value, ok := f.Input[name]
	if !ok {
		return module.Timestamp{}, fmt.Errorf("signature Action omitted %s", name)
	}
	raw, ok := value.Text()
	if !ok {
		return module.Timestamp{}, fmt.Errorf("signature Action %s is not a timestamp", name)
	}
	instant, err := module.ParseTimestamp(raw)
	return instant.UTC(), err
}
func signatureAAReads(reads []aa.ReadRef) []aa.ReadRef {
	return append(reads, must(aa.NewReadRef(aa.ReadBasis, "basis.time")))
}

func sendSignature(kind string, changeStatus bool, value module.Value) (module.Value, error) {
	f, err := decodeSignatureFacts(value)
	if err != nil {
		return module.Value{}, err
	}
	id, _ := f.Target["request_id"].Text()
	invoke := must(aa.NewEffect(aa.EffectInvoke, "signature_delivery.send_signature_request", "Deliver "+kind+" for signature request "+id, nil, []aa.NamedValueExpr{field("request_id", expression("target", "request_id")), field("signer", expression("target", "signer")), field("document_revision", expression("target", "document_revision")), field("kind", literal(text(kind)))}))
	reads := refs([2]string{"target", "request_id"}, [2]string{"target", "signer"}, [2]string{"target", "document_revision"}, [2]string{"target", "status"})
	effects, permissions := []aa.Effect{invoke}, []aa.EffectPermission{perm(aa.EffectInvoke, "signature_delivery.send_signature_request")}
	deps := []string{"signature_delivery.send_signature_request"}
	if changeStatus {
		effects = append([]aa.Effect{set("SignatureRequest", "Queue the signature request for delivery", expression("target", "key"), field("status", literal(text("sending"))))}, effects...)
		permissions = append(permissions, perm(aa.EffectSetProperties, "SignatureRequest", "status"))
		deps = append(deps, "SignatureRequest")
	}
	return output(effects, reads, deps, permissions)
}
func sendSignatureRequest(c module.Context, v module.Value) (module.Value, error) {
	return sendSignature("request", true, v)
}
func sendSignatureReminder(c module.Context, v module.Value) (module.Value, error) {
	return sendSignature("reminder", false, v)
}

func confirmSignatureDelivery(_ module.Context, value module.Value) (module.Value, error) {
	f, err := decodeSignatureFacts(value)
	if err != nil {
		return module.Value{}, err
	}
	id, _ := f.Target["request_id"].Text()
	confirmed := set("SignatureRequest", "Signature request "+id+" acknowledged by the signature provider", expression("target", "key"), field("status", literal(text("sent"))), field("delivery_id", expression("input", "delivery_id")))
	return output([]aa.Effect{confirmed}, refs([2]string{"target", "request_id"}, [2]string{"target", "status"}, [2]string{"input", "delivery_id"}), []string{"SignatureRequest"}, []aa.EffectPermission{perm(aa.EffectSetProperties, "SignatureRequest", "status", "delivery_id")})
}

func signatureTransition(status, caseStatus, line string, value module.Value) (module.Value, error) {
	f, err := decodeSignatureFacts(value)
	if err != nil {
		return module.Value{}, err
	}
	id, _ := f.Target["request_id"].Text()
	setStatus := set("SignatureRequest", line, expression("target", "key"), field("status", literal(text(status))))
	setCase := set("OnboardingCase", line, expression("target", "case_id"), field("status", literal(text(caseStatus))))
	subject := expression("target", "request_id")
	emit := must(aa.NewEffect(aa.EffectEmit, signatureChanged, "Signature request "+id+" became "+status, &subject, nil))
	return output([]aa.Effect{setStatus, setCase, emit}, refs([2]string{"target", "request_id"}, [2]string{"target", "case_id"}, [2]string{"target", "status"}), []string{"SignatureRequest", "OnboardingCase"}, []aa.EffectPermission{perm(aa.EffectSetProperties, "SignatureRequest", "status"), perm(aa.EffectSetProperties, "OnboardingCase", "status"), perm(aa.EffectEmit, signatureChanged)})
}
func expireSignature(c module.Context, v module.Value) (module.Value, error) {
	return signatureTransition("expired", "signature_expired", "Expire unsigned signature request", v)
}
func cancelSignature(c module.Context, v module.Value) (module.Value, error) {
	return signatureTransition("cancelled", "signature_cancelled", "Cancel signature request", v)
}
func acceptSignature(_ module.Context, value module.Value) (module.Value, error) {
	f, err := decodeSignatureFacts(value)
	if err != nil {
		return module.Value{}, err
	}
	id, _ := f.Target["request_id"].Text()
	accepted := set("SignatureRequest", "Accept signature evidence", expression("target", "key"), field("status", literal(text("signed"))), field("signature_evidence_file_id", expression("input", "signature_evidence")))
	subject := expression("target", "request_id")
	emit := must(aa.NewEffect(aa.EffectEmit, signatureChanged, "Signature request "+id+" received accepted evidence", &subject, nil))
	return output([]aa.Effect{accepted, emit}, signatureAAReads(refs([2]string{"target", "request_id"}, [2]string{"target", "status"}, [2]string{"target", "document_revision"}, [2]string{"target", "expires_at"}, [2]string{"input", "document_revision"}, [2]string{"input", "signature_evidence"})), []string{"SignatureRequest"}, []aa.EffectPermission{perm(aa.EffectSetProperties, "SignatureRequest", "status", "signature_evidence_file_id"), perm(aa.EffectEmit, signatureChanged)})
}
func activateSignedAgreement(_ module.Context, _ module.Value) (module.Value, error) {
	request := set("SignatureRequest", "Activate accepted signature", expression("target", "key"), field("status", literal(text("activated"))))
	caseEffect := set("OnboardingCase", "Complete signed onboarding agreement", expression("target", "case_id"), field("status", literal(text("complete"))))
	supplier := set("Supplier", "Activate supplier from signed agreement", expression("target", "supplier_id"), field("is_active", literal(av.Bool(true))))
	reads := refs([2]string{"target", "request_id"}, [2]string{"target", "case_id"}, [2]string{"target", "supplier_id"}, [2]string{"target", "status"}, [2]string{"target", "document_revision"}, [2]string{"target", "signature_evidence_file_id"}, [2]string{"input", "document_revision"}, [2]string{"input", "signature_evidence"})
	return output([]aa.Effect{request, caseEffect, supplier}, reads, []string{"SignatureRequest", "OnboardingCase", "Supplier"}, []aa.EffectPermission{perm(aa.EffectSetProperties, "SignatureRequest", "status"), perm(aa.EffectSetProperties, "OnboardingCase", "status"), perm(aa.EffectSetProperties, "Supplier", "is_active")})
}

func collectSignatureWorkflow() workflow.Spec {
	requestID := must(workflow.NewInput("request_id", av.TypeString, false, ""))
	reminder := must(workflow.NewInput("reminder_at", av.TypeTimestamp, false, ""))
	expires := must(workflow.NewInput("expires_at", av.TypeTimestamp, false, ""))
	active := must(workflow.Reference("input.request_id"))
	resultID := must(workflow.NewInput("request_id", av.TypeString, false, ""))
	code := must(workflow.NewCode(workflow.CodeOptions{Module: "collect_signature", Entrypoint: "run", Functions: []string{"signature_request_state", "signature_request_terminal"}, Actions: []string{"SendSignatureRequest", "ConfirmSignatureDelivery", "SendSignatureReminder", "ActivateSignedAgreement", "ExpireSignatureRequest"}, Signals: []string{signatureChanged}, Results: []workflow.Result{
		must(workflow.NewResult("signed", "Agreement signature accepted and activated", workflow.DispositionResolved, []workflow.Input{resultID})), must(workflow.NewResult("expired", "Agreement signature window expired", workflow.DispositionAttention, []workflow.Input{resultID})), must(workflow.NewResult("cancelled", "Agreement signature request cancelled", workflow.DispositionNoAction, []workflow.Input{resultID})),
	}, MaxOperations: 12, MaxHistoryBytes: 262144, MaxReplayFuel: 100000000}))
	return must(workflow.NewSpec("CollectSignature", workflow.SpecOptions{Label: "Collect agreement signature", Description: "Send and remind for one immutable document revision, then activate, expire, or observe cancellation through ordinary Actions.", Inputs: []workflow.Input{requestID, reminder, expires}, ActiveKey: &active, Code: &code, MaxDuration: "1128h", MaxFunctionRuns: 8}))
}
