package repositories

import (
	"fmt"
	"log"
	"nearle/models"
	"strings"
	"time"

	"gorm.io/gorm"
)

type OrderRepository interface {
	GetTenantOrders(q models.DeliveryQuery) ([]models.OrderInfo, error)
	GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error)
	GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error)
	GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error)
	GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error)
	GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error)
	GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error)
	GetOrderSummary(tid, pid, cid, aid int, fdate, tdate string) ([]models.Ordersummarydaily, error)
	GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error)
	GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error)
	GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error)
	UpdateOrder(order *models.Orders) error
	CreateOrder(order models.Orders) (models.Orders, error)
	GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error)
	GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error)
	GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error)
	GetTimeSeries(tenantID, locationID int, granularity, fromDate, toDate string) ([]models.TimeSeriesData, error)
}

type orderRepository struct {
	db *gorm.DB
}

func NewOrderRepository(db *gorm.DB) OrderRepository {
	return &orderRepository{db: db}
}

const (
	base = `SELECT DISTINCT a.orderheaderid, a.applocationid, h.locationname AS applocation, a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid,
		a.orderid, a.orderstatus, a.orderdate, a.ordernotes, a.itemcount, a.deliverytime AS deliverydate,
		a.pending, a.processing, a.ready, a.delivered AS completed, a.cancelled, 
		a.deliverycharge, a.kms,
		a.customerid, a.pickuplocationid, a.pickupaddress, a.pickuplat, a.pickuplong,
		a.pickupcustomer, a.pickupcontactno, a.pickuplocation as pickupsuburb, a.pickupcity,
		a.deliveryid AS deliverycustomerid,  a.deliveryaddress, a.deliverylat, a.deliverylong, a.deliverytype, 
		a.deliverycustomer,a.deliverycontactno,a.deliverylocation as deliverysuburb, a.deliverycity, a.paymenttype, a.smsdelivery, b.customertoken,
		c.tenantname, c.tenanttoken, c.primarycontact AS tenantcontactno, c.postcode AS tenantpostcode, c.suburb AS tenantsuburb, c.city AS tenantcity,
		d.locationname, d.contactno AS locationcontactno, d.postcode AS locationpostcode, d.suburb AS locationsuburb, d.city AS locationcity
		FROM orders a
		INNER JOIN customers b ON a.customerid = b.customerid
		INNER JOIN tenants c ON a.tenantid = c.tenantid
		INNER JOIN tenantlocations d ON a.locationid = d.locationid
	
		INNER JOIN app_location h ON a.applocationid = h.applocationid
		INNER JOIN app_locationconfig i ON a.applocationid = i.applocationid`

	orderdetails = `SELECT DISTINCT a.orderheaderid, a.applocationid,
	a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid,
	a.orderid, a.orderstatus, a.orderdate, a.ordernotes, a.itemcount, a.deliverytime AS deliverydate,
	a.pending, a.processing, a.ready, a.delivered AS completed, a.cancelled,
	a.deliverycharge, a.kms, 
	a.customerid, a.pickupaddress, a.pickuplat, a.pickuplong,
	a.pickupcustomer, a.pickupcontactno, a.pickuplocation as pickupsuburb, a.pickupcity,
	a.deliveryid AS deliverycustomerid,  a.deliveryaddress, a.deliverylat, a.deliverylong, a.deliverytype, 
	a.deliverycustomer,a.deliverycontactno,a.deliverylocation as deliverysuburb, a.deliverycity,a.paymenttype, a.smsdelivery, a.orderamount,
	b.tenantname, b.tenanttoken, b.primarycontact AS tenantcontactno, b.postcode AS tenantpostcode, b.suburb AS tenantsuburb,b.city AS tenantcity,
	c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode, c.suburb AS locationsuburb, c.city AS locationcity, 
	d.locationname AS applocation
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid`
)

func (r *orderRepository) GetTenantOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) {
	var data []models.OrderInfo
	var query string
	var params []interface{}

	offset := (input.Pageno - 1) * input.Pagesize
	baseQuery := base + ` WHERE a.tenantid = ?`
	params = append(params, input.Tenantid)

	if input.Status == "ongoing" {
		query = baseQuery + ` AND a.orderstatus IN ('pending','processing','ready')`
	} else {
		query = baseQuery + ` AND a.orderstatus = ?`
		params = append(params, input.Status)
	}

	query += ` AND a.deliverytime::date BETWEEN ? AND ?`
	params = append(params, input.Fromdate, input.ToDate)

	if input.Keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			c.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + input.Keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	if input.Configid != 0 {
		query += ` AND a.configid = ?`
		params = append(params, input.Configid)
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, input.Pagesize, offset)

	fmt.Println("Executing:", query)
	res := r.db.Raw(query, params...).Find(&data)

	return data, res.Error
}

func (r *orderRepository) GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) {
	var data []models.OrderInfo

	if pageno <= 0 {
		pageno = 1
	}
	if pagesize <= 0 {
		pagesize = 10
	}
	offset := (pageno - 1) * pagesize

	fmt.Println("Getting partner order details")

	query := orderdetails + ` WHERE a.partnerid = ?`
	params := []interface{}{pid}

	if fdate != "" && tdate != "" {
		query += ` AND a.deliverytime::date BETWEEN ? AND ?`
		params = append(params, fdate, tdate)
	}
	if stat != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, stat)
	}
	if keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	var total int64
	countQuery := `
	SELECT COUNT(DISTINCT a.orderheaderid)
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
	LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
	LEFT JOIN app_users g ON f.userid = g.userid
	WHERE a.partnerid = ?`
	countParams := []interface{}{pid}

	if fdate != "" && tdate != "" {
		countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?`
		countParams = append(countParams, fdate, tdate)
	}
	if stat != "" {
		countQuery += ` AND a.orderstatus = ?`
		countParams = append(countParams, stat)
	}
	if keyword != "" {
		countQuery += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		countParams = append(countParams, like, like, like, like, like, like)
	}

	r.db.Raw(countQuery, countParams...).Scan(&total)

	if int64(offset) >= total {
		offset = 0
		pageno = 1
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, pagesize, offset)

	fmt.Println("QUERY:", query)
	fmt.Println("PARAMS:", params)

	r.db.Raw(query, params...).Find(&data)
	fmt.Println("RESULT COUNT:", len(data))

	return data, nil
}

func (r *orderRepository) GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) {
	var data []models.OrderInfo

	if pageno <= 0 {
		pageno = 1
	}
	if pagesize <= 0 {
		pagesize = 10
	}
	offset := (pageno - 1) * pagesize

	fmt.Println("Getting customer order details")

	query := orderdetails + ` WHERE a.customerid = ?`
	params := []interface{}{cid}

	if mid != 0 {
		query += ` AND a.moduleid = ?`
		params = append(params, mid)
	}
	if fdate != "" && tdate != "" {
		query += ` AND a.orderdate::date BETWEEN ? AND ?`
		params = append(params, fdate, tdate)
	}
	if stat != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, stat)
	}
	if keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	var total int64
	countQuery := `
	SELECT COUNT(DISTINCT a.orderheaderid)
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
	WHERE a.customerid = ?`
	countParams := []interface{}{cid}

	if mid != 0 {
		countQuery += ` AND a.moduleid = ?`
		countParams = append(countParams, mid)
	}
	if fdate != "" && tdate != "" {
		countQuery += ` AND a.orderdate::date BETWEEN ? AND ?`
		countParams = append(countParams, fdate, tdate)
	}
	if stat != "" {
		countQuery += ` AND a.orderstatus = ?`
		countParams = append(countParams, stat)
	}
	if keyword != "" {
		countQuery += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		countParams = append(countParams, like, like, like, like, like, like)
	}

	r.db.Raw(countQuery, countParams...).Scan(&total)

	if int64(offset) >= total {
		offset = 0
		pageno = 1
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, pagesize, offset)

	fmt.Println("QUERY:", query)
	fmt.Println("PARAMS:", params)

	res := r.db.Raw(query, params...).Find(&data)
	if res.Error != nil {
		fmt.Println("ERROR:", res.Error)
	}
	fmt.Println("RESULT COUNT:", len(data))

	return data, nil
}

func (r *orderRepository) GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) {
	var data []models.OrderInfo

	if pageno <= 0 {
		pageno = 1
	}
	if pagesize <= 0 {
		pagesize = 10
	}
	offset := (pageno - 1) * pagesize

	fmt.Println("Getting admin order details")

	query := orderdetails + ` WHERE 1=1`
	params := []interface{}{}

	if aid != 0 {
		query += ` AND a.applocationid = ?`
		params = append(params, aid)
	}

	if fdate != "" && tdate != "" {
		query += ` AND a.deliverytime::date BETWEEN ? AND ?`
		params = append(params, fdate, tdate)
	}

	if stat != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, stat)
	}

	if keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	var total int64
	countQuery := `
	SELECT COUNT(DISTINCT a.orderheaderid)
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
	LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
	LEFT JOIN app_users g ON f.userid = g.userid
	WHERE 1=1
	`
	countParams := []interface{}{}

	if aid != 0 {
		countQuery += ` AND a.applocationid = ?`
		countParams = append(countParams, aid)
	}
	if fdate != "" && tdate != "" {
		countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?`
		countParams = append(countParams, fdate, tdate)
	}
	if stat != "" {
		countQuery += ` AND a.orderstatus = ?`
		countParams = append(countParams, stat)
	}
	if keyword != "" {
		countQuery += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		countParams = append(countParams, like, like, like, like, like, like)
	}

	r.db.Raw(countQuery, countParams...).Scan(&total)

	if int64(offset) >= total {
		offset = 0
		pageno = 1
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, pagesize, offset)

	fmt.Println("QUERY:", query)
	fmt.Println("PARAMS:", params)

	r.db.Raw(query, params...).Find(&data)
	fmt.Println("RESULT COUNT:", len(data))

	return data, nil
}

func (r *orderRepository) GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) {
	var data []models.OrderInfo

	if pageno <= 0 {
		pageno = 1
	}
	if pagesize <= 0 {
		pagesize = 10
	}
	offset := (pageno - 1) * pagesize

	fmt.Println("Getting user order details")

	query := orderdetails + ` WHERE e.status = 'Active' AND e.userid = ?`
	params := []interface{}{uid}

	if fdate != "" && tdate != "" {
		query += ` AND a.deliverytime::date BETWEEN ? AND ?`
		params = append(params, fdate, tdate)
	}
	if stat != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, stat)
	}
	if keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	var total int64
	countQuery := `
	SELECT COUNT(DISTINCT a.orderheaderid)
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
	LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
	LEFT JOIN app_users g ON f.userid = g.userid
	WHERE e.status = 'Active' AND e.userid = ?
	`
	countParams := []interface{}{uid}

	if fdate != "" && tdate != "" {
		countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?`
		countParams = append(countParams, fdate, tdate)
	}
	if stat != "" {
		countQuery += ` AND a.orderstatus = ?`
		countParams = append(countParams, stat)
	}
	if keyword != "" {
		countQuery += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		countParams = append(countParams, like, like, like, like, like, like)
	}

	r.db.Raw(countQuery, countParams...).Scan(&total)

	if int64(offset) >= total {
		offset = 0
		pageno = 1
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, pagesize, offset)

	fmt.Println("QUERY:", query)
	fmt.Println("PARAMS:", params)

	r.db.Raw(query, params...).Find(&data)
	fmt.Println("RESULT COUNT:", len(data))

	return data, nil
}

func (r *orderRepository) GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) {
	var data []models.OrderInfo

	if pageno <= 0 {
		pageno = 1
	}
	if pagesize <= 0 {
		pagesize = 10
	}
	offset := (pageno - 1) * pagesize

	fmt.Println("Getting all orders")

	query := orderdetails + ` WHERE 1=1`
	params := []interface{}{}

	if fdate != "" && tdate != "" {
		query += ` AND a.deliverytime::date BETWEEN ? AND ?`
		params = append(params, fdate, tdate)
	}

	if stat != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, stat)
	}

	if keyword != "" {
		query += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		params = append(params, like, like, like, like, like, like)
	}

	var total int64
	countQuery := `
	SELECT COUNT(DISTINCT a.orderheaderid)
	FROM orders a
	INNER JOIN tenants b ON a.tenantid = b.tenantid
	INNER JOIN tenantlocations c ON a.locationid = c.locationid
	INNER JOIN app_location d ON a.applocationid = d.applocationid
	INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid
	LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid
	LEFT JOIN app_users g ON f.userid = g.userid
	WHERE 1=1
	`
	countParams := []interface{}{}

	if fdate != "" && tdate != "" {
		countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?`
		countParams = append(countParams, fdate, tdate)
	}
	if stat != "" {
		countQuery += ` AND a.orderstatus = ?`
		countParams = append(countParams, stat)
	}
	if keyword != "" {
		countQuery += ` AND (
			a.pickupcustomer LIKE ? OR
			b.tenantname LIKE ? OR
			a.deliverycustomer LIKE ? OR
			a.pickupcontactno LIKE ? OR
			a.deliverycontactno LIKE ? OR
			a.orderid LIKE ?
		)`
		like := "%" + keyword + "%"
		countParams = append(countParams, like, like, like, like, like, like)
	}

	r.db.Raw(countQuery, countParams...).Scan(&total)

	if int64(offset) >= total {
		offset = 0
		pageno = 1
	}

	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, pagesize, offset)

	fmt.Println("QUERY:", query)
	fmt.Println("PARAMS:", params)

	r.db.Raw(query, params...).Find(&data)
	fmt.Println("RESULT COUNT:", len(data))

	return data, nil
}

func (r *orderRepository) GetOrderSummary(tid, pid, cid, lid int, fdate, tdate string) ([]models.Ordersummarydaily, error) {
	var data []models.Ordersummarydaily

	// Base SELECT
	const base = `
		SELECT
			COUNT(*) AS total,
			SUM(CASE WHEN o.orderstatus = 'created' THEN 1 ELSE 0 END) AS created,
			SUM(CASE WHEN o.orderstatus = 'pending' THEN 1 ELSE 0 END) AS pending,
			SUM(CASE WHEN o.orderstatus = 'processing' THEN 1 ELSE 0 END) AS processing,
			SUM(CASE WHEN o.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered,
			SUM(CASE WHEN o.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS cancelled,
			t.tenantid,
			t.tenantname
		FROM orders o
		INNER JOIN tenants t ON o.tenantid = t.tenantid
	`

	var params []interface{}
	var q1 string

	// Apply filters (at least one scoping id is required — enforced by the caller)
	switch {
	case tid != 0:
		q1 = base + " WHERE o.configid = 1 AND o.tenantid = ?"
		params = append(params, tid)
	case pid != 0:
		q1 = base + " WHERE o.configid = 1 AND o.partnerid = ?"
		params = append(params, pid)
	case cid != 0:
		q1 = base + " WHERE o.configid = 1 AND o.customerid = ?"
		params = append(params, cid)
	case lid != 0:
		q1 = base + " WHERE o.configid = 1 AND o.locationid = ?"
		params = append(params, lid)
	default:
		return nil, fmt.Errorf("at least one of tenantid, partnerid, customerid or locationid is required")
	}

	// Date filter
	if fdate != "" && tdate != "" {
		q1 += " AND o.orderdate::date BETWEEN ? AND ?"
		params = append(params, fdate, tdate)
	}

	// Group by tenant
	q1 += " GROUP BY t.tenantid, t.tenantname"

	// Debug
	fmt.Println("Executing GetOrderSummary query:", q1)

	if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil {
		return nil, err
	}

	return data, nil
}

func (r *orderRepository) GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error) {
	var data []models.Ordersummarylocation
	var params []interface{}

	q1 := `
	SELECT 
		l.locationid, 
		l.locationname, 
		COALESCE(COUNT(o.orderid), 0) AS total,
		COALESCE(SUM(CASE WHEN o.orderstatus = 'created' THEN 1 ELSE 0 END), 0) AS created,
		COALESCE(SUM(CASE WHEN o.orderstatus = 'pending' THEN 1 ELSE 0 END), 0) AS pending,
		COALESCE(SUM(CASE WHEN o.orderstatus = 'processing' THEN 1 ELSE 0 END), 0) AS processing,
		COALESCE(SUM(CASE WHEN o.orderstatus = 'delivered' THEN 1 ELSE 0 END), 0) AS delivered,
		COALESCE(SUM(CASE WHEN o.orderstatus = 'cancelled' THEN 1 ELSE 0 END), 0) AS cancelled
	FROM tenantlocations l
	LEFT JOIN orders o 
		ON l.locationid = o.locationid 
		AND l.tenantid = o.tenantid
		AND o.configid = 1
	`

	if tenantID != 0 {
		q1 += " WHERE l.tenantid = ?"
		params = append(params, tenantID)
	}

	q1 += " GROUP BY l.locationid, l.locationname ORDER BY l.locationid"

	fmt.Println("Executing query:", q1)

	if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil {
		return nil, err
	}

	return data, nil
}

func (r *orderRepository) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error) {
	var summary models.TenantRevenueSummary

	// 1. If tid is 0 and lid is not 0, lookup the tenantid from tenantlocations
	if tid == 0 && lid != 0 {
		var tenantLoc struct {
			Tenantid int
		}
		if err := r.db.Table("tenantlocations").Select("tenantid").Where("locationid = ?", lid).Scan(&tenantLoc).Error; err != nil {
			return nil, err
		}
		tid = tenantLoc.Tenantid
	}

	if tid == 0 {
		return nil, fmt.Errorf("tenant ID is required or could not be determined")
	}

	summary.Tenantid = tid

	// 2. Fetch the tenant name
	var tenant struct {
		Tenantname string
	}
	if err := r.db.Table("tenants").Select("tenantname").Where("tenantid = ?", tid).Scan(&tenant).Error; err != nil {
		return nil, err
	}
	summary.Tenantname = tenant.Tenantname

	// 3. Fetch overall revenue for this tenant
	overallQuery := `
		SELECT COALESCE(SUM(orderamount), 0) AS overall_revenue
		FROM orders
		WHERE tenantid = ? AND orderstatus = 'delivered' AND configid = 1
	`
	var overallParams []interface{}
	overallParams = append(overallParams, tid)

	if lid != 0 {
		overallQuery += " AND locationid = ?"
		overallParams = append(overallParams, lid)
	}

	if fdate != "" && tdate != "" {
		overallQuery += " AND orderdate::date BETWEEN ? AND ?"
		overallParams = append(overallParams, fdate, tdate)
	}

	var overallRev float64
	if err := r.db.Raw(overallQuery, overallParams...).Scan(&overallRev).Error; err != nil {
		return nil, err
	}
	summary.OverallRevenue = overallRev

	// 4. Fetch revenue details by location
	locationQuery := `
		SELECT 
			l.locationid, 
			l.locationname, 
			COALESCE(SUM(o.orderamount), 0) AS revenue
		FROM tenantlocations l
		LEFT JOIN orders o 
			ON l.locationid = o.locationid 
			AND o.orderstatus = 'delivered'
			AND o.configid = 1
	`
	var locParams []interface{}

	if fdate != "" && tdate != "" {
		locationQuery += " AND o.orderdate::date BETWEEN ? AND ?"
		locParams = append(locParams, fdate, tdate)
	}

	locationQuery += " WHERE l.tenantid = ?"
	locParams = append(locParams, tid)

	if lid != 0 {
		locationQuery += " AND l.locationid = ?"
		locParams = append(locParams, lid)
	}

	locationQuery += " GROUP BY l.locationid, l.locationname ORDER BY l.locationid"

	var locRevenues []models.LocationRevenueDetails
	if err := r.db.Raw(locationQuery, locParams...).Scan(&locRevenues).Error; err != nil {
		return nil, err
	}
	
	if locRevenues == nil {
		locRevenues = []models.LocationRevenueDetails{}
	}
	summary.LocationRevenue = locRevenues

	return &summary, nil
}

func (r *orderRepository) GetDistinctLocations() ([]models.OrderInsight, error) {
	var locations []models.OrderInsight

	q1 := `
		SELECT DISTINCT a.applocationid, b.locationname 
		FROM orders a 
		INNER JOIN app_location b ON a.applocationid = b.applocationid
		WHERE b.status = 'Active'`

	if err := r.db.Raw(q1).Scan(&locations).Error; err != nil {
		return nil, err
	}
	return locations, nil
}

func (r *orderRepository) GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error) {
	var summary models.SalesSummaryResponse
	
	whereClause := "tenantid = ? AND orderstatus IN ('delivered', 'completed') AND configid = 1"
	var params []interface{}
	params = append(params, tid)
	
	if lid != 0 {
		whereClause += " AND locationid = ?"
		params = append(params, lid)
	}
	
	if fdate != "" && tdate != "" {
		whereClause += " AND orderdate::date BETWEEN ? AND ?"
		params = append(params, fdate, tdate)
	}
	
	totalsQuery := fmt.Sprintf(`
		SELECT 
			COALESCE(SUM(COALESCE(ordervalue, 0) + COALESCE(orderamount, 0) + COALESCE(deliveryamt, 0)), 0) AS total_revenue,
			COUNT(orderheaderid) AS total_orders
		FROM orders 
		WHERE %s`, whereClause)
		
	var result struct {
		TotalRevenue float64
		TotalOrders  int
	}
	
	if err := r.db.Raw(totalsQuery, params...).Scan(&result).Error; err != nil {
		return nil, err
	}
	
	summary.TotalRevenue = result.TotalRevenue
	summary.TotalOrders = result.TotalOrders
	
	if summary.TotalOrders > 0 {
		summary.AverageOrderValue = summary.TotalRevenue / float64(summary.TotalOrders)
	}
	
	chartQuery := fmt.Sprintf(`
		SELECT 
			CAST(orderdate AS DATE) AS date,
			COALESCE(SUM(COALESCE(ordervalue, 0) + COALESCE(orderamount, 0) + COALESCE(deliveryamt, 0)), 0) AS revenue,
			COUNT(orderheaderid) AS orders
		FROM orders 
		WHERE %s
		GROUP BY CAST(orderdate AS DATE)
		ORDER BY date ASC`, whereClause)
		
	var chartData []models.SalesSummaryChartData
	if err := r.db.Raw(chartQuery, params...).Scan(&chartData).Error; err != nil {
		return nil, err
	}
	
	if chartData == nil {
		chartData = []models.SalesSummaryChartData{}
	}
	summary.ChartData = chartData
	
	var topLocations []models.SalesSummaryTopLocation
	if lid == 0 {
		topWhere := "o.tenantid = ? AND o.orderstatus IN ('delivered', 'completed') AND o.configid = 1"
		var topParams []interface{}
		topParams = append(topParams, tid)
		if fdate != "" && tdate != "" {
			topWhere += " AND o.orderdate::date BETWEEN ? AND ?"
			topParams = append(topParams, fdate, tdate)
		}
		
		cleanTopLocQuery := fmt.Sprintf(`
			SELECT 
				COALESCE(l.locationname, 'Unknown') AS locationname,
				COALESCE(SUM(COALESCE(o.ordervalue, 0) + COALESCE(o.orderamount, 0) + COALESCE(o.deliveryamt, 0)), 0) AS revenue
			FROM orders o
			LEFT JOIN tenantlocations l ON o.locationid = l.locationid
			WHERE %s
			GROUP BY l.locationid, l.locationname
			ORDER BY revenue DESC
			LIMIT 5`, topWhere)
			
		if err := r.db.Raw(cleanTopLocQuery, topParams...).Scan(&topLocations).Error; err != nil {
			return nil, err
		}
	}
	
	if topLocations == nil {
		topLocations = []models.SalesSummaryTopLocation{}
	}
	summary.TopLocations = topLocations
	
	return &summary, nil
}

func (r *orderRepository) GetMonthlyOrders(applocationid string) (*models.Ordermonths, error) {
	var orderMonths models.Ordermonths

	q2 := `
		SELECT 
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 1 THEN 1 ELSE 0 END), 0) AS jan,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 2 THEN 1 ELSE 0 END), 0) AS feb,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 3 THEN 1 ELSE 0 END), 0) AS mar,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 4 THEN 1 ELSE 0 END), 0) AS apr,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 5 THEN 1 ELSE 0 END), 0) AS may,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 6 THEN 1 ELSE 0 END), 0) AS jun,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 7 THEN 1 ELSE 0 END), 0) AS jul,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 8 THEN 1 ELSE 0 END), 0) AS aug,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 9 THEN 1 ELSE 0 END), 0) AS sep,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 10 THEN 1 ELSE 0 END), 0) AS oct,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 11 THEN 1 ELSE 0 END), 0) AS nov,
			COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 12 THEN 1 ELSE 0 END), 0) AS dece
		FROM orders a
		WHERE a.applocationid = ? 
		AND EXTRACT(YEAR FROM a.orderdate) = EXTRACT(YEAR FROM CURRENT_DATE)`

	if err := r.db.Raw(q2, applocationid).Scan(&orderMonths).Error; err != nil {
		return nil, err
	}

	return &orderMonths, nil
}

func (r *orderRepository) GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error) {
	var locations []models.OrderInsightv1
	var params []interface{}

	// ✅ Query 1: Get all locations (even without orders)
	q1 := `SELECT b.locationid, b.locationname
	       FROM tenantlocations b
	       WHERE b.status = 'Active'`

	if tenantID != 0 {
		q1 += " AND b.tenantid = ?"
		params = append(params, tenantID)
	}

	if err := r.db.Raw(q1, params...).Scan(&locations).Error; err != nil {
		return nil, err
	}

	// ✅ Query 2: Monthly order counts per location
	q2 := `SELECT 
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 1 THEN 1 ELSE 0 END), 0) AS jan,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 2 THEN 1 ELSE 0 END), 0) AS feb,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 3 THEN 1 ELSE 0 END), 0) AS mar,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 4 THEN 1 ELSE 0 END), 0) AS apr,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 5 THEN 1 ELSE 0 END), 0) AS may,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 6 THEN 1 ELSE 0 END), 0) AS jun,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 7 THEN 1 ELSE 0 END), 0) AS jul,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 8 THEN 1 ELSE 0 END), 0) AS aug,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 9 THEN 1 ELSE 0 END), 0) AS sep,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 10 THEN 1 ELSE 0 END), 0) AS oct,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 11 THEN 1 ELSE 0 END), 0) AS nov,
		COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 12 THEN 1 ELSE 0 END), 0) AS dece
		FROM orders a
		WHERE a.locationid = ? AND EXTRACT(YEAR FROM a.orderdate) = EXTRACT(YEAR FROM CURRENT_DATE)`

	if tenantID != 0 {
		q2 += " AND a.tenantid = ?"
	}

	// ✅ Attach monthly order counts for each location
	for i := range locations {
		var orderMonths models.Ordermonths

		if tenantID != 0 {
			if err := r.db.Raw(q2, locations[i].Locationid, tenantID).Scan(&orderMonths).Error; err != nil {
				return nil, err
			}
		} else {
			if err := r.db.Raw(q2, locations[i].Locationid).Scan(&orderMonths).Error; err != nil {
				return nil, err
			}
		}

		locations[i].Ordermonths = &orderMonths
	}

	return locations, nil
}

func (r *orderRepository) GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error) {
	var details []models.OrderDetails

	query := `
		SELECT a.orderdetailid, a.orderheaderid, a.tenantid, a.locationid, a.productid, a.productname, a.productdescription, a.supplyqty, a.balanceqty,
			a.orderqty, a.price, a.unitid, a.unitname, a.productaddonid, a.addontypeid, a.productmapid, a.productvariantid, a.productaddondescription,
			a.discountid, a.discountname, a.discountcode, a.discountterms, a.discountpercentage, a.discountamount, a.landingamount, a.taxpercentage,
			a.taxamount, a.productsumprice, a.itemstatus, a.delivered, COALESCE(b.orderamount, 0) as orderamount, COALESCE(b.taxamount, 0) as totaltaxamount,
			c.productimage
		FROM orderdetails a
		LEFT JOIN orders b ON b.orderheaderid = a.orderheaderid
		LEFT JOIN products c ON a.productid = c.productid
		WHERE a.orderheaderid = ?
	`

	if err := r.db.Raw(query, orderHeaderID).Scan(&details).Error; err != nil {
		return nil, err
	}

	return details, nil
}

func (r *orderRepository) UpdateOrder(order *models.Orders) error {
	tx := r.db.Begin()

	// Handle stock restoration on order cancellation
	var existingOrder models.Orders
	if err := tx.Where("orderheaderid = ?", order.Orderheaderid).First(&existingOrder).Error; err == nil {
		newStatus := strings.ToLower(strings.TrimSpace(order.Orderstatus))
		oldStatus := strings.ToLower(strings.TrimSpace(existingOrder.Orderstatus))

		if newStatus == "cancelled" && oldStatus != "cancelled" {
			var items []models.OrderDetail
			if err := tx.Table("orderdetails").Where("orderheaderid = ?", order.Orderheaderid).Find(&items).Error; err == nil {
				for _, item := range items {
					itemLocID := item.Locationid
					if itemLocID == 0 {
						itemLocID = existingOrder.Locationid
					}
					qty := int(item.Orderqty)
					if qty <= 0 {
						qty = 1
					}

					restoredStock := models.Productstock{
						Tenantid:   existingOrder.Tenantid,
						Stockdate:  time.Now(),
						Locationid: itemLocID,
						Productid:  item.Productid,
						Quantity:   qty,
						Stocktype:  "in",
						Status:     "Active",
					}
					if err := tx.Table("productstocks").Create(&restoredStock).Error; err != nil {
						tx.Rollback()
						return err
					}

					// Update productlocation status back to available if stock is restored
					tx.Table("productlocations").
						Where("productid = ? AND tenantid = ? AND locationid = ?", item.Productid, existingOrder.Tenantid, itemLocID).
						Update("status", "available")
				}
			}
		}
	}

	if err := tx.Where("orderheaderid = ?", order.Orderheaderid).Updates(order).Error; err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit().Error
}

func (r *orderRepository) getSequenceno(tid int, prefix string) string {
	type SeqResult struct {
		Orderseqno string
	}

	var q1 string
	// Formats the ID as tenantid-subprefix+seqno (e.g., 908-20245189)
	switch prefix {
	case "ORD":
		q1 = `SELECT CONCAT(tenantid, '-', 
		             CASE WHEN subprefix IS NULL OR CAST(subprefix AS TEXT) IN ('0', '0.0', '') THEN '' ELSE CAST(subprefix AS TEXT) END, 
		             COALESCE(MAX(orderseqno) + 1, 1)) AS orderseqno
		      FROM ordersequences WHERE tenantid = ?
		      GROUP BY tenantid, subprefix`
	case "INV":
		q1 = `SELECT CONCAT(tenantid, '-', 
		             CASE WHEN subprefix IS NULL OR CAST(subprefix AS TEXT) IN ('0', '0.0', '') THEN '' ELSE CAST(subprefix AS TEXT) END, 
		             COALESCE(MAX(invoiceseqno) + 1, 1)) AS orderseqno
		      FROM ordersequences WHERE tenantid = ?
		      GROUP BY tenantid, subprefix`
	}

	var result SeqResult
	r.db.Raw(q1, tid).Scan(&result)

	// Fallback if no row exists in the database
	if result.Orderseqno == "" {
		return fmt.Sprintf("%d-1", tid)
	}

	return result.Orderseqno
}

func (r *orderRepository) updateSeqno(tid int, prefix string) error {
	var field string
	switch prefix {
	case "ORD":
		field = "orderseqno"
	case "INV":
		field = "invoiceseqno"
	default:
		return fmt.Errorf("invalid prefix: %s", prefix)
	}

	// 🛠️ Improved: Check if row exists, if not, create it
	var count int64
	r.db.Table("ordersequences").Where("tenantid = ?", tid).Count(&count)

	if count == 0 {
		newSeq := map[string]interface{}{
			"tenantid":   tid,
			field:        1,
			"subprefix":  nil, // Use NULL so CONCAT ignores it or set default here
		}
		return r.db.Table("ordersequences").Create(&newSeq).Error
	}

	// If it exists, perform the update
	return r.db.Table("ordersequences").
		Where("tenantid = ?", tid).
		Update(field, gorm.Expr(fmt.Sprintf("%s + 1", field))).Error
}

func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) {
	tx := r.db.Begin()

	locID := data.Locationid
	if locID == 0 {
		locID = data.Applocationid
	}

	// 🛠️ Step 1: Pre-validate stock availability for all items before placing order
	for _, item := range data.Items {
		itemLocID := item.Locationid
		if itemLocID == 0 {
			itemLocID = locID
		}

		requestedQty := int(item.Orderqty)
		if requestedQty <= 0 {
			requestedQty = 1
		}

		var availableStock int
		stockQuery := `
			SELECT COALESCE(
				SUM(CASE WHEN LOWER(stocktype) = 'in' THEN quantity ELSE 0 END) -
				SUM(CASE WHEN LOWER(stocktype) = 'out' THEN quantity ELSE 0 END),
				0
			)
			FROM productstocks
			WHERE productid = ? AND tenantid = ? AND locationid = ?
		`
		if err := tx.Raw(stockQuery, item.Productid, data.Tenantid, itemLocID).Scan(&availableStock).Error; err != nil {
			tx.Rollback()
			return models.Orders{}, fmt.Errorf("failed to verify stock for product %d: %w", item.Productid, err)
		}

		// If stock tracking exists and available stock is less than requested quantity, block order placement
		if availableStock < requestedQty {
			tx.Rollback()
			pName := item.Productname
			if pName == "" {
				pName = fmt.Sprintf("ID %d", item.Productid)
			}
			return models.Orders{}, fmt.Errorf("insufficient stock for product '%s': requested %d, available %d", pName, requestedQty, availableStock)
		}
	}

	// 🛠️ Step 2: Create Order Header
	data.Orderid = r.getSequenceno(data.Tenantid, "ORD")

	if err := tx.Create(&data).Error; err != nil {
		tx.Rollback()
		return models.Orders{}, err
	}

	// 🛠️ Step 3: Insert Order Details & Record "out" stock deduction
	for _, item := range data.Items {
		item.Orderheaderid = data.Orderheaderid
		item.Tenantid = data.Tenantid
		itemLocID := item.Locationid
		if itemLocID == 0 {
			itemLocID = locID
		}
		item.Locationid = itemLocID

		if err := tx.Table("orderdetails").Create(&item).Error; err != nil {
			tx.Rollback()
			return models.Orders{}, err
		}

		qty := int(item.Orderqty)
		if qty <= 0 {
			qty = 1
		}

		stock := models.Productstock{
			Tenantid:   data.Tenantid,
			Stockdate:  time.Now(),
			Locationid: itemLocID,
			Productid:  item.Productid,
			Quantity:   qty,
			Stocktype:  "out",
			Status:     "Active",
		}
		if err := tx.Table("productstocks").Create(&stock).Error; err != nil {
			tx.Rollback()
			return models.Orders{}, err
		}

		// Update productlocation status to 'outofstock' if remaining stock drops to 0 or below
		var remainingStock int
		remStockQuery := `
			SELECT COALESCE(
				SUM(CASE WHEN LOWER(stocktype) = 'in' THEN quantity ELSE 0 END) -
				SUM(CASE WHEN LOWER(stocktype) = 'out' THEN quantity ELSE 0 END),
				0
			)
			FROM productstocks
			WHERE productid = ? AND tenantid = ? AND locationid = ?
		`
		if err := tx.Raw(remStockQuery, item.Productid, data.Tenantid, itemLocID).Scan(&remainingStock).Error; err == nil {
			if remainingStock <= 0 {
				tx.Table("productlocations").
					Where("productid = ? AND tenantid = ? AND locationid = ?", item.Productid, data.Tenantid, itemLocID).
					Update("status", "outofstock")
			}
		}
	}

	if err := r.updateSeqno(data.Tenantid, "ORD"); err != nil {
		log.Println("updateSeqno error:", err)
	}

	if err := tx.Commit().Error; err != nil {
		return models.Orders{}, err
	}

	var order models.Orders
	if err := r.db.Where("orderheaderid = ?", data.Orderheaderid).First(&order).Error; err != nil {
		return models.Orders{}, err
	}
	var items []models.OrderDetail
	if err := r.db.Table("orderdetails").Where("orderheaderid = ?", data.Orderheaderid).Find(&items).Error; err == nil {
		order.Items = items
	}

	return order, nil
}

func (r *orderRepository) getOrderDetailsByHeaderID(orderHeaderID int) ([]models.OrderDetails, float64, float64, error) {
	var details []models.OrderDetails
	var orderAmount float64
	var totalTaxAmount float64

	query := `
		SELECT a.orderdetailid,a.orderheaderid,a.tenantid,a.locationid,a.productid,a.productname,a.productdescription,a.supplyqty,a.balanceqty,a.orderqty,a.price,a.unitid,
			   a.unitname,a.productaddonid,a.addontypeid,a.productmapid,a.productvariantid,a.productaddondescription,a.discountid,a.discountname,a.discountcode,a.discountterms,
			   a.discountpercentage,a.discountamount,a.landingamount,a.taxpercentage,a.taxamount,a.productsumprice,a.itemstatus,a.delivered,COALESCE(b.orderamount, 0) as orderamount,
			   COALESCE(b.taxamount, 0) as totaltaxamount,c.productimage
		FROM orderdetails a
		LEFT JOIN orders b ON b.orderheaderid = a.orderheaderid
		LEFT JOIN products c ON a.productid = c.productid
		WHERE a.orderheaderid = ?`

	err := r.db.Raw(query, orderHeaderID).Scan(&details).Error
	if err != nil {
		return nil, 0, 0, err
	}

	if len(details) > 0 {
		orderAmount = details[0].Orderamount
		totalTaxAmount = details[0].Totaltaxamount
	}

	return details, orderAmount, totalTaxAmount, nil
}

func (r *orderRepository) GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) {

	orders := make([]models.CustomerOrder, 0)

	baseQuery := `
		SELECT a.orderheaderid, a.applocationid, a.tenantid, a.locationid, a.partnerid, a.configid,
		       a.categoryid, a.subcategoryid, a.moduleid, a.orderid, a.orderstatus, a.orderdate,
		       a.ordernotes, a.itemcount, a.deliverytime, a.pending, a.processing, a.ready,
		       a.delivered, a.cancelled, a.deliverycharge, a.kms, a.customerid, a.pickupaddress,
		       a.pickuplat, a.pickuplong, a.pickupcustomer, a.pickupcontactno, a.pickuplocation AS pickupsuburb,
		       a.pickupcity, a.deliveryid AS deliverycustomerid, a.deliveryaddress, a.deliverylat,
		       a.deliverylong, a.deliverytype, a.deliverycustomer, a.deliverycontactno,
		       a.deliverylocation AS deliverysuburb, a.deliverycity, a.paymenttype, a.smsdelivery, 
		       a.taxamount,
		       b.tenantname, b.tenanttoken, b.primarycontact AS tenantcontactno,
		       b.postcode AS tenantpostcode, b.suburb AS tenantsuburb, b.city AS tenantcity, b.registrationno,
		       c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode,
		       c.suburb AS locationsuburb, c.city AS locationcity,
		       d.locationname AS applocation
		FROM orders a
		INNER JOIN tenants b ON a.tenantid = b.tenantid
		INNER JOIN tenantlocations c ON a.locationid = c.locationid
		INNER JOIN app_location d ON a.applocationid = d.applocationid
		WHERE 1=1
	`

	params := []interface{}{}

	if customerID != "" {
		baseQuery += " AND a.customerid = ?"
		params = append(params, customerID)
	}

	if tenantID != "" && tenantID != "0" {
		baseQuery += " AND a.tenantid = ?"
		params = append(params, tenantID)
	}

	if moduleID != "" {
		baseQuery += " AND a.moduleid = ?"
		params = append(params, moduleID)
	}

	if fromDate != "" && toDate != "" {
		baseQuery += " AND DATE(a.orderdate) BETWEEN ? AND ?"
		params = append(params, fromDate, toDate)
	}

	if orderStatus != "" {
		baseQuery += " AND a.orderstatus = ?"
		params = append(params, orderStatus)
	}

	if keyword != "" {
		baseQuery += " AND EXISTS (SELECT 1 FROM orderdetails od WHERE od.orderheaderid = a.orderheaderid AND od.productname LIKE ?)"
		params = append(params, "%"+keyword+"%")
	}

	baseQuery += " ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?"
	params = append(params, pageSize, offset)

	if err := r.db.Raw(baseQuery, params...).Scan(&orders).Error; err != nil {
		return nil, err
	}

	// 🚀 NO LOOPING YET — Now fetch all details in one query
	return r.attachOrderDetails(orders)
}

func (r *orderRepository) attachOrderDetails(orders []models.CustomerOrder) ([]models.CustomerOrder, error) {

	if len(orders) == 0 {
		return orders, nil
	}

	orderIDs := make([]int, 0, len(orders))
	for _, o := range orders {
		orderIDs = append(orderIDs, o.Orderheaderid)
	}

	var details []models.OrderDetails
	query := `
		SELECT a.*, p.productimage
		FROM orderdetails a
		LEFT JOIN products p ON a.productid = p.productid
		WHERE a.orderheaderid IN ?
	`

	if err := r.db.Raw(query, orderIDs).Scan(&details).Error; err != nil {
		return nil, err
	}

	// map orderheaderid → []details
	detailMap := map[int][]models.OrderDetails{}
	for _, d := range details {
		detailMap[d.Orderheaderid] = append(detailMap[d.Orderheaderid], d)
	}

	for i := range orders {
		orders[i].OrderDetails = detailMap[orders[i].Orderheaderid]

		if len(orders[i].OrderDetails) > 0 {
			orders[i].Orderamount = orders[i].OrderDetails[0].Orderamount
			orders[i].Totaltaxamount = orders[i].OrderDetails[0].Totaltaxamount
		}
	}

	return orders, nil
}

func (r *orderRepository) GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) {

	var data []models.OrderInfo
	var params []interface{}

	offset := (input.Pageno - 1) * input.Pagesize

	// Start building SQL
	query := base + `
        WHERE a.tenantid = ?
          AND a.locationid = ?
    `
	params = append(params, input.Tenantid, input.Locationid)

	// Status filter
	if input.Status == "ongoing" {
		query += ` AND a.orderstatus IN ('pending','processing','ready')`
	} else if input.Status != "" {
		query += ` AND a.orderstatus = ?`
		params = append(params, input.Status)
	}

	// Applocation filter
	if input.Applocationid != 0 {
		query += ` AND a.applocationid = ?`
		params = append(params, input.Applocationid)
	}

	// Config filter
	if input.Configid != 0 {
		query += ` AND a.configid = ?`
		params = append(params, input.Configid)
	}

	// Date filter
	query += ` AND DATE(a.deliverytime) BETWEEN ? AND ?`
	params = append(params, input.Fromdate, input.ToDate)

	// Keyword filter
	if input.Keyword != "" {
		like := "%" + input.Keyword + "%"
		query += ` AND (
            a.pickupcustomer LIKE ? OR
            c.tenantname LIKE ? OR
            a.deliverycustomer LIKE ? OR
            a.pickupcontactno LIKE ? OR
            a.deliverycontactno LIKE ? OR
            a.orderid LIKE ?
        )`
		params = append(params, like, like, like, like, like, like)
	}

	// Sorting + pagination
	query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?`
	params = append(params, input.Pagesize, offset)

	fmt.Println("Executing Tenant+Location SQL:", query)

	result := r.db.Raw(query, params...).Find(&data)
	return data, result.Error
}

func (r *orderRepository) GetTimeSeries(tenantID, locationID int, granularity, fromDate, toDate string) ([]models.TimeSeriesData, error) {
	var data []models.TimeSeriesData

	var dateFunc, dateFuncO2 string
	switch granularity {
	case "month":
		dateFunc = "TO_CHAR(o.orderdate::date, 'YYYY-MM')"
		dateFuncO2 = "TO_CHAR(o2.orderdate::date, 'YYYY-MM')"
	case "year":
		dateFunc = "TO_CHAR(o.orderdate::date, 'YYYY')"
		dateFuncO2 = "TO_CHAR(o2.orderdate::date, 'YYYY')"
	case "day":
		fallthrough
	default:
		dateFunc = "TO_CHAR(o.orderdate::date, 'YYYY-MM-DD')"
		dateFuncO2 = "TO_CHAR(o2.orderdate::date, 'YYYY-MM-DD')"
	}

	var locFilter, dateFilter string
	var params []interface{}
	
	// subquery params
	params = append(params, tenantID)
	if locationID != 0 {
		locFilter = " AND o2.locationid = ?"
		params = append(params, locationID)
	}
	if fromDate != "" && toDate != "" {
		dateFilter = " AND o2.orderdate::date BETWEEN ? AND ?"
		params = append(params, fromDate, toDate)
	}
	
	// main query params
	params = append(params, tenantID)
	if locationID != 0 {
		params = append(params, locationID)
	}
	if fromDate != "" && toDate != "" {
		params = append(params, fromDate, toDate)
	}

	query := fmt.Sprintf(`
		SELECT 
			%s AS label,
			COUNT(o.orderheaderid) AS orders,
			COALESCE(SUM(o.orderamount), 0) AS revenue,
			COALESCE(SUM(CASE WHEN o.orderstatus = 'cancelled' THEN 1 ELSE 0 END), 0) AS cancelled,
			COALESCE(SUM(CASE WHEN o.orderstatus IN ('delivered', 'completed') THEN 1 ELSE 0 END), 0) AS delivered,
			COALESCE(MAX(sku_counts.activeskus), 0) AS activeskus
		FROM orders o
		LEFT JOIN (
			SELECT %s AS label, COUNT(DISTINCT d.productid) AS activeskus
			FROM orders o2
			JOIN orderdetails d ON o2.orderheaderid = d.orderheaderid
			WHERE o2.tenantid = ? AND o2.configid = 1
			%s
			%s
			GROUP BY %s
		) sku_counts ON %s = sku_counts.label
		WHERE o.tenantid = ? 
		  AND o.configid = 1
	`, dateFunc, dateFuncO2, locFilter, dateFilter, dateFuncO2, dateFunc)

	if locationID != 0 {
		query += " AND o.locationid = ?"
	}

	if fromDate != "" && toDate != "" {
		query += " AND o.orderdate::date BETWEEN ? AND ?"
	}

	query += fmt.Sprintf(" GROUP BY %s ORDER BY %s", dateFunc, dateFunc)

	if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil {
		return nil, err
	}
	
	if data == nil {
		data = []models.TimeSeriesData{}
	}

	return data, nil
}
