package models

import "time"

// Counter sales, stored at the fidelity the till actually rang them.
//
// Separate from `orders` on purpose. An app order and a counter bill are
// different documents: a bill carries a cashier, a terminal, a rounding
// adjustment, promo campaigns, loyalty movement and a payment split across
// several tenders, none of which `orders` has anywhere to put. Forcing one into
// the other's shape loses whichever fields do not fit, and the loss is silent.
//
// The cost of the split is that existing revenue queries do not see these rows
// until they are extended to union them in — done in orderRepository's summary
// queries, and the thing to remember when adding a new report.
//
// Stock is *not* separate: a counter sale writes the same productstocks "out"
// rows an app order does, through the same helper. Two stock ledgers would mean
// the catalogue pull sends a till figures that ignore its own sales.

// PosOrders is one counter bill.
type PosOrders struct {
	Posorderid int `json:"posorderid" gorm:"primaryKey;autoIncrement;column:posorderid"`

	// The UUID minted at the till. Globally unique by construction and the only
	// thing that identifies this bill, so it carries a unique index: delivery is
	// at-least-once and the same bill legitimately arrives more than once.
	Terminalorderid string `json:"terminalorderid" gorm:"column:terminalorderid;uniqueIndex;not null"`

	// Human-facing, and unique only per terminal — a till that was replaced
	// restarts its own series, so gaps are normal and duplicates across
	// terminals are expected.
	Invoicenumber string `json:"invoicenumber" gorm:"column:invoicenumber;index"`

	Tenantid   int `json:"tenantid" gorm:"column:tenantid;index"`
	Locationid int `json:"locationid" gorm:"column:locationid;index"`

	// Which physical till, e.g. "T4A9". Free text: nothing keys on it, but a
	// support call starts with it.
	Terminalid  string `json:"terminalid" gorm:"column:terminalid;index"`
	Cashiername string `json:"cashiername" gorm:"column:cashiername"`

	// Resolved against the customers table. Zero for a walk-in.
	Customerid     int    `json:"customerid" gorm:"column:customerid;index"`
	Customermobile string `json:"customermobile" gorm:"column:customermobile"`
	Customername   string `json:"customername" gorm:"column:customername"`

	// When the sale was rung, not when it reached us — a till that was offline
	// for a day uploads bills whose Billedat is yesterday, and every daily
	// figure must use this rather than Receivedat.
	Billedat time.Time `json:"billedat" gorm:"column:billedat;index"`

	// YYYY-MM-DD of Billedat, denormalised so a day's takings are one indexed
	// equality match rather than a range scan with timezone arithmetic.
	Businessdate string `json:"businessdate" gorm:"column:businessdate;index"`

	Subtotal  float64 `json:"subtotal" gorm:"column:subtotal"`
	Discount  float64 `json:"discount" gorm:"column:discount"`
	Taxamount float64 `json:"taxamount" gorm:"column:taxamount"`

	// The paise adjustment printed on the bill. Kept because total is not
	// derivable from the other columns without it.
	Roundoff float64 `json:"roundoff" gorm:"column:roundoff"`

	// What the shopper actually paid. The figure every revenue report sums.
	Total float64 `json:"total" gorm:"column:total"`

	Pointsearned   int `json:"pointsearned" gorm:"column:pointsearned"`
	Pointsredeemed int `json:"pointsredeemed" gorm:"column:pointsredeemed"`

	Itemcount int `json:"itemcount" gorm:"column:itemcount"`

	// The largest tender, for the common "how did they pay" grouping.
	Paymentmode string `json:"paymentmode" gorm:"column:paymentmode;index"`

	// The full split, verbatim. A bill can be part cash, part card, part
	// loyalty, and collapsing that to one mode would lose the reconciliation a
	// cashier settles their drawer against.
	Paymentsjson string `json:"paymentsjson" gorm:"column:paymentsjson;type:jsonb"`

	// Campaigns that fired, stored as amounts rather than rules — a bill read
	// back years later must show what was given, not what today's rules give.
	Promosjson string `json:"promosjson" gorm:"column:promosjson;type:jsonb"`

	// GST per slab, as printed on the tax invoice.
	Taxbreakdownjson string `json:"taxbreakdownjson" gorm:"column:taxbreakdownjson;type:jsonb"`

	// Which upload carried this bill, and when it landed. Kept for tracing a
	// terminal's complaint back to a specific batch.
	Batchid    string    `json:"batchid" gorm:"column:batchid;index"`
	Receivedat time.Time `json:"receivedat" gorm:"column:receivedat"`

	Created time.Time `json:"created" gorm:"column:created;autoCreateTime"`
	Updated time.Time `json:"updated" gorm:"column:updated;autoUpdateTime"`

	Items []PosOrderItems `json:"items" gorm:"-"`
}

func (PosOrders) TableName() string {
	return "pos_orders"
}

// PosOrderItems is one line of a counter bill.
type PosOrderItems struct {
	Posorderitemid int `json:"posorderitemid" gorm:"primaryKey;autoIncrement;column:posorderitemid"`
	Posorderid     int `json:"posorderid" gorm:"column:posorderid;index"`

	Tenantid   int `json:"tenantid" gorm:"column:tenantid;index"`
	Locationid int `json:"locationid" gorm:"column:locationid;index"`
	Productid  int `json:"productid" gorm:"column:productid;index"`

	// Snapshotted rather than joined. A product renamed or withdrawn next month
	// must not change what a bill from today says it sold.
	Productname string `json:"productname" gorm:"column:productname"`
	Barcode     string `json:"barcode" gorm:"column:barcode"`
	Unitname    string `json:"unitname" gorm:"column:unitname"`

	// Fractional: a counter sells 1.5 kg of onions. Note that productstocks
	// cannot represent that — see roundStockQty.
	Quantity float64 `json:"quantity" gorm:"column:quantity"`

	Unitprice      float64 `json:"unitprice" gorm:"column:unitprice"`
	Discountamount float64 `json:"discountamount" gorm:"column:discountamount"`

	// Stored as a fraction (0.18), matching how the till holds it.
	Gstrate   float64 `json:"gstrate" gorm:"column:gstrate"`
	Taxamount float64 `json:"taxamount" gorm:"column:taxamount"`

	// What this line contributed to the bill total, after its share of every
	// discount. The lines sum to the bill's Total less Roundoff.
	Linetotal float64 `json:"linetotal" gorm:"column:linetotal"`

	Created time.Time `json:"created" gorm:"column:created;autoCreateTime"`
}

func (PosOrderItems) TableName() string {
	return "pos_order_items"
}

// PosSalesFilter scopes a query over counter sales.
//
// Locationid is required and is the authorisation boundary — every read is
// scoped to one outlet, so a caller cannot page through another shop's takings
// by omitting a parameter.
type PosSalesFilter struct {
	Locationid  int
	Fromdate    string // YYYY-MM-DD, matched against businessdate
	Todate      string
	Terminalid  string
	Cashiername string
	Paymentmode string
	Pageno      int
	Pagesize    int
}

// PosSalesPage is one page of bills, with the total so a caller can paginate
// without a second request.
type PosSalesPage struct {
	Total    int64       `json:"total"`
	Pageno   int         `json:"pageno"`
	Pagesize int         `json:"pagesize"`
	Bills    []PosOrders `json:"bills"`
}

// PosSalesSummary totals a range of counter sales.
//
// Deliberately separate from the bill list: a shop settling a till wants the
// figures, not five hundred rows, and computing them client-side would mean
// fetching every page first.
type PosSalesSummary struct {
	Locationid   int     `json:"locationid"`
	Fromdate     string  `json:"fromdate"`
	Todate       string  `json:"todate"`
	Billcount    int     `json:"billcount"`
	Itemcount    int     `json:"itemcount"`
	Grosssales   float64 `json:"grosssales"`
	Taxcollected float64 `json:"taxcollected"`
	Discount     float64 `json:"discountgiven"`
	Roundoff     float64 `json:"roundoff"`
	Averagebill  float64 `json:"averagebill"`

	// What a cashier reconciles the drawer against.
	Bypaymentmode []PosPaymentTotal `json:"bypaymentmode"`

	// One row per trading day, for a chart.
	Byday []PosDayTotal `json:"byday"`

	// Which tills contributed, so an outlet with several counters can see them
	// apart without a second query.
	Byterminal []PosTerminalTotal `json:"byterminal"`
}

type PosPaymentTotal struct {
	Paymentmode string  `json:"paymentmode"`
	Billcount   int     `json:"billcount"`
	Amount      float64 `json:"amount"`
}

type PosDayTotal struct {
	Businessdate string  `json:"businessdate"`
	Billcount    int     `json:"billcount"`
	Amount       float64 `json:"amount"`
}

type PosTerminalTotal struct {
	Terminalid string  `json:"terminalid"`
	Billcount  int     `json:"billcount"`
	Amount     float64 `json:"amount"`
}
