package repositories

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

	"github.com/jinzhu/copier"
	"gorm.io/gorm"
)

type DeliveriesRepository interface {
	CreateDeliveries(data []models.Deliveries) error
	UpdateDelivery(data models.UpdateDeliveryStatus) error
	GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error)
	GetDeliveryInsight(tid int) ([]models.OrderInsightv1, error)
	GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error)
	GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error)
	GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error)
	GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
	GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error)
	GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo
}

type deliveriesRepository struct {
	db *gorm.DB
}

func NewDeliveriesRepository(db *gorm.DB) DeliveriesRepository {
	return &deliveriesRepository{db: db}
}

const (
	core = `SELECT COUNT(*) AS total, 
	SUM(CASE WHEN orderstatus = 'pending' THEN 1 ELSE 0 END) AS pending,
	SUM(CASE WHEN orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted,
	SUM(CASE WHEN orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived,
	SUM(CASE WHEN orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked,
	SUM(CASE WHEN orderstatus = 'active' THEN 1 ELSE 0 END) AS active,
	SUM(CASE WHEN orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered,
	SUM(CASE WHEN orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS cancelled`

	reports = `SELECT b.tenantid,b.tenantname,
	COUNT(*) AS totalorders,
	SUM(CASE WHEN a.orderstatus = 'created' THEN 1 ELSE 0 END) AS orderscreated,
	SUM(CASE WHEN a.orderstatus = 'pending' THEN 1 ELSE 0 END) AS orderspending,
	SUM(CASE WHEN a.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS orderscancelled,
	SUM(CASE WHEN a.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS orderscompleted,
	SUM(CASE WHEN c.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveriespending,
	SUM(CASE WHEN c.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS deliveriescompleted,
	SUM(CASE WHEN c.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS deliveriescancelled,
	SUM(CASE WHEN c.paymenttype = 64 THEN c.deliveryamt ELSE 0 END) AS paylater,
	SUM(CASE WHEN c.paymenttype = 43 THEN c.deliveryamt ELSE 0 END) AS payondelivery,
	ROUND(SUM(c.kms), 2) AS kms,
	ROUND(SUM(c.actualkms), 2) AS actualkms,
	SUM(c.deliveryamt) AS charges
	FROM
	tenants b
	right JOIN orders a ON b.tenantid = a.tenantid
	right JOIN deliveries c ON a.orderheaderid = c.orderheaderid `

	Ridersummary = `SELECT a.userid, a.firstname, a.lastname,
    CONCAT(a.firstname, ' ', a.lastname) AS fullname,a.status,
    SUM(CASE WHEN b.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveries,
    SUM(CASE WHEN b.orderstatus = 'assigned' THEN 1 ELSE 0 END) AS assigned,
	SUM(CASE WHEN b.orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted,
    SUM(CASE WHEN b.orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived,
    SUM(CASE WHEN b.orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked,
    SUM(CASE WHEN b.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered,
    SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.actualkms ELSE 0 END) AS actualkms,
    SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.kms ELSE 0 END) AS kms,
	SUM(CASE WHEN b.paymenttype = 64 THEN b.deliveryamt ELSE 0 END) AS paylater, 
	SUM(CASE WHEN b.paymenttype = 43 THEN b.deliveryamt ELSE 0 END) AS payondelivery, 
    SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.deliveryamt ELSE 0 END) AS deliveryamt
	FROM app_users a
	LEFT JOIN deliveries b ON a.userid = b.userid`
)

const (
	deliveries = `SELECT distinct a.deliveryid,a.applocationid,f.locationname AS applocation,a.orderheaderid,a.configid,a.tenantid,a.partnerid,a.locationid,a.userid,a.categoryid,a.subcategoryid,a.moduleid,
	a.orderid,a.deliverydate,a.orderstatus,a.assigntime,a.starttime,a.arrivaltime,a.pickuptime,a.deliverytime,a.canceltime,
	a.customerid,a.pickupcustomer,a.pickupcontactno,a.pickuplocationid,a.pickupaddress,a.pickuplocation,a.pickuplat,a.pickuplon,
	a.deliverycustomerid,a.deliverycustomer,a.deliverycontactno,a.deliverylocationid,a.deliveryaddress,a.deliverylocation,
	a.droplat,a.droplon,a.deliverylat,a.deliverylong,
	a.riderslat,a.riderslon,a.deliveryamt,a.kms,a.actualkms,a.riderkms,a.deliverycharges,a.deliverytype,a.paymenttype,a.smsdelivery,
	a.expecteddeliverytime,a.profit,a.transitminutes,a.calculationdistancekm,
	a.notes,a.ordernotes,b.tenantname,b.primarycontact as tenantcontactno,b.tenanttoken,b.suburb as tenantsuburb,b.city as tenantcity,
	c.firstname AS ridername,c.userfcmtoken,e.locationname,e.suburb AS locationsuburb,e.contactno AS locationcontactno
	FROM deliveries a
	INNER JOIN tenants b ON a.tenantid=b.tenantid
	INNER JOIN app_users c ON a.userid=c.userid
	INNER JOIN tenantlocations e ON a.locationid=e.locationid
	INNER JOIN app_location f ON a.applocationid = f.applocationid
	INNER JOIN app_locationconfig g ON f.applocationid = g.applocationid`
)

func (r *deliveriesRepository) CreateDeliveries(data []models.Deliveries) error {
	var que []models.Deliveryqueues
	var ord models.Updateorderstatus

	tx := r.db.Begin()

	for i := range data {

		if err := tx.Create(&data[i]).Error; err != nil {
			tx.Rollback()
			return err
		}

		if err := copier.Copy(&que, &data[i]); err != nil {
			tx.Rollback()
			return err
		}

		if err := tx.Create(&que).Error; err != nil {
			tx.Rollback()
			return err
		}

		ord.Orderstatus = data[i].Orderstatus
		ord.Pending = data[i].Deliverydate

		if err := tx.Table("orders").
			Where("orderheaderid=?", data[i].Orderheaderid).
			Updates(&ord).Error; err != nil {
			tx.Rollback()
			return err
		}
	}

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

	return nil
}

func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) error {
	var ord models.Updateorderstatus
	var cloc models.Customerlocations

	if data.Deliveryid == 0 {
		return errors.New("deliveryid is required")
	}

	tx := r.db.Begin()
	if tx.Error != nil {
		return tx.Error
	}

	if err := tx.Table("deliveries").Where("deliveryid = ?", data.Deliveryid).Updates(&data).Error; err != nil {
		tx.Rollback()
		return err
	}

	// The parent order is resolved from the delivery row rather than taken from
	// the request. Every status branch below writes the order with
	// "WHERE orderheaderid = ?", and a client that omits orderheaderid made that
	// "WHERE orderheaderid = 0", matching nothing. GORM reports no error for an
	// update that affects no rows, so the handler still answered 201 Success
	// while the order silently kept its old status — 635 deliveries are marked
	// delivered against an order still reading pending because of this.
	//
	// deliveryid is the one field every caller must send (it is how the row
	// above is found), so deriving the link from it makes the sync independent
	// of how complete the client's payload is.
	orderHeaderID := data.Orderheaderid
	if orderHeaderID == 0 {
		if err := tx.Table("deliveries").
			Select("orderheaderid").
			Where("deliveryid = ?", data.Deliveryid).
			Scan(&orderHeaderID).Error; err != nil {
			tx.Rollback()
			return err
		}
	}
	if orderHeaderID == 0 {
		tx.Rollback()
		return fmt.Errorf("delivery %d has no order attached", data.Deliveryid)
	}

	// syncOrder applies the status to the parent order and fails loudly if the
	// row is not there, instead of reporting success for a write that landed
	// nowhere.
	syncOrder := func() error {
		res := tx.Table("orders").Where("orderheaderid = ?", orderHeaderID).Updates(&ord)
		if res.Error != nil {
			return res.Error
		}
		if res.RowsAffected == 0 {
			return fmt.Errorf("order %d not found for delivery %d", orderHeaderID, data.Deliveryid)
		}
		return nil
	}

	// The lifecycle timestamp mirrored onto the order. Clients frequently send
	// the status without one, and because Updates() skips zero-valued struct
	// fields the order's own column was left blank while its status moved on.
	stamp := func(supplied string) string {
		if strings.TrimSpace(supplied) != "" {
			return supplied
		}
		return time.Now().Format("2006-01-02 15:04:05")
	}

	switch data.Orderstatus {
	case "pending":
		ord.Orderstatus = data.Orderstatus
		ord.Pending = stamp(data.Assigntime)
		if err := syncOrder(); err != nil {
			tx.Rollback()
			return err
		}

	case "picked":
		if data.Pickuplocationid != 0 && data.Deliverytype != "B" {
			cloc.Latitude = data.Riderslat
			cloc.Longitude = data.Riderslon
			cloc.Address = data.Address
			cloc.Suburb = data.Suburb
			cloc.City = data.City
			cloc.State = data.State
			cloc.Postcode = data.Postcode

			if err := tx.Table("customerlocations").Where("locationid = ?", data.Pickuplocationid).Updates(&cloc).Error; err != nil {
				tx.Rollback()
				return err
			}
		}

	case "delivered":
		ord.Orderstatus = data.Orderstatus
		ord.Delivered = stamp(data.Deliverytime)
		if err := syncOrder(); err != nil {
			tx.Rollback()
			return err
		}

		if data.Deliverylocationid != 0 && data.Deliverytype != "B" {
			cloc.Latitude = data.Deliverylat
			cloc.Longitude = data.Deliverylong
			cloc.Address = data.Address
			cloc.Suburb = data.Suburb
			cloc.City = data.City
			cloc.State = data.State
			cloc.Postcode = data.Postcode

			if err := tx.Table("customerlocations").Where("locationid = ?", data.Deliverylocationid).Updates(&cloc).Error; err != nil {
				tx.Rollback()
				return err
			}
		}

	case "cancelled":
		ord.Orderstatus = data.Orderstatus
		ord.Cancelled = stamp(data.Canceltime)
		if err := syncOrder(); err != nil {
			tx.Rollback()
			return err
		}
	}

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

	return nil
}

func (r *deliveriesRepository) GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) {
	var data models.DeliverySummary
	var q1 string
	var params []interface{}

	// Base query
	q1 = core + " FROM deliveries WHERE configid = 1"

	// Filters (allow multiple together)
	if tid != 0 {
		q1 += " AND tenantid = ?"
		params = append(params, tid)
	}
	if pid != 0 {
		q1 += " AND partnerid = ?"
		params = append(params, pid)
	}
	if uid != 0 {
		q1 += " AND userid = ?"
		params = append(params, uid)
	}
	if aid != 0 {
		q1 += " AND applocationid = ?"
		params = append(params, aid)
	}
	if lid != 0 {
		q1 += " AND locationid = ?"
		params = append(params, lid)
	}

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

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

	print(q1)

	return data, nil
}

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

	// Base query for fetching active locations
	q1 := `
		SELECT DISTINCT l.locationid, l.locationname 
		FROM tenantlocations l 
		LEFT JOIN deliveries d ON l.locationid = d.locationid
		WHERE l.status = 'Active'
	`

	// Add tenant filter if provided
	if tenantID != 0 {
		q1 += " AND l.tenantid = ?"
		params = append(params, tenantID)
	}

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

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

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

	// Attach month-wise insights for each location
	for i := range locations {
		var orderMonths models.Ordermonths

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

		if err != nil {
			return nil, err
		}

		locations[i].Ordermonths = &orderMonths
	}

	return locations, nil
}

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

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

	// ✅ Only filter tenant locations in WHERE
	if tenantID != 0 {
		q1 += " WHERE b.tenantid = ?"
		params = append(params, tenantID)
	}

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

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

	return data, nil
}

func (r *deliveriesRepository) GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) {
	var data []models.ReportSummary
	var q1 string

	switch {
	case tid != 0:
		if fdate != "" && tdate != "" {
			q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) +
				` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname`
		} else {
			q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) + ` GROUP BY a.tenantid, b.tenantname`
		}

	case pid != 0:
		if fdate != "" && tdate != "" {
			q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) +
				` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname`
		} else {
			q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) + ` GROUP BY a.tenantid, b.tenantname`
		}

	case uid != 0:
		if fdate != "" && tdate != "" {
			q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) +
				` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname`
		} else {
			q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) + ` GROUP BY a.tenantid, b.tenantname`
		}

	case aid != 0:
		if fdate != "" && tdate != "" {
			q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) +
				` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname`
		} else {
			q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) + ` GROUP BY a.tenantid, b.tenantname`
		}

	default:
		if fdate != "" && tdate != "" {
			q1 = reports + ` WHERE a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname`
		} else {
			q1 = reports + ` GROUP BY a.tenantid, b.tenantname`
		}
	}

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

	return data, nil
}

func (r *deliveriesRepository) GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) {
	var data []models.Ridersummary
	var q1 string

	switch {
	case aid != 0:
		if fdate != "" && tdate != "" {
			q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) +
				` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname`
		} else {
			q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) +
				` GROUP BY a.userid, a.firstname, a.lastname`
		}

	case pid != 0:
		if fdate != "" && tdate != "" {
			q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) +
				` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname`
		} else {
			q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) +
				` GROUP BY a.userid, a.firstname, a.lastname`
		}

	case tid != 0:
		if fdate != "" && tdate != "" {
			q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) +
				` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname`
		} else {
			q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) +
				` GROUP BY a.userid, a.firstname, a.lastname`
		}

	default:
		if fdate != "" && tdate != "" {
			q1 = Ridersummary + ` WHERE a.configid=6 AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname`
		} else {
			q1 = Ridersummary + ` WHERE a.configid=6 GROUP BY a.userid, a.firstname, a.lastname`
		}
	}

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

	return data, nil
}

func (r *deliveriesRepository) GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	fmt.Println("🔍 Fetching Tenant Deliveries")

	var data []models.Deliveryinfo
	var queryBuilder strings.Builder
	var params []interface{}

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

	baseQuery := `
	SELECT a.*, b.tenantname, c.firstname AS ridername, c.contactno AS ridercontact
	FROM deliveries a
	JOIN tenants b ON a.tenantid = b.tenantid
	JOIN app_users c ON a.userid = c.userid
	WHERE a.tenantid = ?
	`
	queryBuilder.WriteString(baseQuery)
	params = append(params, input.Tenantid)

	if input.Status != "" {
		queryBuilder.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Fromdate != "" && input.ToDate != "" {
		queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	if input.Keyword != "" {
		k := "%" + input.Keyword + "%"
		queryBuilder.WriteString(`
			AND (
				a.pickupcustomer LIKE ? OR
				a.deliverycustomer LIKE ? OR
				a.pickupcontactno LIKE ? OR
				a.deliverycontactno LIKE ? OR
				a.orderid LIKE ?
			)
		`)
		for i := 0; i < 5; i++ {
			params = append(params, k)
		}
	}

	queryBuilder.WriteString(" ORDER BY a.deliveryid DESC")
	if input.Pagesize != 0 {
		queryBuilder.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	query := queryBuilder.String()
	fmt.Println("Final query:", query)
	fmt.Println("Params:", params)

	r.db.Raw(query, params...).Scan(&data)
	return data
}

func (r *deliveriesRepository) GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	fmt.Println("🔍 Fetching Partner Deliveries")

	var data []models.Deliveryinfo
	var queryBuilder strings.Builder
	var params []interface{}

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

	queryBuilder.WriteString(deliveries + ` WHERE a.partnerid = ?`)
	params = append(params, input.Partnerid)

	if input.Status != "" {
		queryBuilder.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Fromdate != "" && input.ToDate != "" {
		queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	queryBuilder.WriteString(" ORDER BY a.deliveryid DESC")

	if input.Pagesize > 0 {
		queryBuilder.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	finalQuery := queryBuilder.String()
	log.Println("Final Partner Query:", finalQuery, "Params:", params)

	r.db.Raw(finalQuery, params...).Scan(&data)
	return data
}

func (r *deliveriesRepository) GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	fmt.Println("🔍 Fetching Customer Deliveries")

	var data []models.Deliveryinfo
	var queryBuilder strings.Builder
	var params []interface{}

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

	queryBuilder.WriteString(deliveries + ` WHERE a.customerid = ?`)
	params = append(params, input.Customerid)

	if input.Partnerid != 0 {
		queryBuilder.WriteString(" AND a.partnerid = ?")
		params = append(params, input.Partnerid)
	}

	if input.Status != "" {
		queryBuilder.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Fromdate != "" && input.ToDate != "" {
		queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	if input.Keyword != "" {
		k := "%" + input.Keyword + "%"
		queryBuilder.WriteString(` AND (
			a.pickupcustomer LIKE ? OR 
			a.deliverycustomer LIKE ? OR 
			a.pickupcontactno LIKE ? OR 
			a.deliverycontactno LIKE ? OR 
			a.orderid LIKE ?
		)`)
		for i := 0; i < 5; i++ {
			params = append(params, k)
		}
	}

	queryBuilder.WriteString(" ORDER BY a.deliveryid DESC")
	if input.Pagesize != 0 {
		queryBuilder.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	query := queryBuilder.String()
	fmt.Println("Final query:", query)
	fmt.Println("Params:", params)

	r.db.Raw(query, params...).Scan(&data)
	return data
}

func (r *deliveriesRepository) GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	fmt.Println("Admin Deliveries")

	var data []models.Deliveryinfo
	var queryBuilder strings.Builder
	var params []interface{}

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

	queryBuilder.WriteString(`
		SELECT a.*, b.tenantname, c.firstname as ridername
		FROM deliveries a 
		JOIN tenants b ON a.tenantid = b.tenantid 
		JOIN app_users c ON c.userid = a.userid
		WHERE a.applocationid = ?
	`)
	params = append(params, input.Applocationid)

	if input.Status != "" {
		queryBuilder.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Fromdate != "" && input.ToDate != "" {
		queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	if input.Keyword != "" {
		k := "%" + input.Keyword + "%"
		queryBuilder.WriteString(`
			AND (
				a.pickupcustomer LIKE ? OR
				b.tenantname LIKE ? OR
				a.deliverycustomer LIKE ? OR
				a.pickupcontactno LIKE ? OR
				a.deliverycontactno LIKE ? OR
				a.orderid LIKE ?
			)
		`)
		for i := 0; i < 6; i++ {
			params = append(params, k)
		}
	}

	queryBuilder.WriteString(" ORDER BY a.deliveryid DESC")

	if input.Pagesize > 0 {
		queryBuilder.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	finalQuery := queryBuilder.String()

	log.Println("AdminDeliveries query:", finalQuery)
	log.Println("Params:", params)

	r.db.Raw(finalQuery, params...).Scan(&data)
	return data
}

func (r *deliveriesRepository) GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	var q1 string

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

	var data []models.Deliveryinfo

	if input.Status != "" {

		if input.Pagesize != 0 {

			fmt.Println("Rider Deliveriesv2")

			q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' 
		and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid 
		desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset)

		} else {

			fmt.Println("Rider Deliveriesv1")

			q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' 
		and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid asc`

		}

	} else {

		if input.Pagesize != 0 {
			fmt.Println("Rider Deliveriesv2")

			q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and 
		a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid 
		desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset)

		} else {

			fmt.Println("Rider Deliveriesv1")

			q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and 
			a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid desc`

		}

	}

	r.db.Raw(q1).Find(&data)

	return data
}

func (r *deliveriesRepository) GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	var data []models.Deliveryinfo

	fmt.Println("Appsuser Deliveries -new")

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

	q1 = deliveries + ` WHERE g.status = 'Active' AND g.userid=` + strconv.Itoa(input.Appuserid)

	if input.Status != "" {
		q1 += ` AND a.orderstatus='` + input.Status + `'`
	}

	if input.Fromdate != "" && input.ToDate != "" {
		q1 += ` AND a.deliverydate::date BETWEEN '` + input.Fromdate + `' AND '` + input.ToDate + `'`
	}

	if input.Keyword != "" {
		k := "%" + input.Keyword + "%"
		q1 += ` AND (
			a.pickupcustomer LIKE '` + k + `' OR
			b.tenantname LIKE '` + k + `' OR
			a.deliverycustomer LIKE '` + k + `' OR
			a.pickupcontactno LIKE '` + k + `' OR
			a.deliverycontactno LIKE '` + k + `' OR
			CAST(a.orderid AS TEXT) LIKE '` + k + `'
		)`
	}

	q1 += ` ORDER BY a.deliveryid DESC`
	if input.Pagesize != 0 {
		q1 += ` LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset)
	}

	log.Println("Final Query:", q1)

	r.db.Raw(q1).Find(&data)
	return data
}

func (r *deliveriesRepository) GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {
	var data []models.Deliveryinfo
	var queryBuilder strings.Builder
	var params []interface{}

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

	baseQuery := `
	SELECT a.*, b.tenantname, c.firstname as ridername, c.contactno as ridercontact,  d.customertoken
	FROM deliveries a
	JOIN tenants b ON a.tenantid = b.tenantid
	Join app_users c ON a.userid = c.userid
	JOIN customer d ON a.deliverycustomerid = d.customerid
	WHERE 1=1
	`

	queryBuilder.WriteString(baseQuery)

	if input.Fromdate != "" && input.ToDate != "" {
		queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	if input.Status != "" {
		queryBuilder.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Keyword != "" {
		k := "%" + input.Keyword + "%"
		queryBuilder.WriteString(`
			AND (
				a.pickupcustomer LIKE ? OR
				b.tenantname LIKE ? OR
				a.deliverycustomer LIKE ? OR
				a.pickupcontactno LIKE ? OR
				a.deliverycontactno LIKE ? OR
				a.orderid LIKE ?
			)
		`)
		for i := 0; i < 6; i++ {
			params = append(params, k)
		}
	}

	queryBuilder.WriteString(" ORDER BY a.deliveryid DESC")

	if input.Pagesize != 0 {
		queryBuilder.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	query := queryBuilder.String()

	log.Println("Final query:", query)
	log.Println("With params:", params)

	r.db.Raw(query, params...).Scan(&data)

	return data
}

func (r *deliveriesRepository) GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) {
	var data []models.Deliveryinfo
	var q1 string

	if fdate != "" && tdate != "" {
		q1 = deliveries + ` 
			WHERE a.orderstatus='pending' 
			AND a.userid=? 
			AND DATE(a.deliverydate) BETWEEN ? AND ? 
			ORDER BY a.deliveryid ASC`
		if err := r.db.Raw(q1, uid, fdate, tdate).Find(&data).Error; err != nil {
			return nil, err
		}
	} else {
		q1 = deliveries + ` 
			WHERE a.orderstatus='pending' 
			AND a.userid=? 
			AND DATE(a.deliverydate)=CURDATE() 
			ORDER BY a.deliveryid ASC`
		if err := r.db.Raw(q1, uid).Find(&data).Error; err != nil {
			return nil, err
		}
	}

	return data, nil
}

func (r *deliveriesRepository) GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo {

	var data []models.Deliveryinfo
	var params []interface{}
	var qb strings.Builder

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

	qb.WriteString(`
        SELECT a.*, b.tenantname, c.firstname AS ridername, 
               c.contactno AS ridercontact, d.customertoken
        FROM deliveries a
        JOIN tenants b ON a.tenantid = b.tenantid
        JOIN app_users c ON a.userid = c.userid
        JOIN customers d ON a.deliverycustomerid = d.customerid
        WHERE a.tenantid = ? AND a.locationid = ?
    `)

	params = append(params, input.Tenantid, input.Locationid)

	if input.Fromdate != "" && input.ToDate != "" {
		qb.WriteString(" AND DATE(a.deliverydate) BETWEEN ? AND ?")
		params = append(params, input.Fromdate, input.ToDate)
	}

	if input.Status != "" {
		qb.WriteString(" AND a.orderstatus = ?")
		params = append(params, input.Status)
	}

	if input.Keyword != "" {
		like := "%" + input.Keyword + "%"
		qb.WriteString(`
            AND (
                a.pickupcustomer LIKE ? OR
                b.tenantname LIKE ? OR
                a.deliverycustomer LIKE ? OR  m 
                a.pickupcontactno LIKE ? OR
                a.deliverycontactno LIKE ? OR
                a.orderid LIKE ?
            )
        `)
		for i := 0; i < 6; i++ {
			params = append(params, like)
		}
	}

	qb.WriteString(" ORDER BY a.deliveryid DESC")

	if input.Pagesize != 0 {
		qb.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, input.Pagesize, offset)
	}

	query := qb.String()

	log.Println("Tenant+Location Query:", query)
	log.Println("Params:", params)

	r.db.Raw(query, params...).Scan(&data)

	return data
}
