// fees/v1/fees.proto
syntax = "proto3";
package fees.v1;

option go_package = "github.com/tech/sendico/pkg/proto/billing/fees/v1;feesv1";

import "google/protobuf/timestamp.proto";
import "api/proto/common/money/v1/money.proto";
import "api/proto/common/fx/v1/fx.proto";
import "api/proto/common/accounting/v1/posting.proto";
import "api/proto/common/trace/v1/trace.proto";

// --------------------
// Core business enums
// --------------------
enum Trigger {
  TRIGGER_UNSPECIFIED   = 0;
  TRIGGER_CAPTURE       = 1;
  TRIGGER_REFUND        = 2;
  TRIGGER_DISPUTE       = 3;
  TRIGGER_PAYOUT        = 4;
  TRIGGER_FX_CONVERSION = 5;
}

// What to do if net-payable is insufficient to collect fees now.
enum InsufficientNetPolicy {
  INSUFFICIENT_NET_UNSPECIFIED = 0;
  BLOCK_POSTING                = 1; // fail the request
  SWEEP_ORG_CASH               = 2; // charge an org cash account
  INVOICE_LATER                = 3; // return zero lines; AR later
}

// Optional per-call overrides (rare).
message PolicyOverrides {
  InsufficientNetPolicy insufficient_net = 1;
}

// --------------------
// Request/response meta
// --------------------
message RequestMeta {
  string organization_ref = 1; // org scope; tenant resolved internally
  common.trace.v1.TraceContext trace = 2;
}

// ResponseMeta carries tracing context for fee engine responses.
message ResponseMeta {
  common.trace.v1.TraceContext trace = 1;
}

// --------------------
// Intent & outputs
// --------------------

// What the ledger/PO intends to do; used to select plan rules.
message Intent {
  Trigger trigger                     = 1;
  common.money.v1.Money base_amount   = 2; // fee base (e.g., captured gross)
  google.protobuf.Timestamp booked_at = 3; // for effective-dated plan
  string origin_type                  = 4; // e.g., "charge.capture"
  string origin_ref                   = 5; // gateway or business id
  map<string,string> attributes       = 6; // e.g., mcc, card_present, country
}

// FX details actually used during fee calc (if any).
message FXUsed {
  common.fx.v1.CurrencyPair pair = 1;
  common.fx.v1.Side side         = 2;
  common.money.v1.Decimal rate   = 3; // applied rate
  int64 asof_unix_ms             = 4; // source timestamp (ms)
  string provider                = 5; // e.g., "ECB", "XE"
  string rate_ref                = 6; // provider ref/id
  common.money.v1.Decimal spread_bps = 7; // applied spread
}

// A derived posting line ready for the ledger to post as-is.
message DerivedPostingLine {
  string  ledger_account_ref = 1; // resolved account
  common.money.v1.Money money = 2; // amount/currency
  common.accounting.v1.PostingLineType line_type = 3; // FEE/TAX/SPREAD/REVERSAL
  common.accounting.v1.EntrySide       side      = 4; // DEBIT/CREDIT
  map<string,string> meta = 5; // fee_rule_id, rule_version, tax_code, tax_rate, fx_rate_used, etc.
}

// Snapshot of rules applied for audit/replay.
message AppliedRule {
  string rule_id       = 1;
  string rule_version  = 2;
  string formula       = 3; // e.g., "2.90% + 0.30 (min 0.50)"
  common.money.v1.RoundingMode rounding = 4;
  string tax_code      = 5; // if applicable
  string tax_rate      = 6; // decimal string
  map<string,string> parameters = 7; // thresholds, tiers, etc.
}

// --------------------
// RPC: synchronous quote for posting
// --------------------
message QuoteFeesRequest {
  RequestMeta     meta   = 1;
  Intent          intent = 2;
  PolicyOverrides policy = 3;
}

// QuoteFeesResponse returns derived fee lines and the rules that produced them.
message QuoteFeesResponse {
  ResponseMeta meta                  = 1;
  repeated DerivedPostingLine lines  = 2; // derived fee/tax/spread lines
  repeated AppliedRule       applied = 3; // rules snapshot
  FXUsed fx_used                     = 4; // optional if FX participated
}

// --------------------
// RPC: pre-pricing (UI/PO) with signed token
// --------------------
message PrecomputeFeesRequest {
  RequestMeta               meta   = 1;
  Intent                    intent = 2;
  int64                     ttl_ms = 3; // token validity window
}

// PrecomputeFeesResponse returns a signed fee token and optional preview lines.
message PrecomputeFeesResponse {
  ResponseMeta              meta           = 1;
  string                    fee_quote_token = 2; // opaque, signed
  google.protobuf.Timestamp expires_at     = 3;
  // Optional preview so UI can render exact numbers now:
  repeated DerivedPostingLine lines  = 4;
  repeated AppliedRule       applied = 5;
  FXUsed fx_used                     = 6;
}

// --------------------
// RPC: validate/decode a token before posting
// --------------------
message ValidateFeeTokenRequest {
  RequestMeta meta            = 1;
  string      fee_quote_token = 2;
}

// ValidateFeeTokenResponse returns the validation result and embedded fee data.
message ValidateFeeTokenResponse {
  ResponseMeta meta           = 1;
  bool  valid                 = 2;
  string reason               = 3; // if invalid
  // If valid, return normalized content embedded in the token:
  Intent                    intent  = 4;
  repeated DerivedPostingLine lines = 5;
  repeated AppliedRule       applied = 6;
  FXUsed fx_used                    = 7;
}

// --------------------
// Service
// --------------------
service FeeEngine {
  // Compute derived fee/tax/spread lines for immediate posting.
  rpc QuoteFees        (QuoteFeesRequest)        returns (QuoteFeesResponse);

  // Pre-price for UX/PO and return a signed token to post later.
  rpc PrecomputeFees   (PrecomputeFeesRequest)   returns (PrecomputeFeesResponse);

  // Verify/expand a token just before posting (prevents policy drift).
  rpc ValidateFeeToken (ValidateFeeTokenRequest) returns (ValidateFeeTokenResponse);
}
