syntax = "proto3";

package ledger.v1;

option go_package = "github.com/tech/sendico/pkg/proto/ledger/v1;ledgerv1";

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "api/proto/common/describable/v1/describable.proto";
import "api/proto/common/money/v1/money.proto";

// ===== Enums =====

// EntryType classifies the kind of journal entry.
enum EntryType {
  // ENTRY_TYPE_UNSPECIFIED is the default zero value.
  ENTRY_TYPE_UNSPECIFIED = 0;
  // ENTRY_CREDIT records an inbound credit.
  ENTRY_CREDIT   = 1;
  // ENTRY_DEBIT records an outbound debit.
  ENTRY_DEBIT    = 2;
  // ENTRY_TRANSFER records a transfer between accounts.
  ENTRY_TRANSFER = 3;
  // ENTRY_FX records a foreign-exchange conversion.
  ENTRY_FX       = 4;
  // ENTRY_FEE records a fee charge.
  ENTRY_FEE      = 5;
  // ENTRY_ADJUST records a manual adjustment.
  ENTRY_ADJUST   = 6;
  // ENTRY_REVERSE records a reversal of a prior entry.
  ENTRY_REVERSE  = 7;
}

// LineType classifies the purpose of a posting line within an entry.
enum LineType {
  // LINE_TYPE_UNSPECIFIED is the default zero value.
  LINE_TYPE_UNSPECIFIED = 0;
  // LINE_MAIN is the primary posting line.
  LINE_MAIN     = 1;
  // LINE_FEE is a fee posting line.
  LINE_FEE      = 2;
  // LINE_SPREAD is an FX spread posting line.
  LINE_SPREAD   = 3;
  // LINE_REVERSAL is a reversal posting line.
  LINE_REVERSAL = 4;
}

// AccountType classifies the fundamental accounting type of an account.
enum AccountType {
  // ACCOUNT_TYPE_UNSPECIFIED is the default zero value.
  ACCOUNT_TYPE_UNSPECIFIED = 0;
  // ACCOUNT_TYPE_ASSET represents an asset account.
  ACCOUNT_TYPE_ASSET       = 1;
  // ACCOUNT_TYPE_LIABILITY represents a liability account.
  ACCOUNT_TYPE_LIABILITY   = 2;
  // ACCOUNT_TYPE_REVENUE represents a revenue account.
  ACCOUNT_TYPE_REVENUE     = 3;
  // ACCOUNT_TYPE_EXPENSE represents an expense account.
  ACCOUNT_TYPE_EXPENSE     = 4;
}

// AccountStatus indicates whether an account is active or frozen.
enum AccountStatus {
  // ACCOUNT_STATUS_UNSPECIFIED is the default zero value.
  ACCOUNT_STATUS_UNSPECIFIED = 0;
  // ACCOUNT_STATUS_ACTIVE means the account accepts postings.
  ACCOUNT_STATUS_ACTIVE      = 1;
  // ACCOUNT_STATUS_FROZEN means the account is blocked from new postings.
  ACCOUNT_STATUS_FROZEN      = 2;
}

// AccountRole defines the functional role of an account within an organization.
enum AccountRole {
  // ACCOUNT_ROLE_UNSPECIFIED is the default zero value.
  ACCOUNT_ROLE_UNSPECIFIED = 0;
  // ACCOUNT_ROLE_OPERATING is the main operating account.
  ACCOUNT_ROLE_OPERATING   = 1;
  // ACCOUNT_ROLE_HOLD is a temporary hold account.
  ACCOUNT_ROLE_HOLD        = 2;
  // ACCOUNT_ROLE_TRANSIT is an in-transit account.
  ACCOUNT_ROLE_TRANSIT     = 3;
  // ACCOUNT_ROLE_SETTLEMENT is a settlement account.
  ACCOUNT_ROLE_SETTLEMENT  = 4;
  // ACCOUNT_ROLE_CLEARING is a clearing account.
  ACCOUNT_ROLE_CLEARING    = 5;
  // ACCOUNT_ROLE_PENDING is a pending-settlement account.
  ACCOUNT_ROLE_PENDING     = 6;
  // ACCOUNT_ROLE_RESERVE is a reserve account.
  ACCOUNT_ROLE_RESERVE     = 7;
  // ACCOUNT_ROLE_LIQUIDITY is a liquidity pool account.
  ACCOUNT_ROLE_LIQUIDITY   = 8;
  // ACCOUNT_ROLE_FEE is a fee collection account.
  ACCOUNT_ROLE_FEE         = 9;
  // ACCOUNT_ROLE_CHARGEBACK is a chargeback account.
  ACCOUNT_ROLE_CHARGEBACK  = 10;
  // ACCOUNT_ROLE_ADJUSTMENT is an adjustment account.
  ACCOUNT_ROLE_ADJUSTMENT  = 11;
}

// LedgerAccount captures the canonical representation of an account resource.
message LedgerAccount {
  string ledger_account_ref          = 1;
  string organization_ref            = 2;
  string account_code                = 3;
  AccountType account_type           = 4;
  string currency                    = 5;
  AccountStatus status               = 6;
  bool allow_negative                = 7;
  reserved 8;
  reserved "is_settlement";
  map<string, string> metadata       = 9;
  google.protobuf.Timestamp created_at = 10;
  google.protobuf.Timestamp updated_at = 11;
  common.describable.v1.Describable describable = 12;
  string owner_ref            = 13;
  AccountRole role            = 14;
}

// A single posting line (mirrors your PostingLine model)
message PostingLine {
  string ledger_account_ref   = 1;
  common.money.v1.Money money = 2;
  LineType line_type          = 3; // MAIN, FEE, SPREAD, ...
}

// ===== Requests/Responses =====

// CreateAccountRequest is the request to create a new ledger account.
message CreateAccountRequest {
  string organization_ref = 1;
  string owner_ref        = 2;
  reserved 3;
  reserved "account_code";
  AccountType account_type = 4;
  string currency         = 5;
  AccountStatus status    = 6;
  bool allow_negative     = 7;
  reserved 8;
  reserved "is_settlement";
  map<string, string> metadata = 9;
  common.describable.v1.Describable describable = 10;
  AccountRole role = 11;
}

// CreateAccountResponse is the response for CreateAccount.
message CreateAccountResponse {
  LedgerAccount account = 1;
}

// Common: optional event_time lets caller set business time; server may default to now.
message PostCreditRequest {
  string idempotency_key           = 1;
  string organization_ref          = 2; // aligns with PermissionBound
  string ledger_account_ref        = 3;
  common.money.v1.Money  money     = 4;
  string description               = 5;
  repeated PostingLine charges     = 6; // FEE/SPREAD lines (no MAIN here)
  map<string, string> metadata     = 7;
  google.protobuf.Timestamp event_time = 8;
  string contra_ledger_account_ref = 9; // optional override for settlement/contra account
  AccountRole role                 = 10; // optional: assert target account has this role
}

// PostDebitRequest is the request to post a debit entry.
message PostDebitRequest {
  string idempotency_key           = 1;
  string organization_ref          = 2;
  string ledger_account_ref        = 3;
  common.money.v1.Money  money     = 4;
  string description               = 5;
  repeated PostingLine charges     = 6; // FEE/SPREAD
  map<string, string> metadata     = 7;
  google.protobuf.Timestamp event_time = 8;
  string contra_ledger_account_ref = 9; // optional override for settlement/contra account
  AccountRole role                 = 10; // optional: assert target account has this role
}

// TransferRequest is the request to transfer funds between two ledger accounts.
message TransferRequest {
  string idempotency_key           = 1;
  string organization_ref          = 2;
  string from_ledger_account_ref   = 3;
  string to_ledger_account_ref     = 4;
  common.money.v1.Money  money     = 5; // transfer amount/currency
  string description               = 6;
  repeated PostingLine charges     = 7; // optional FEE/SPREAD lines
  map<string, string> metadata     = 8;
  google.protobuf.Timestamp event_time = 9;
  AccountRole from_role = 10;
  AccountRole to_role = 11;
}

// FXRequest is the request to post a foreign-exchange conversion entry.
message FXRequest {
  string idempotency_key           = 1;
  string organization_ref          = 2;
  string from_ledger_account_ref   = 3;
  string to_ledger_account_ref     = 4;

  common.money.v1.Money  from_money = 5; // debited
  common.money.v1.Money  to_money   = 6; // credited
  string rate                      = 7; // quoted rate as string (snapshot for audit)

  string description               = 8;
  repeated PostingLine charges     = 9; // FEE/SPREAD lines
  map<string, string> metadata     = 10;
  google.protobuf.Timestamp event_time = 11;
}

// PostResponse is the common response returned after any posting operation.
message PostResponse {
  string journal_entry_ref = 1;
  int64  version           = 2; // ledger's entry version (monotonic per scope)
  EntryType entry_type     = 3;
}

// ---- Balances & Entries ----

// GetBalanceRequest is the request to retrieve an account balance.
message GetBalanceRequest {
  string ledger_account_ref = 1;
}

// BalanceResponse holds the current balance of a ledger account.
message BalanceResponse {
  string ledger_account_ref          = 1;
  common.money.v1.Money  balance     = 2;
  int64  version                     = 3;
  google.protobuf.Timestamp last_updated = 4;
}

// GetEntryRequest is the request to retrieve a journal entry by reference.
message GetEntryRequest {
  string entry_ref = 1;
}

// JournalEntryResponse represents a complete journal entry with all posting lines.
message JournalEntryResponse {
  string entry_ref                       = 1;
  string idempotency_key                 = 2;
  EntryType entry_type                   = 3;
  string description                     = 4;
  google.protobuf.Timestamp event_time   = 5;
  int64  version                         = 6;
  repeated PostingLine lines             = 7;
  map<string, string> metadata           = 8;
  repeated string ledger_account_refs    = 9; // denormalized set for client-side filtering
}

// GetStatementRequest is the request to retrieve paginated journal entries for an account.
message GetStatementRequest {
  string ledger_account_ref = 1;
  string cursor             = 2; // opaque
  int32  limit              = 3; // page size
}

// StatementResponse is a paginated list of journal entries.
message StatementResponse {
  repeated JournalEntryResponse entries = 1;
  string next_cursor                    = 2;
}

// ListAccountsRequest is the request to list ledger accounts with optional filters.
message ListAccountsRequest {
  string organization_ref = 1;
  // Optional owner filter with 3-state semantics:
  // - not set: return all accounts within organization
  // - set to empty string: return accounts where owner_ref is null/empty
  // - set to a value: return accounts where owner_ref matches
  google.protobuf.StringValue owner_ref_filter = 2;
}

// ListAccountsResponse is the response for ListAccounts.
message ListAccountsResponse {
  repeated LedgerAccount accounts = 1;
}

// ---- Account status mutations ----

// BlockAccountRequest is the request to freeze (block) a ledger account.
message BlockAccountRequest {
  string ledger_account_ref = 1;
  string organization_ref   = 2;
  AccountRole role           = 3; // optional: assert account has this role before blocking
}

// BlockAccountResponse is the response for BlockAccount.
message BlockAccountResponse {
  LedgerAccount account = 1;
}

// UnblockAccountRequest is the request to unfreeze (unblock) a ledger account.
message UnblockAccountRequest {
  string ledger_account_ref = 1;
  string organization_ref   = 2;
  AccountRole role           = 3; // optional: assert account has this role before unblocking
}

// UnblockAccountResponse is the response for UnblockAccount.
message UnblockAccountResponse {
  LedgerAccount account = 1;
}
