package warehousedeposit

import (
	"context"
	"time"

	"github.com/shopspring/decimal"
)

// WarehouseDeposit is received by a Kasir Pusat (head-office cashier) working
// directly in this ERP — not a store/outlet POS till, which is a separate
// app with its own shift/drawer concept (internal/cashier). Receiving a
// deposit here is a plain custody confirmation (received_by/received_at):
// the cash was already recognized in the books the moment the underlying
// payment was recorded (finance.Service.recordInvoicePayment posts the
// income cashflow then), so this step never touches cashflows/drawers.
type WarehouseDeposit struct {
	ID             int64           `json:"id"`
	DepositNumber  string          `json:"deposit_number"`
	WarehouseID    int64           `json:"warehouse_id"`
	WarehouseName  string          `json:"warehouse_name,omitempty"`
	DriverID       *int64          `json:"driver_id,omitempty"`
	DriverName     string          `json:"driver_name"`
	Amount         decimal.Decimal `json:"amount"`
	DepositDate    string          `json:"deposit_date"`
	Notes          string          `json:"notes"`
	Status         string          `json:"status"` // pending, received, cancelled
	CreatedBy      *int64          `json:"created_by,omitempty"`
	CreatedByName  string          `json:"created_by_name,omitempty"`
	ReceivedBy     *int64          `json:"received_by,omitempty"`
	ReceivedByName string          `json:"received_by_name,omitempty"`
	ReceivedAt     *time.Time      `json:"received_at,omitempty"`
	CreatedAt      time.Time       `json:"created_at"`
	UpdatedAt      time.Time       `json:"updated_at"`
}

// CreateInput no longer carries a manually-typed Amount: a Setoran is built
// by picking which not-yet-deposited cash/giro payments (PaymentIDs) are
// physically being handed over right now — the amount is always the sum of
// those payments, computed server-side, so it can never drift from what was
// actually recorded as paid.
type CreateInput struct {
	WarehouseID int64   `json:"warehouse_id"`
	DriverID    *int64  `json:"driver_id,omitempty"` // nil for a direct/ad-hoc deposit (e.g. Sales handing over cash received without a driver run)
	PaymentIDs  []int64 `json:"payment_ids"`
	DepositDate string  `json:"deposit_date"`
	Notes       string  `json:"notes"`
}

type Filter struct {
	Page        int
	PerPage     int
	WarehouseID int64
	Status      string
	DateFrom    string
	DateTo      string
	Search      string
}

// UndepositedPayment is one cash/giro payment not yet picked up by a Setoran
// — a candidate row in the "Catat Setoran" checklist.
type UndepositedPayment struct {
	PaymentID     int64           `json:"payment_id"`
	PaymentNumber string          `json:"payment_number"`
	Amount        decimal.Decimal `json:"amount"`
	PaymentMethod string          `json:"payment_method"`
	PaymentDate   string          `json:"payment_date"`
	InvoiceID     int64           `json:"invoice_id"`
	InvoiceNumber string          `json:"invoice_number"`
	CustomerName  string          `json:"customer_name"`
}

// UndepositedFilter narrows ListUndepositedPayments: DriverID+Date scopes to
// one driver's dispatched DOs (the Warehouse "from driver" mode), Search
// matches customer name / invoice number for the ad-hoc "direct payment"
// mode. Both may be empty for an unfiltered list.
type UndepositedFilter struct {
	DriverID *int64
	Date     string
	Search   string
}

type Store interface {
	// CreateWithPayments inserts d and atomically links every id in
	// paymentIDs to it (payments.deposit_id), after verifying each one is a
	// cash/giro payment not already claimed by another Setoran. d.Amount is
	// set to the sum of those payments' amounts.
	CreateWithPayments(ctx context.Context, d *WarehouseDeposit, paymentIDs []int64) error
	GetByID(ctx context.Context, id int64) (*WarehouseDeposit, error)
	GetByIDForUpdate(ctx context.Context, id int64) (*WarehouseDeposit, error)
	List(ctx context.Context, f Filter) ([]WarehouseDeposit, int, error)
	ListUndepositedPayments(ctx context.Context, f UndepositedFilter) ([]UndepositedPayment, error)
	UpdateReceived(ctx context.Context, id, receivedBy int64, receivedAt time.Time) error
	// DriverName resolves a driver's display name from the driver master data
	// (users.application_access='driver') so it can be snapshotted onto the
	// deposit row instead of trusting free-text client input.
	DriverName(ctx context.Context, driverID int64) (string, error)
}
