package models

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"

	"gorm.io/gorm"
)

// FlexibleString handles JSON that could be either a number or a string,
// and ensures it can be saved/read from the database as a string.
type FlexibleString string

func (fs *FlexibleString) UnmarshalJSON(b []byte) error {
	if len(b) == 0 {
		return nil
	}
	// If it's a string in JSON
	if b[0] == '"' {
		var s string
		if err := json.Unmarshal(b, &s); err != nil {
			return err
		}
		*fs = FlexibleString(s)
		return nil
	}
	// If it's a number in JSON
	var f float64
	if err := json.Unmarshal(b, &f); err != nil {
		return err
	}
	*fs = FlexibleString(strconv.FormatFloat(f, 'f', -1, 64))
	return nil
}

func (fs FlexibleString) MarshalJSON() ([]byte, error) {
	return json.Marshal(string(fs))
}

// 🔥 Database Logic: Handle reading from DB
func (fs *FlexibleString) Scan(value interface{}) error {
	if value == nil {
		*fs = ""
		return nil
	}
	switch v := value.(type) {
	case []byte:
		*fs = FlexibleString(v)
	case string:
		*fs = FlexibleString(v)
	case float64:
		*fs = FlexibleString(strconv.FormatFloat(v, 'f', -1, 64))
	case int64:
		*fs = FlexibleString(strconv.FormatInt(v, 10))
	default:
		*fs = FlexibleString(fmt.Sprintf("%v", v))
	}
	return nil
}

// 🔥 Database Logic: Handle writing to DB
func (fs FlexibleString) Value() (driver.Value, error) {
	return string(fs), nil
}

func (fs FlexibleString) String() string {
	return string(fs)
}

type OrderInfo struct {
	Orderheaderid      int            `json:"orderheaderid"`
	Applocationid      int            `json:"applocationid"`
	Applocation        string         `json:"applocation"`
	Tenantid           int            `json:"tenantid"`
	Partnerid          int            `json:"partnerid"`
	Locationid         int            `json:"locationid"`
	Categoryid         int            `json:"categoryid"`
	Subcategoryid      int            `json:"subcategoryid"`
	Moduleid           int            `json:"moduleid"`
	Configid           int            `json:"configid"`
	Orderid            string         `json:"orderid"`
	Orderdate          string         `json:"orderdate"`
	Deliverydate       string         `json:"deliverydate"`
	Orderstatus        string         `json:"orderstatus"`
	Deliverystatus     string         `json:"deliverystatus"`
	Deliveryamt        float64        `json:"deliveryamt"`
	Itemcount          int            `json:"itemcount"`
	Ordernotes         string         `json:"ordernotes"`
	Kms                FlexibleString `json:"kms"`
	Actualkms          FlexibleString `json:"actualkms"`
	Pending            string         `json:"Pending"`
	Processing         string         `json:"processing"`
	Ready              string         `json:"ready"`
	Cancelled          string         `json:"cancelled"`
	Delivered          string         `json:"delivered"`
	Assigntime         string         `json:"assigntime"`
	Starttime          string         `json:"starttime"`
	Arrivaltime        string         `json:"arrivaltime"`
	Pickuptime         string         `json:"pickuptime"`
	Deliverytime       string         `json:"deliverytime"`
	Canceltime         string         `json:"canceltime"`
	Deliverycharge     float32        `json:"deliverycharge"`
	Orderamount        float32        `json:"orderamount"`
	Customerid         int            `json:"customerid"`
	Pickupcustomer     string         `json:"pickupcustomer"`
	Pickupcontactno    string         `json:"pickupcontactno"`
	Pickuplocationid   int            `json:"pickuplocationid"`
	Pickupaddress      string         `json:"pickupaddress"`
	Pickupsuburb       string         `json:"pickupsuburb"`
	Pickupcity         string         `json:"pickupcity"`
	Pickuplat          FlexibleString `json:"pickuplat"`
	Pickuplong         FlexibleString `json:"pickuplong"`
	Deliveryid         int            `json:"deliveryid"`
	Deliverycustomerid int            `json:"deliverycustomerid"`
	Deliverycustomer   string         `json:"deliverycustomer"`
	Deliverycontactno  string         `json:"deliverycontactno"`
	Deliverylocationid int            `json:"deliverylocationid"`
	Deliveryaddress    string         `json:"deliveryaddress"`
	Deliverysuburb     string         `json:"deliverysuburb"`
	Droplat            FlexibleString `json:"droplat"`
	Droplon            FlexibleString `json:"droplon"`
	Deliverylat        FlexibleString `json:"deliverylat"`
	Deliverylong       FlexibleString `json:"deliverylong"`
	Deliverytype       string         `json:"deliverytype"`
	Paymenttype        int            `json:"paymenttype"`
	Tenantname         string         `json:"tenantname"`
	Tenanttoken        string         `json:"tenanttoken"`
	Tenantsuburb       string         `json:"tenantsuburb"`
	Tenantcity         string         `json:"tenantcity"`
	Tenantcontactno    string         `json:"tenantcontactno"`
	Tenantpostcode     string         `json:"tenantpostcode"`
	Locationname       string         `json:"locationname"`
	Locationsuburb     string         `json:"locationsuburb"`
	Locationcity       string         `json:"locationcity"`
	Locationcontactno  string         `json:"locationcontactno"`
	Rider              string         `json:"rider"`
	Ridercontactno     string         `json:"ridercontactno"`
	Riderkms           FlexibleString `json:"riderkms"`
	Smsdelivery        int            `json:"smsdelivery"`
	Customertoken      string         `json:"customertoken"`
	Ridertoken         string         `json:"ridertoken"`
}

type DeliveryQuery struct {
	Tenantid      int
	Customerid    int
	Partnerid     int
	UserID        int
	Appuserid     int
	Applocationid int
	Moduleid      int
	Locationid    int
	Configid      int
	Fromdate      string
	ToDate        string
	Status        string
	Pageno        int
	Pagesize      int
	Conn          *gorm.DB
	Keyword       string `json:"keyword"`
}

type Ordersummary struct {
	Total      int `json:"total"`
	Created    int `json:"created"`
	Pending    int `json:"pending"`
	Processing int `json:"processing"`
	Delivered  int `json:"delivered"`
	Cancelled  int `json:"cancelled"`
	// Locationid   int    `json:"locationid"`
	Locationname  string `json:"locationname"`
	Applocationid string `json:"applocationid"`
}

type Ordersummarylocation struct {
	Total        int    `json:"total"`
	Created      int    `json:"created"`
	Pending      int    `json:"pending"`
	Processing   int    `json:"processing"`
	Delivered    int    `json:"delivered"`
	Cancelled    int    `json:"cancelled"`
	Locationid   int    `json:"locationid"`
	Locationname string `json:"locationname"`
}

type Ordersummarydaily struct {
	Total        int    `json:"total"`
	Created      int    `json:"created"`
	Pending      int    `json:"pending"`
	Processing   int    `json:"processing"`
	Delivered    int    `json:"delivered"`
	Cancelled    int    `json:"cancelled"`
	Tenantid     int    `json:"tenantid"`
	Tenantname   string `json:"tenantname"`
	Locationid   int    `json:"locationid"`
	Locationname string `json:"locationname"`
}

type OrderInsight struct {
	Applocationid int          `json:"applocationid" gorm:"Primary_Key"`
	Locationname  string       `json:"locationname"`
	Ordermonths   *Ordermonths `json:"ordermonths" gorm:"-"`
}

type OrderInsightv1 struct {
	Locationid   int          `json:"locationid" gorm:"Primary_Key"`
	Locationname string       `json:"locationname"`
	Ordermonths  *Ordermonths `json:"ordermonths" gorm:"-"`
}

type Ordermonths struct {
	Jan  int `json:"jan"`
	Feb  int `json:"feb"`
	Mar  int `json:"mar"`
	Apr  int `json:"apr"`
	May  int `json:"may"`
	Jun  int `json:"jun"`
	Jul  int `json:"jul"`
	Aug  int `Json:"aug"`
	Sep  int `Json:"sep"`
	Oct  int `Json:"oct"`
	Nov  int `Json:"nov"`
	Dece int `Json:"dece"`
	// Applocationid int `json:"applocationid"`
}

type Orders struct {
	Orderheaderid      int            `json:"orderheaderid" gorm:"Primary_Key"`
	Tenantid           int            `json:"tenantid"`
	Locationid         int            `json:"locationid"`
	Applocationid      int            `json:"applocationid"`
	Moduleid           int            `json:"moduleid"`
	Partnerid          int            `json:"partnerid"`
	Configid           int            `json:"configid"`
	Categoryid         int            `json:"categoryid"`
	Subcategoryid      int            `json:"subcategoryid"`
	Orderid            string         `json:"orderid"`
	Orderdate          string         `json:"orderdate,omitempty"`
	Deliverytime       string         `json:"deliverytime"`
	Deliverytype       string         `json:"deliverytype"`
	Orderstatus        string         `json:"orderstatus"`
	Pending            string         `json:"pending"`
	Processing         string         `json:"processing"`
	Ready              string         `json:"ready"`
	Delivered          string         `json:"delivered"`
	Cancelled          string         `json:"cancelled"`
	Customerid         int            `json:"customerid"`
	Pickupaddress      string         `json:"pickupaddress"`
	Pickuplat          FlexibleString `json:"pickuplat"`
	Pickuplong         FlexibleString `json:"pickuplong"`
	Pickupcustomer     string         `json:"pickupcustomer"`
	Pickupcontactno    string         `json:"pickupcontactno"`
	Pickuplocation     string         `json:"pickupsuburb"` // alias
	Pickupcity         string         `json:"pickupcity"`
	Deliveryid         int            `json:"deliverycustomerid"` // alias
	Deliverycustomer   string         `json:"deliverycustomer"`
	Deliverycontactno  string         `json:"deliverycontactno"`
	Deliveryaddress    string         `json:"deliveryaddress"`
	Deliverylocation   string         `json:"deliverylocation"`
	Deliverycity       string         `json:"deliverycity"`
	Deliverylocationid int            `json:"deliverylocationid"`
	Deliverylat        FlexibleString `json:"deliverylat"`
	Deliverylong       FlexibleString `json:"deliverylong"`
	Promotionid        int            `json:"promotionid"`
	Promoname          string         `json:"promoname"`
	Promoterms         string         `json:"promoterms"`
	Promovalue         int            `json:"promovalue"`
	Promoamount        float32        `json:"promoamount"`
	Orderamount        float32        `json:"orderamount"`
	Taxamount          float32        `json:"taxamount"`
	Ordercharges       float32        `json:"ordercharges"`
	Ordervalue         float32        `json:"ordervalue"`
	Itemcount          int            `json:"itemcount"`
	Paymenttype        int            `json:"paymenttype"`
	Paymentstatus      int            `json:"paymentstatus"`
	Deliverycharge     float32        `json:"deliverycharge"`
	Ordernotes         string         `json:"ordernotes"`
	Kms                FlexibleString `json:"kms"`
	Remarks            string         `json:"remarks"`
	Tenantuserid       int            `json:"tenantuserid"`
	Partneruserid      int            `json:"partneruserid"`
	Smsdelivery        int            `json:"smsdelivery" gorm:"default:0"`
	Items              []OrderDetail  `json:"items" gorm:"-"`
}

type OrderDetails struct {
	Orderdetailid           int     `json:"orderdetailid" gorm:"primaryKey;autoIncrement"`
	Orderheaderid           int     `json:"orderheaderid"`
	Tenantid                int     `json:"tenantid"`
	Locationid              int     `json:"locationid"`
	Productid               int     `json:"productid"`
	Productname             string  `json:"productname"`
	Productdescription      string  `json:"productdescription"`
	Supplyqty               float64 `json:"supplyqty"`
	Balanceqty              float64 `json:"balanceqty"`
	Orderqty                float64 `json:"orderqty"`
	Price                   float64 `json:"price"`
	Unitid                  int     `json:"unitid"`
	Unitname                string  `json:"unitname"`
	Productaddonid          int     `json:"productaddonid"`
	Addontypeid             int     `json:"addontypeid"`
	Productmapid            int     `json:"productmapid"`
	Productvariantid        int     `json:"productvariantid"`
	Productaddondescription string  `json:"productaddondescription"`
	Discountid              int     `json:"discountid"`
	Discountname            string  `json:"discountname"`
	Discountcode            string  `json:"discountcode"`
	Discountterms           string  `json:"discountterms"`
	Discountpercentage      float64 `json:"discountpercentage"`
	Discountamount          float64 `json:"discountamount"`
	Landingamount           float64 `json:"landingamount"`
	Taxpercentage           float64 `json:"taxpercentage"`
	Taxamount               float64 `json:"taxamount"`
	Totaltaxamount          float64 `json:"totaltaxamount"`
	Productsumprice         float64 `json:"productsumprice"`
	Itemstatus              string  `json:"itemstatus"`
	Delivered               string  `json:"delivered"`
	Orderamount             float64 `json:"Orderamount"`
	Productimage            string  `json:"productimage"`
}

type OrderDetail struct {
	Orderdetailid           int     `json:"orderdetailid" gorm:"primaryKey;autoIncrement"`
	Orderheaderid           int     `json:"orderheaderid"`
	Tenantid                int     `json:"tenantid"`
	Locationid              int     `json:"locationid"`
	Productid               int     `json:"productid"`
	Productname             string  `json:"productname"`
	Productdescription      string  `json:"productdescription"`
	Supplyqty               float64 `json:"supplyqty"`
	Balanceqty              float64 `json:"balanceqty"`
	Orderqty                float64 `json:"orderqty"`
	Price                   float64 `json:"price"`
	Unitid                  int     `json:"unitid"`
	Unitname                string  `json:"unitname"`
	Productaddonid          int     `json:"productaddonid"`
	Addontypeid             int     `json:"addontypeid"`
	Productmapid            int     `json:"productmapid"`
	Productvariantid        int     `json:"productvariantid"`
	Productaddondescription string  `json:"productaddondescription"`
	Discountid              int     `json:"discountid"`
	Discountname            string  `json:"discountname"`
	Discountcode            string  `json:"discountcode"`
	Discountterms           string  `json:"discountterms"`
	Discountpercentage      float64 `json:"discountpercentage"`
	Discountamount          float64 `json:"discountamount"`
	Landingamount           float64 `json:"landingamount"`
	Taxpercentage           float64 `json:"taxpercentage"`
	Taxamount               float64 `json:"taxamount"`
	Productsumprice         float64 `json:"productsumprice"`
	Itemstatus              string  `json:"itemstatus"`
	Delivered               string  `json:"delivered"`
	Orderamount             float64 `json:"-" gorm:"-"`
	Productimage            string  `json:"productimage" gorm:"-"` // only for JSON
}

type CustomerOrder struct {
	Orderheaderid  int            `json:"orderheaderid" gorm:"Primary_Key"`
	Applocationid  int            `json:"applocationid"`
	Tenantid       int            `json:"tenantid"`
	Locationid     int            `json:"locationid"`
	Partnerid      int            `json:"partnerid"`
	Configid       int            `json:"configid"`
	Categoryid     int            `json:"categoryid"`
	Subcategoryid  int            `json:"subcategoryid"`
	Moduleid       int            `json:"moduleid"`
	Orderid        string         `json:"orderid"`
	Orderstatus    string         `json:"orderstatus"`
	Orderdate      string         `json:"orderdate,omitempty"`
	Ordernotes     string         `json:"ordernotes"`
	Itemcount      int            `json:"itemcount"`
	Deliverytime   string         `json:"deliverytime"` // alias for delivered
	Pending        string         `json:"pending"`
	Processing     string         `json:"processing"`
	Ready          string         `json:"ready"`
	Delivered      string         `json:"delivered"` // original delivered column
	Cancelled      string         `json:"cancelled"`
	Deliverycharge float32        `json:"deliverycharge"`
	Kms            FlexibleString `json:"kms"`

	Smsdelivery int `json:"smsdelivery"`

	// Tenant Info
	Tenantname      string `json:"tenantname"`
	Tenanttoken     string `json:"tenanttoken"`
	Tenantcontactno string `json:"tenantcontactno"`
	Tenantpostcode  string `json:"tenantpostcode"`
	Tenantsuburb    string `json:"tenantsuburb"`
	Tenantcity      string `json:"tenantcity"`
	Registrationno  string `json:"registrationno"`

	// Location Info
	Locationname      string `json:"locationname"`
	Locationcontactno string `json:"locationcontactno"`
	Locationpostcode  string `json:"locationpostcode"`
	Locationsuburb    string `json:"locationsuburb"`
	Locationcity      string `json:"locationcity"`

	// App Location
	Applocation string `json:"applocation"`

	// Delivery Info (from `deliveries` table)
	Deliverystatus    string         `json:"deliverystatus"`
	DeliveryUserID    int            `json:"deliveryid"` // alias
	Assigntime        string         `json:"assigntime"`
	Starttime         string         `json:"starttime"`
	Arrivaltime       string         `json:"arrivaltime"`
	Pickuptime        string         `json:"pickuptime"`
	Finaldeliverytime string         `json:"finaldeliverytime"` // alias for f.deliverytime
	Canceltime        string         `json:"canceltime"`
	Riderkms          FlexibleString `json:"riderkms"`
	// Actualkms   sql.NullFloat64 `json:"actualkms"`
	// Actualkms   sql.NullFloat64 `json:"actualkms"`
	// Riderkms    sql.NullFloat64 `json:"riderkms"`
	// Deliveryamt sql.NullFloat64 `json:"deliveryamt"`
	Droplat FlexibleString `json:"droplat"`
	Droplon FlexibleString `json:"droplon"`

	// Rider
	Rider          string         `json:"rider"`
	Orderamount    float64        `json:"orderamount"`
	Taxamount      float64        `json:"taxamount"`
	Totaltaxamount float64        `json:"totaltaxamount"`
	Riderkms_v2    FlexibleString `json:"-" gorm:"column:riderkms"` // Extra safety

	// Items if applicable
	OrderDetails []OrderDetails `json:"orderdetails" gorm:"-"`
}

type Ordersequences struct {
	Sequenceid        int    `json:"sequenceid" gorm:"Primary_Key"`
	Tenantid          int    `json:"tenantid"`
	Orderprefix       string `json:"orderprefix" gorm:"default:ORD"`
	Customerprefix    string `json:"customerprefix" gorm:"default:CUS"`
	Appointmentprefix string `json:"appointmentprefix" gorm:"default:ORD"`
	Receiptprefix     string `json:"receiptprefix" gorm:"default:REC"`
	Paymentprefix     string `json:"paymentprefix" gorm:"default:PAY"`
}

// ── Offline (in-store) sales import ───────────────────────────────────────────
//
// A sale rung up at the counter never passes through the app, so nothing
// deducts its stock. These types carry a spreadsheet of such sales into the
// same order path online orders use, so one ledger remains the single source
// of truth for stock and one revenue figure covers both channels.

// OfflineSaleItem is one spreadsheet row. Only Productid and Qtysold are
// required; the rest fall back to the product's own pricing when left blank.
// Productname is carried for verification against Productid, not for matching
// (see SaleTemplateRow for why a name can't be a key).
type OfflineSaleItem struct {
	Productid      int     `json:"productid"`
	Productname    string  `json:"productname"`
	Qtysold        float64 `json:"qtysold"`
	Unitprice      float64 `json:"unitprice"`
	Discountamount float64 `json:"discountamount"`
	Taxpercent     float64 `json:"taxpercent"`
}

// OfflineSaleBill is one counter bill — the rows of a spreadsheet grouped by
// their branch and bill number.
//
// Locationid is the branch the bill was rung up at, taken from the spreadsheet
// row rather than from a store the operator picked in the UI. One workbook can
// therefore carry sales for every branch a merchant runs, and each bill's stock
// comes out of its own outlet. Bills are grouped per branch, so the same bill
// number at two outlets is two separate sales, not a duplicate.
//
// Billno is what makes a re-upload of the same file safe: it is recorded on the
// order and refused if already present for that branch.
type OfflineSaleBill struct {
	Locationid     int               `json:"locationid"`
	Billno         string            `json:"billno"`
	Saledate       string            `json:"saledate"`
	Paymentmode    string            `json:"paymentmode"`
	Customername   string            `json:"customername"`
	Customermobile string            `json:"customermobile"`
	Remarks        string            `json:"remarks"`
	Items          []OfflineSaleItem `json:"items"`
}

// OfflineSalesUpload is the request body.
//
// Locationid here is a scope constraint, not the destination. Left at 0 the
// bills go to whichever branch each one names, which is what a multi-branch
// owner uploads. Set to a branch it pins the whole upload to that outlet and
// any bill naming a different one is refused — that is how a store user is
// held to their own store no matter what the spreadsheet says.
//
// Every branch referenced is checked against Tenantid regardless, so no upload
// can reach an outlet the merchant does not own.
type OfflineSalesUpload struct {
	Tenantid   int               `json:"tenantid"`
	Locationid int               `json:"locationid"`
	Userid     int               `json:"userid"`
	Bills      []OfflineSaleBill `json:"bills"`
}

// Outcomes a single bill can have. A bill is all-or-nothing: it either commits
// with its stock movement or it leaves nothing behind.
const (
	OfflineSaleImported  = "imported"
	OfflineSaleDuplicate = "duplicate"
	OfflineSaleFailed    = "failed"
)

// OfflineSaleResult reports one bill's fate. Bills are independent, so a file
// with one bad bill still imports the rest and names exactly what it skipped.
// The branch is echoed back because a single upload spans several, and "bill 7
// failed" is not actionable without knowing which store it belonged to.
type OfflineSaleResult struct {
	Locationid    int     `json:"locationid"`
	Locationname  string  `json:"locationname"`
	Billno        string  `json:"billno"`
	Status        string  `json:"status"`
	Orderid       string  `json:"orderid"`
	Orderheaderid int     `json:"orderheaderid"`
	Itemcount     int     `json:"itemcount"`
	Amount        float64 `json:"amount"`
	Message       string  `json:"message"`
}

type OfflineSalesUploadResponse struct {
	Imported    int                 `json:"imported"`
	Duplicate   int                 `json:"duplicate"`
	Failed      int                 `json:"failed"`
	Totalamount float64             `json:"totalamount"`
	Results     []OfflineSaleResult `json:"results"`
}

type TenantRevenueSummary struct {
	Tenantid        int                      `json:"tenantid"`
	Tenantname      string                   `json:"tenantname"`
	OverallRevenue  float64                  `json:"overallrevenue"`
	LocationRevenue []LocationRevenueDetails `json:"locationrevenue"`
}

type LocationRevenueDetails struct {
	Locationid   int     `json:"locationid"`
	Locationname string  `json:"locationname"`
	Revenue      float64 `json:"revenue"`
}

type SalesSummaryChartData struct {
	Date    string  `json:"date"`
	Revenue float64 `json:"revenue"`
	Orders  int     `json:"orders"`
}

type SalesSummaryTopLocation struct {
	Locationname string  `json:"locationname"`
	Revenue      float64 `json:"revenue"`
}

type SalesSummaryResponse struct {
	TotalRevenue      float64                   `json:"totalRevenue"`
	TotalOrders       int                       `json:"totalOrders"`
	AverageOrderValue float64                   `json:"averageOrderValue"`
	ChartData         []SalesSummaryChartData   `json:"chartData"`
	TopLocations      []SalesSummaryTopLocation `json:"topLocations"`
}

type TimeSeriesData struct {
	Label      string  `json:"label"`
	Orders     int     `json:"orders"`
	Revenue    float64 `json:"revenue"`
	Cancelled  int     `json:"cancelled"`
	Delivered  int     `json:"delivered"`
	Activeskus int     `json:"activeskus"`
}
