import { sha256Json } from "../crypto";
import { createActionReceiptV04, type ActionReceiptV04 } from "../receipt";
import type { RoleSigners } from "../runtime";
import type { JsonObject } from "../types";
import {
  COUNTERPARTY_ID,
  EFFECT_ARGUMENTS,
  EFFECT_CLASS,
  EFFECT_LOCK_PROFILE,
  EFFECT_TARGET,
  GRANT_ID,
  MANDATE_ID,
  RECEIVER_ID,
  RECEIVER_NONCE,
  REDEMPTION_PROFILE,
  RUN_ID,
  SEMANTIC_EPOCH,
  VALID_FROM,
  VALID_UNTIL,
} from "./constants";
import { evidenceBundleRoot } from "./kernel";
import type { AcceptedEffectLockContext, RedemptionWire } from "./types";

const GRANT_EVENT_ID = "01500000-0000-4000-8000-000000000001";
const MANDATE_EVENT_ID = "01500000-0000-4000-8000-000000000002";
const DECISION_EVENT_ID = "01500000-0000-4000-8000-000000000003";

export async function createFixtureGrant(input: {
  signers: RoleSigners;
  context: AcceptedEffectLockContext;
}): Promise<ActionReceiptV04> {
  return createActionReceiptV04({
    actionType: "effect.grant.issue",
    subject: {
      profile: EFFECT_LOCK_PROFILE,
      grant_id: GRANT_ID,
      run_id: RUN_ID,
      receiver_id: RECEIVER_ID,
      counterparty_id: COUNTERPARTY_ID,
      target: EFFECT_TARGET,
      effect_class: EFFECT_CLASS,
      arguments_hash: await sha256Json(EFFECT_ARGUMENTS),
      receiver_nonce: RECEIVER_NONCE,
      evidence_policy_hash: input.context.evidence_policy_hash,
      verifier_hash: input.context.verifier_hash,
      runtime_graph_root: input.context.runtime_graph_root,
      role_set_hash: input.context.role_set_hash,
      semantic_epoch: SEMANTIC_EPOCH,
      authority_epoch: input.context.authority_epoch,
      valid_from: VALID_FROM,
      valid_until: VALID_UNTIL,
      use_limit: 1,
      issuer_role: "target",
    },
    eventId: GRANT_EVENT_ID,
    claimedAt: VALID_FROM,
    evidence: [],
    signer: input.signers.target,
    issuerRole: "target",
  });
}

export async function createFixtureRedemption(input: {
  grant: ActionReceiptV04;
  signers: RoleSigners;
  context: AcceptedEffectLockContext;
  decisionEventId?: string;
}): Promise<RedemptionWire> {
  const argumentsHash = await sha256Json(EFFECT_ARGUMENTS);
  const request: JsonObject = {
    subject: { id: COUNTERPARTY_ID },
    action: { name: EFFECT_CLASS },
    resource: { id: EFFECT_TARGET, type: "receiver-state" },
    context: {
      arguments_hash: argumentsHash,
      receiver_nonce: RECEIVER_NONCE,
      run_id: RUN_ID,
    },
  };
  const requestHash = await sha256Json(request);
  const response: JsonObject = {
    decision: true,
    context: {
      authority_epoch: input.context.authority_epoch,
      policy_hash: input.context.evidence_policy_hash,
      request_hash: requestHash,
    },
  };
  const responseHash = await sha256Json(response);
  const mandate = await createActionReceiptV04({
    actionType: "eval.run.authorize",
    subject: {
      profile: EFFECT_LOCK_PROFILE,
      mandate_id: MANDATE_ID,
      run_id: RUN_ID,
      receiver_id: RECEIVER_ID,
      counterparty_id: COUNTERPARTY_ID,
      target: EFFECT_TARGET,
      effect_class: EFFECT_CLASS,
      arguments_hash: argumentsHash,
      valid_from: VALID_FROM,
      valid_until: VALID_UNTIL,
      issuer_role: "authorization",
    },
    eventId: MANDATE_EVENT_ID,
    claimedAt: VALID_FROM,
    evidence: [],
    signer: input.signers.authorization,
    issuerRole: "authorization",
  });
  const decision = await createActionReceiptV04({
    actionType: "boundary.decision",
    subject: {
      profile: EFFECT_LOCK_PROFILE,
      decision: "PERMIT",
      run_id: RUN_ID,
      receiver_id: RECEIVER_ID,
      counterparty_id: COUNTERPARTY_ID,
      target: EFFECT_TARGET,
      effect_class: EFFECT_CLASS,
      arguments_hash: argumentsHash,
      receiver_nonce: RECEIVER_NONCE,
      request_hash: requestHash,
      response_hash: responseHash,
      evidence_policy_hash: input.context.evidence_policy_hash,
      semantic_epoch: input.context.semantic_epoch,
      authority_epoch: input.context.authority_epoch,
      issuer_role: "boundary",
    },
    eventId: input.decisionEventId ?? DECISION_EVENT_ID,
    claimedAt: VALID_FROM,
    evidence: [
      {
        name: "mandate",
        hash: mandate.hashes.attestation,
        grounding: "counterparty_signed",
      },
      {
        name: "authzen-request",
        hash: requestHash,
        grounding: "self_asserted",
      },
      {
        name: "authzen-response",
        hash: responseHash,
        grounding: "self_asserted",
      },
    ],
    signer: input.signers.boundary,
    issuerRole: "boundary",
  });
  const root = await evidenceBundleRoot({
    grant: input.grant,
    mandate,
    boundaryDecision: decision,
    authzenRequest: request,
    authzenResponse: response,
    effectArguments: EFFECT_ARGUMENTS,
    receiverNonce: RECEIVER_NONCE,
  });
  return {
    profile: REDEMPTION_PROFILE,
    grant: input.grant as unknown as JsonObject,
    mandate: mandate as unknown as JsonObject,
    boundary_decision: decision as unknown as JsonObject,
    authzen_request: request,
    authzen_response: response,
    effect_arguments: EFFECT_ARGUMENTS,
    receiver_nonce: RECEIVER_NONCE,
    evidence_bundle_root: root,
  } as RedemptionWire;
}
