package domain

import (
	"context"
	"encoding/json"
	"errors"
	"nearle/db"
	"nearle/models"
	"nearle/utils"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func GetActiveRiders(partnerid, aid, uid int, keyword string, pageno, pagesize int) []models.RiderInfo {

	data := make([]models.RiderInfo, 0) // ✅ IMPORTANT
	var qb strings.Builder
	var params []interface{}

	offset := 0
	if pageno > 0 && pagesize > 0 {
		offset = (pageno - 1) * pagesize
	}

	qb.WriteString(`
		WITH latest_logs AS (
			SELECT *,
			       ROW_NUMBER() OVER (PARTITION BY userid ORDER BY logdate DESC) AS rn
			FROM riderlogs
			WHERE logdate >= CURRENT_DATE
			  AND logdate < CURRENT_DATE + INTERVAL '1 day'
		)
		SELECT DISTINCT
			a.userid,a.firstname,a.lastname,CONCAT(a.firstname,' ',a.lastname) AS fullname,a.contactno,a.userfcmtoken,a.partnerid,a.applocationid,c.identificationno,
			c.registrationno,c.licenseno,c.vehiclename,c.vehicleno,d.shiftid,d.starttime,d.endtime,d.shifthours,d.basefare,d.fuelcharge,e.logdate,e.login,e.logout,
			e.workhours,e.shorthours,e.logstatus,e.latitude,e.longitude,a.status,f.locationname AS applocation
		FROM app_users a
		JOIN ridersettings c ON c.userid = a.userid
		JOIN ridershifts d ON d.shiftid = c.shiftid
		JOIN latest_logs e ON e.userid = a.userid AND e.rn = 1
		JOIN app_location f ON f.applocationid = a.applocationid
		
		WHERE a.configid = 6
		  AND a.status = 'Active'
		  AND e.logstatus = 0
		  AND EXISTS (
			  SELECT 1
			  FROM app_userpools p
			  WHERE p.userid = a.userid
			    AND p.onduty = 1
		  )
	`)

	if aid != 0 {
		qb.WriteString(" AND a.applocationid = ?")
		params = append(params, aid)
	}
	if partnerid != 0 {
		qb.WriteString(" AND a.partnerid = ?")
		params = append(params, partnerid)
	}
	if uid != 0 {
		qb.WriteString(" AND a.userid = ?")
		params = append(params, uid)
	}

	if keyword != "" {
		k := "%" + strings.ToLower(keyword) + "%"
		qb.WriteString(`
			AND (
				LOWER(a.firstname) LIKE ? OR
				LOWER(a.lastname) LIKE ? OR
				LOWER(a.contactno) LIKE ? OR
				LOWER(a.userid::text) LIKE ?
			)
		`)
		for i := 0; i < 4; i++ {
			params = append(params, k)
		}
	}

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

	if pageno > 0 && pagesize > 0 {
		qb.WriteString(" LIMIT ? OFFSET ?")
		params = append(params, pagesize, offset)
	}

	utils.Logger.Debugf("ActiveRiders SQL: %s", qb.String())
	utils.Logger.Debugf("Params: %v", params)

	print(qb.String())
	print(params)

	db.DB.Raw(qb.String(), params...).Scan(&data)
	return data
}

func GetActiveRidersv2(partnerid, aid, uid, tenantid int) []models.RiderInfo {
	var data []models.RiderInfo
	var q1 string

	const riders = `SELECT DISTINCT a.*,a.userid, a.firstname, a.lastname,
		CONCAT(a.firstname, ' ', a.lastname) AS fullname,
		a.contactno, a.userfcmtoken, a.partnerid, a.applocationid,
		c.identificationno, c.registrationno, c.licenseno, c.vehiclename, c.vehicleno,
		d.shiftid, d.starttime, d.endtime, d.shifthours, d.basefare, d.fuelcharge,
		e.logdate, e.login, e.logout, e.workhours, e.shorthours, e.logstatus, a.status,
		f.locationname AS applocation
	FROM 
		app_users a
	LEFT JOIN ridersettings c ON a.userid = c.userid
	INNER JOIN ridershifts d ON a.shiftid = d.shiftid	
	INNER JOIN (
    SELECT r1.*
    FROM riderlogs r1
    INNER JOIN (
					SELECT userid, MAX(logdate) AS max_logdate
					FROM riderlogs
					WHERE logdate::date = CURRENT_DATE
					GROUP BY userid
				) r2 ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
	WHERE r1.logstatus = 0
	) e ON a.userid = e.userid
	INNER JOIN app_location f ON a.applocationid = f.applocationid
	INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid`

	if aid != 0 {
		q1 = riders + " WHERE a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.applocationid = ?"
		db.DB.Raw(q1, aid).Find(&data)
	} else if partnerid != 0 {
		q1 = riders + " WHERE a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.partnerid = ?"
		db.DB.Raw(q1, partnerid).Find(&data)
	} else if tenantid != 0 {
		q1 = riders + " WHERE a.status = 'Active' AND a.configid = 6 AND a.status = 'Active' AND a.tenantid = ?"
		db.DB.Raw(q1, tenantid).Find(&data)
	} else {
		q1 = riders + " WHERE g.status = 'Active' AND a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND g.userid = ?"
		db.DB.Raw(q1, uid).Find(&data)
	}

	utils.Logger.Debugf("Query executed for riders")

	return data
}

func GetRiderShifts(aid int) []models.Ridershifts {

	var data []models.Ridershifts

	q1 := "Select a.*,concat(a.starttime,'-',a.endtime) as shiftname from ridershifts a where a.applocationid=?"

	db.DB.Raw(q1, aid).Find(&data)

	return data
}

func GetRiderPricing(aid int) []models.RiderPricing {

	var data []models.RiderPricing

	q1 := `SELECT a.shiftid, a.basefare, a.additionalcharges, a.fuelcharge, a.applocationid, b.locationname from ridershifts a
 LEFT JOIN app_location b ON a.applocationid = b.applocationid`

	if aid > 0 {
		q1 += ` WHERE a.applocationid=` + strconv.Itoa(aid)
	}

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

	return data
}

func GetRiderPool(partnerid int) []models.RiderPool {

	var data []models.RiderPool

	q1 := `SELECT b.poolid,a.userid,a.firstname,a.lastname,CONCAT(a.firstname,' ',a.lastname) AS fullname,
				a.contactno,a.userfcmtoken,a.partnerid,a.applocationid,
				c.identificationno,c.registrationno,c.licenseno,c.vehiclename,c.vehicleno,
				d.shiftid,d.starttime,d.endtime,d.shifthours,d.basefare,d.fuelcharge,a.status
				FROM 
				app_users a
				INNER JOIN app_userpools b ON a.userid=b.userid
				INNER JOIN ridersettings c ON a.userid=c.userid
				INNER JOIN ridershifts d ON c.shiftid=d.shiftid
				WHERE a.configid=6 and a.status='Active' and a.partnerid=?`

	db.DB.Raw(q1, partnerid).Find(&data)

	return data
}

func GetRiderInfo(userid int) models.RiderInfo {

	var data models.RiderInfo

	q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname,
			a.partnerid,a.configid,a.contactno,
			a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,
			b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
			c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status,
			a.applocationid,d.locationname as applocation,d.logseconds
			FROM app_users a
			INNER JOIN ridersettings b ON a.userid=b.userid
			INNER JOIN ridershifts c on b.shiftid=c.shiftid
			INNER JOIN app_location d on a.applocationid=d.applocationid
			WHERE a.userid=?`

	utils.Logger.Debugf("Query executed for userid: %d", userid)

	db.DB.Raw(q1, userid).Find(&data)

	return data
}

func GetRiderInfov2(userid int) models.RiderInfo {

	var data models.RiderInfo

	q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname,
			a.partnerid,a.configid,a.contactno,
			a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,a.tenantid,
			b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
			c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status,
			a.applocationid,d.locationname as applocation,d.logseconds
			FROM app_users a
			LEFT JOIN ridersettings b ON a.userid=b.userid
			INNER JOIN ridershifts c ON a.shiftid=c.shiftid
			INNER JOIN app_location d on a.applocationid=d.applocationid
			WHERE a.userid=?`

	utils.Logger.Debugf("Query executed for userid: %d", userid)

	db.DB.Raw(q1, userid).Find(&data)

	return data
}

func GetRiderDetails(userid int) models.RiderDetails {

	var data models.RiderDetails

	q1 := `SELECT a.*,b.*,c.* from app_users a
		INNER JOIN ridersettings b ON a.userid=b.userid
		LEFT JOIN ridershifts  c ON b.shiftid=c.shiftid
		WHERE a.userid=?`

	db.DB.Raw(q1, userid).Find(&data)

	return data
}

func GetAllRiders(aid, pid, uid int, status, keyword string, pageno, pagesize int) []models.RiderInfo {
	var data []models.RiderInfo
	var q1 string
	var params []interface{}

	const riders = `SELECT DISTINCT a.userid,a.firstname,a.lastname, 
		CONCAT(a.firstname,' ',a.lastname) AS username,
		a.partnerid,a.configid,a.contactno,
		a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,a.name,a.capacity,a.rangekm,a.batterypercentage,a.type,
		b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
		c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,
		c.fuelcharge,a.status,a.applocationid,d.locationname AS applocation
	FROM app_users a
	INNER JOIN ridersettings b ON a.userid=b.userid
	INNER JOIN ridershifts c ON b.shiftid=c.shiftid
	INNER JOIN app_location d ON a.applocationid=d.applocationid
	INNER JOIN app_locationconfig e ON a.applocationid=e.applocationid`

	conditions := []string{"e.status='Active'"}

	if aid != 0 {
		conditions = append(conditions, "a.applocationid=?")
		params = append(params, aid)
	}

	if pid != 0 {
		conditions = append(conditions, "a.partnerid=?")
		params = append(params, pid)
	}

	if uid != 0 {
		conditions = append(conditions, "a.userid=?")
		params = append(params, uid)
	}

	if status != "" {
		conditions = append(conditions, "a.status=?")
		params = append(params, status)
	}

	if keyword != "" {
		k := "%" + strings.ToLower(keyword) + "%"
		conditions = append(conditions,
			`(LOWER(a.firstname) LIKE ? 
			OR LOWER(a.lastname) LIKE ? 
			OR LOWER(a.contactno) LIKE ? 
			OR LOWER(a.userid::text) LIKE ?)`)
		params = append(params, k, k, k, k)
	}

	q1 = riders + " WHERE " + strings.Join(conditions, " AND ") + " ORDER BY a.userid DESC"

	// Apply pagination only if pagesize > 0
	if pagesize > 0 {
		offset := (pageno - 1) * pagesize
		q1 += " LIMIT ? OFFSET ?"
		params = append(params, pagesize, offset)
	}

	db.DB.Raw(q1, params...).Find(&data)

	return data
}

func GetPartners(aid, pid int) []models.Partnerinfo {

	// onduty := c.Query("onduty")

	var data []models.Partnerinfo
	var q1 string

	if pid != 0 {

		q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
			 contactno,address,suburb,state,city,partnerimage from partnerinfo where  status='Active' and partnerid=?`
		db.DB.Raw(q1, pid).Find(&data)

	} else if aid != 0 {

		q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
		      contactno,address,suburb,state,city,partnerimage from partnerinfo where status='Active' and applocationid=?`
		db.DB.Raw(q1, aid).Find(&data)

	} else {

		q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
		      contactno,address,suburb,state,city,partnerimage from partnerinfo where status='Active'`
		db.DB.Raw(q1).Find(&data)

	}

	return data

}

func GetLocationConfig(uid, cid int) []models.Locationconfigs {

	var data []models.Locationconfigs
	q1 := `SELECT Distinct a.applocationid,a.locationname FROM app_location a 
	inner join app_locationconfig b ON a.applocationid=b.applocationid 
	INNER JOIN app_users c ON a.applocationid=c.applocationid
    INNER JOIN app_roles d ON d.roleid=c.roleid
	WHERE b.status='Active' and b.userid=? ORDER BY applocationid ASC`
	db.DB.Raw(q1, uid).Find(&data)
	return data
}

func GetPartnerUsers(aid int) []models.UserInfo {

	var data []models.UserInfo

	q1 := `SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno,a.firstname,a.lastname,concat(a.firstname,' ',a.lastname) as fullname,
		a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.applocationid,a.partnerid
		from app_users a where configid=5 and a.applocationid=?`

	db.DB.Raw(q1, aid).Find(&data)

	return data

}

func GetAdmintoken(pid, uid, tid, aid int) models.AdminToken {

	var data models.AdminToken
	var q1 string

	if tid != 0 && pid != 0 {

		q1 = `SELECT a.userid,a.userfcmtoken, b.closetime,b.opentime,c.tenantid  
		FROM app_users a 
		INNER JOIN partnerinfo b ON a.partnerid=b.partnerid 
		INNER JOIN tenants c ON a.userid=c.partneruserid
		WHERE b.partnerid=? AND c.tenantid=?`
		db.DB.Raw(q1, pid, tid).Find(&data)

	} else if aid != 0 {

		q1 = `SELECT distinct a.userid,a.userfcmtoken,b.notify,b.applocationid,c.opentime,c.closetime FROM app_users a 
			  INNER JOIN app_locationconfig b ON a.userid=b.userid 
			  INNER JOiN app_location c on a.applocationid= c.applocationid
		      WHERE b.notify='true' AND b.applocationid=?`
		db.DB.Raw(q1, aid).Find(&data)

	}

	return data

}

func CreateRider(data models.Rider) error {

	var userpools models.Userpools

	tx := db.DB.Begin()

	// ✅ FIX 1: Proper upsert for app_users
	t1 := tx.Table("app_users").Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "email"}}, // REQUIRED
		DoUpdates: clause.AssignmentColumns([]string{
			"firstname",
			"lastname",
			"contactno",
			"address",
			"city",
			"state",
		}),
	}).Create(&data)

	if t1.Error != nil {
		tx.Rollback()
		return t1.Error
	}

	// ✅ FIX 2: Manually insert ridersettings (prevents ON CONFLICT error)
	data.Ridersettings.Userid = data.Userid
	data.Ridersettings.Partnerid = data.Partnerid

	tRs := tx.Table("ridersettings").
		Omit("riderid"). // 🚀 THIS FIXES YOUR ISSUE
		Create(&data.Ridersettings)
	if tRs.Error != nil {
		tx.Rollback()
		return tRs.Error
	}

	// ✅ Existing logic (unchanged)
	userpools.Userid = data.Userid
	userpools.Partnerid = data.Partnerid
	userpools.Vehicleid = data.Ridersettings.Vehicleid

	t2 := tx.Table("app_userpools").Create(&userpools)
	if t2.Error != nil {
		tx.Rollback()
		return t2.Error
	}

	return tx.Commit().Error
}

func UpdateRiderSettings(data models.Rider) error {

	tx := db.DB.Begin()

	t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

	t2 := tx.Table("ridersettings").Where("userid=?", data.Userid).Updates(&data.Ridersettings)
	if t2.Error != nil {

		tx.Rollback()
		return t2.Error
	}

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

		return err

	}
	return nil

}

// func UpdateRiderInfo(data models.Rider) error {

// 	tx := db.DB.Begin()

// 	t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
// 	if t1.Error != nil {

// 		tx.Rollback()
// 		return t1.Error
// 	}

// 	err := tx.Commit().Error
// 	if err != nil {

// 		return err

// 	}
// 	return nil

// }

func UpdateRiderInfo(data models.Rider) error {

	tx := db.DB.Begin()

	t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

	t2 := tx.Table("ridersettings").Where("userid=?", data.Userid).Updates(&data.Ridersettings)
	if t2.Error != nil {

		tx.Rollback()
		return t2.Error
	}

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

		return err

	}
	return nil

}

func CreateRiderShift(data models.Ridershifts) error {

	tx := db.DB.Begin()

	t1 := tx.Table("ridershifts").Create(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

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

		return err

	}
	return nil

}

func CreateRiderLog(data models.Riderlogs) error {

	tx := db.DB.Begin()

	t1 := tx.Table("riderlogs").Create(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

	t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 1)
	if t2.Error != nil {

		tx.Rollback()
		return t2.Error
	}

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

		return err

	}
	return nil

}

func GetBreaklog(bid int) models.Riderbreaks {

	var data models.Riderbreaks

	q1 := "select * from riderbreaks where breakid=?"

	db.DB.Raw(q1, bid).Find(&data)

	return data

}

func CreateBreakLog(data models.Riderbreaks) (int, error) {

	tx := db.DB.Begin()

	t1 := tx.Table("riderbreaks").Create(&data)
	if t1.Error != nil {

		tx.Rollback()
		return 0, t1.Error
	}

	t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 0)
	if t2.Error != nil {

		tx.Rollback()
		return 0, t2.Error
	}

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

		return 0, err

	}
	return data.Breakid, nil

}

func UpdateBreakLog(data models.Riderbreaks) error {

	tx := db.DB.Begin()

	t1 := tx.Table("riderbreaks").Where("breakid=?", data.Breakid).Updates(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

	t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 1)
	if t2.Error != nil {

		tx.Rollback()
		return t2.Error
	}

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

		return err

	}
	return nil

}

func UpdateRiderLog(data models.Riderlogs) error {

	tx := db.DB.Begin()

	t1 := tx.Table("riderlogs").Where("userid=?", data.Userid).Updates(&data)
	if t1.Error != nil {

		tx.Rollback()
		return t1.Error
	}

	t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 0)
	if t2.Error != nil {

		tx.Rollback()
		return t2.Error
	}

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

		return err

	}
	return nil

}

func GetRiderLog(uid int) (models.Riderlogs, error) {

	var data models.Riderlogs

	q1 := `SELECT a.*,b.onduty from riderlogs a 
			INNER JOIN app_userpools b ON a.userid=b.userid WHERE a.userid=? and a.logdate::date=CURRENT_DATE`

	err := db.DB.Raw(q1, uid).Find(&data).Error
	if err != nil {

		return data, err

	}

	return data, nil

}

func GetRiderLogs(pid, aid int, fdate, tdate, keyword string) []models.RiderlogDetails {

	var data []models.RiderlogDetails
	var q1 string
	var params []interface{}

	if pid != 0 {
		if fdate != "" && tdate != "" {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE a.partnerid = ?
                AND a.logdate::date between ? and ?`
			params = append(params, pid, fdate, tdate)
		} else {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE a.partnerid = ?
                AND a.logdate::date=CURRENT_DATE`
			params = append(params, pid)
		}

	} else if aid != 0 {

		if fdate != "" && tdate != "" {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE b.applocationid = ?
                AND a.logdate::date between ? and ?`
			params = append(params, aid, fdate, tdate)
		} else {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE b.applocationid = ?
                AND a.logdate::date=CURRENT_DATE`
			params = append(params, aid)
		}

	} else {

		if fdate != "" && tdate != "" {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE a.logdate::date between ? and ?`
			params = append(params, fdate, tdate)
		} else {
			q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
                (SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
                FROM riderlogs a
                INNER JOIN app_users b ON a.userid=b.userid
                WHERE a.logdate::date=CURRENT_DATE`
		}
	}

	if keyword != "" {
		k := "%" + strings.ToLower(keyword) + "%"
		q1 += `
            AND (
                LOWER(b.firstname) LIKE ? OR
                LOWER(b.lastname) LIKE ? OR
                LOWER(CONCAT(b.firstname,' ',b.lastname)) LIKE ? OR
                b.userid LIKE ?
            )`
		params = append(params, k, k, k, keyword)
	}

	// Finish query
	q1 += " ORDER BY logid ASC"

	utils.Logger.Debugf("Query executed for RiderLogs")

	db.DB.Raw(q1, params...).Find(&data)
	return data
}

func GetRiderOrderCount(uid int) (int, error) {

	var dc int

	// q1 = `SELECT COUNT(*) as deliverycount FROM deliveryqueues WHERE queuestatus=0 and userid=` + strconv.Itoa(uid)
	q1 := `SELECT COUNT(*) as deliverycount FROM deliveries 
		WHERE orderstatus IN('pending','accepted','arrived','picked') and userid=?`

	db.DB.Raw(q1, uid).Find(&dc)

	return dc, nil

}

func CreateBreakLogv1(data models.Riderbreaks) (int, error) {
	if db.Rdb == nil {
		return 0, errors.New("Redis client not initialized")
	}

	id, err := db.Rdb.Incr(db.Ctx, "riderbreaks:counter").Result()
	if err != nil {
		utils.Logger.Errorf("Redis INCR error: %v", err)
		return 0, errors.New("failed to generate break ID")
	}
	data.Breakid = int(id)

	jsonData, err := json.Marshal(data)
	if err != nil {
		utils.Logger.Errorf("JSON marshal error: %v", err)
		return 0, errors.New("failed to serialize break data")
	}

	if err := db.Rdb.RPush(db.Ctx, "riderbreaks", jsonData).Err(); err != nil {
		utils.Logger.Errorf("Redis RPush error: %v", err)
		return 0, errors.New("failed to store break in Redis")
	}

	tx := db.DB.Begin()
	if err := updateOnduty(tx, data.Userid); err != nil {
		tx.Rollback()
		return 0, err
	}
	if err := tx.Commit().Error; err != nil {
		utils.Logger.Errorf("MySQL commit error: %v", err)
		return 0, errors.New("failed to update onduty")
	}

	return data.Breakid, nil
}

func updateOnduty(tx *gorm.DB, userid int) error {
	// Try update
	result := tx.Table("app_userpools").
		Where("userid = ?", userid).
		UpdateColumn("onduty", 0)

	if result.Error != nil {
		return result.Error
	}

	// if result.RowsAffected == 0 {
	//     // INSERT instead of update
	//     err := tx.Table("app_userpools").Create(map[string]interface{}{
	//         "userid": userid,
	//         "onduty": 0,
	//     }).Error

	//     if err != nil {
	//         return errors.New("failed to insert onduty row: " + err.Error())
	//     }
	// }

	return nil
}

func GetBreaklogv1(breakID int) models.Riderbreaks {
	if db.Rdb == nil {
		return models.Riderbreaks{}
	}

	vals, err := db.Rdb.LRange(db.Ctx, "riderbreaks", 0, -1).Result()
	if err != nil {
		utils.Logger.Errorf("Redis LRange error: %v", err)
		return models.Riderbreaks{}
	}

	var data models.Riderbreaks
	for _, v := range vals {
		var item models.Riderbreaks
		if err := json.Unmarshal([]byte(v), &item); err != nil {
			continue
		}
		if item.Breakid == breakID {
			data = item
			break
		}
	}

	return data
}

func UpdateBreakLogv1(data models.Riderbreaks) error {
	key := "riderbreaks"

	values, err := db.Rdb.LRange(db.Ctx, key, 0, -1).Result()
	if err != nil {
		return errors.New("failed to read Redis list: " + err.Error())
	}
	if len(values) == 0 {
		return errors.New("no data found in Redis list " + key)
	}

	var foundIndex int = -1
	for i, val := range values {
		var rb models.Riderbreaks
		if err := json.Unmarshal([]byte(val), &rb); err != nil {
			continue
		}
		if rb.Breakid == data.Breakid {
			foundIndex = i
			break
		}
	}

	if foundIndex == -1 {
		return errors.New("Break ID " + strconv.Itoa(data.Breakid) + " not found in Redis")
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		return errors.New("Redis marshal failed: " + err.Error())
	}

	if err := db.Rdb.LSet(db.Ctx, key, int64(foundIndex), jsonData).Err(); err != nil {
		return errors.New("Redis LSET failed: " + err.Error())
	}

	tx := db.DB.Begin()
	t2 := tx.Table("app_userpools").
		Where("userid = ?", data.Userid).
		UpdateColumn("onduty", 1)
	if t2.Error != nil {
		tx.Rollback()
		return t2.Error
	}

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

	return nil
}

func GetRiderWeeklyKms(uid int) (*models.RiderWeeklyKms, error) {
	var result []models.RiderKms
	var overallKms float64

	// 🔥 SAFE + POSTGRES COMPATIBLE QUERY
	q1 := `
		SELECT 
			TRIM(TO_CHAR(deliverydate, 'Dy')) AS day,
			ROUND(
				COALESCE(SUM(NULLIF(riderkms, '')::numeric), 0),
			2) AS kms
		FROM deliveries
		WHERE userid = ?
		  AND deliverydate >= CURRENT_DATE - INTERVAL '7 days'
		GROUP BY EXTRACT(DOW FROM deliverydate), TRIM(TO_CHAR(deliverydate, 'Dy'))
		ORDER BY EXTRACT(DOW FROM deliverydate);
	`

	q2 := `
		SELECT 
			ROUND(
				COALESCE(SUM(NULLIF(riderkms, '')::numeric), 0),
			2) AS overallkms
		FROM deliveries
		WHERE userid = ?;
	`

	// Execute queries
	if err := db.DB.Raw(q1, uid).Scan(&result).Error; err != nil {
		return nil, err
	}

	if err := db.DB.Raw(q2, uid).Scan(&overallKms).Error; err != nil {
		return nil, err
	}

	// Map results
	dayMap := make(map[string]float64)
	for _, r := range result {
		dayMap[r.Day] = r.Kms
	}

	// Ensure all 7 days
	allDays := []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

	var final []models.RiderKms
	var totalKms float64

	for _, d := range allDays {
		kms := dayMap[d]
		final = append(final, models.RiderKms{
			Day: d,
			Kms: kms,
		})
		totalKms += kms
	}

	return &models.RiderWeeklyKms{
		Details:    final,
		TotalKms:   totalKms,
		OverallKms: overallKms,
	}, nil
}

func CreateRiderLogv1(data models.Riderlogs) error {
	ctx := context.Background()

	pong, err := db.Rdb.Ping(ctx).Result()
	utils.Logger.Infof("[Redis] Ping: %s Error: %v", pong, err)
	if err != nil {
		return errors.New("redis connection error: " + err.Error())
	}

	tx := db.DB.Begin()

	if err := tx.Table("riderlogs").Create(&data).Error; err != nil {
		tx.Rollback()
		return errors.New("mysql insert error (riderlogs): " + err.Error())
	}

	utils.Logger.Infof("[MySQL] Generated logid: %d", data.Logid)

	if err := tx.Table("app_userpools").
		Where("userid = ?", data.Userid).
		UpdateColumn("onduty", 1).Error; err != nil {
		tx.Rollback()
		return errors.New("mysql update error (onduty): " + err.Error())
	}

	if err := tx.Commit().Error; err != nil {
		return errors.New("mysql commit error: " + err.Error())
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		return errors.New("failed to marshal redis data: " + err.Error())
	}

	key := "riderlogs"
	pushResult := db.Rdb.RPush(ctx, key, jsonData)
	if pushResult.Err() != nil {
		return errors.New("failed to push rider log to redis: " + pushResult.Err().Error())
	}

	insertedCount, _ := pushResult.Result()
	utils.Logger.Infof("[Redis] RPush OK — key=%s, list length now=%d", key, insertedCount)

	val, err := db.Rdb.LRange(ctx, key, -1, -1).Result()
	if err != nil {
		utils.Logger.Errorf("[Redis] LRange check error: %v", err)
	} else {
		utils.Logger.Infof("[Redis] Last inserted value: %v", val)
	}

	return nil
}

func UpdateRiderLogv1(data models.Riderlogs) error {
	ctx := context.Background()
	key := "riderlogs"

	logs, err := db.Rdb.LRange(ctx, key, 0, -1).Result()
	if err != nil {
		return errors.New("failed to fetch logs from redis: " + err.Error())
	}

	found := false

	for i, item := range logs {
		var existing models.Riderlogs
		if err := json.Unmarshal([]byte(item), &existing); err != nil {
			continue
		}

		if existing.Userid == data.Userid {
			found = true

			existingVal := reflect.ValueOf(&existing).Elem()
			dataVal := reflect.ValueOf(&data).Elem()

			for i := 0; i < dataVal.NumField(); i++ {
				field := dataVal.Field(i)
				if !isZero(field) { // only update if non-zero
					existingVal.Field(i).Set(field)
				}
			}

			updatedJSON, _ := json.Marshal(existing)
			if err := db.Rdb.LSet(ctx, key, int64(i), updatedJSON).Err(); err != nil {
				return errors.New("failed to update redis: " + err.Error())
			}
			break
		}
	}

	if !found {
		return errors.New("rider log for userid " + strconv.Itoa(data.Userid) + " not found in redis")
	}

	tx := db.DB.Begin()
	t1 := tx.Table("app_userpools").
		Where("userid = ?", data.Userid).
		UpdateColumn("onduty", 0)
	if t1.Error != nil {
		tx.Rollback()
		return errors.New("mysql update error: " + t1.Error.Error())
	}

	if err := tx.Commit().Error; err != nil {
		return errors.New("mysql commit error: " + err.Error())
	}

	return nil
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.String:
		return v.Len() == 0
	case reflect.Int, reflect.Int64, reflect.Float64:
		return v.Int() == 0
	case reflect.Ptr, reflect.Interface:
		return v.IsNil()
	default:
		return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
	}
}

func CreateRiderSupport(data models.RiderSupport) error {

	tx := db.DB.Begin()

	t1 := tx.Table("ridersupport").Create(&data)
	if t1.Error != nil {
		tx.Rollback()
		return t1.Error
	}

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

	return nil
}

func GetRiderSupport(uid int) ([]models.RiderSupport, error) {

	var data []models.RiderSupport

	q1 := "SELECT * FROM ridersupport WHERE userid=? ORDER BY ridersupportid DESC"

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

	return data, nil
}

func GetRiderLogsv1(fromdate, todate, keyword string, partnerid int) ([]models.Riderlogsv1, error) {
	ctx := context.Background()
	results := []models.Riderlogsv1{}

	redisList, err := db.Rdb.LRange(ctx, "riderlogs", 0, -1).Result()
	if err != nil {
		return nil, errors.New("redis lrange error: " + err.Error())
	}

	if len(redisList) == 0 {
		return results, nil
	}

	from, _ := time.Parse("2006-01-02", fromdate)
	to, _ := time.Parse("2006-01-02", todate)
	to = to.Add(24 * time.Hour)

	keyword = strings.ToLower(keyword)

	last := make(map[int]models.Riderlogsv1)

	for _, item := range redisList {

		idx := strings.Index(item, `"logdate":"`)
		if idx == -1 {
			continue
		}

		start := idx + len(`"logdate":"`)
		end := start + 19
		if end > len(item) {
			continue
		}

		logDateStr := item[start:end]
		logTime, err := time.Parse("2006-01-02 15:04:05", logDateStr)
		if err != nil {
			continue
		}

		if logTime.Before(from) || logTime.After(to) {
			continue
		}

		var log models.Riderlogsv1
		if err := json.Unmarshal([]byte(item), &log); err != nil {
			continue
		}

		if partnerid > 0 && log.Partnerid != partnerid {
			continue
		}

		if keyword != "" {
			if !strings.Contains(strings.ToLower(log.Firstname), keyword) &&
				!strings.Contains(strings.ToLower(log.Lastname), keyword) &&
				!strings.Contains(strings.ToLower(log.Username), keyword) {
				continue
			}
		}

		if prev, ok := last[log.Userid]; ok {
			prevTime, _ := time.Parse("2006-01-02 15:04:05", prev.Logdate)
			if logTime.After(prevTime) {
				last[log.Userid] = log
			}
		} else {
			last[log.Userid] = log
		}
	}

	for _, v := range last {
		results = append(results, v)
	}

	return results, nil
}

func GetUserByID(userid int) (models.User, error) {

	var user models.User

	q := `SELECT userid, firstname, lastname FROM app_users WHERE userid=? LIMIT 1`

	err := db.DB.Raw(q, userid).Scan(&user).Error
	return user, err
}

func GetUserCached(userid int) (models.User, error) {
	ctx := context.Background()
	var user models.User

	key := "user:info:" + strconv.Itoa(userid)

	val, err := db.Rdb.Get(ctx, key).Result()
	if err == nil {
		json.Unmarshal([]byte(val), &user)
		return user, nil
	}

	q := `SELECT * 
          FROM app_users 
          WHERE userid=? LIMIT 1`

	if err := db.DB.Raw(q, userid).Scan(&user).Error; err != nil {
		return user, err
	}

	b, _ := json.Marshal(user)
	db.Rdb.Set(ctx, key, b, 6*time.Hour)

	return user, nil
}

func GetAllRidersSummary(aid, pid, uid int, keyword string) models.RidersSummary {
	var summary models.RidersSummary
	var conditions []string

	const baseQry = `
		SELECT
			COUNT(DISTINCT CASE WHEN a.status='Active' THEN a.userid END) AS active,
			COUNT(DISTINCT CASE WHEN a.status='Inactive' THEN a.userid END) AS inactive,
			COUNT(DISTINCT a.userid) AS total
		FROM app_users a
		INNER JOIN ridersettings b ON a.userid=b.userid
		INNER JOIN ridershifts c ON b.shiftid=c.shiftid
		INNER JOIN app_location d ON a.applocationid=d.applocationid
		INNER JOIN app_locationconfig e 
			ON a.applocationid=e.applocationid AND e.status='Active'
		WHERE 1=1
	`

	var params []interface{}

	if aid != 0 {
		conditions = append(conditions, "a.applocationid=?")
		params = append(params, aid)
	}

	if pid != 0 {
		conditions = append(conditions, "a.partnerid=?")
		params = append(params, pid)
	}

	if uid != 0 {
		conditions = append(conditions, "a.userid=?")
		params = append(params, uid)
	}

	if keyword != "" {
		k := "%" + strings.ToLower(keyword) + "%"
		conditions = append(conditions, "(LOWER(a.firstname) LIKE ? OR LOWER(a.lastname) LIKE ? OR LOWER(a.contactno) LIKE ? OR LOWER(CAST(a.userid AS CHAR)) LIKE ?)")
		params = append(params, k, k, k, k)
	}

	q := baseQry
	if len(conditions) > 0 {
		q += " AND " + strings.Join(conditions, " AND ")
	}

	utils.Logger.Debugf("SUMMARY QUERY Executed")

	db.DB.Raw(q, params...).Scan(&summary)

	return summary
}

type ActiveRidersSummary struct {
	Total int `json:"total"`
}

func GetActiveRidersSummary(partnerid, aid, uid int, keyword string) ActiveRidersSummary {
	var summary ActiveRidersSummary
	var conditions []string

	const baseQry = `
		SELECT 
			COUNT(DISTINCT a.userid) AS total
		FROM app_users a
		INNER JOIN app_userpools b ON a.userid = b.userid
		INNER JOIN ridersettings c ON a.userid = c.userid
		INNER JOIN ridershifts d ON c.shiftid = d.shiftid
		INNER JOIN (
			SELECT r1.*
			FROM riderlogs r1
			INNER JOIN (
				SELECT userid, MAX(logdate) AS max_logdate
				FROM riderlogs
				WHERE logdate::date = CURRENT_DATE
				GROUP BY userid
			) r2 
			ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
		) e ON a.userid = e.userid
		INNER JOIN app_location f ON a.applocationid = f.applocationid
		INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid
		WHERE 1=1
	`

	conditions = append(conditions,
		"a.configid = 6",
		"a.status = 'Active'",
		"b.onduty = 1",
		"e.logdate::date = CURRENT_DATE",
		"e.logstatus = 0",
	)

	var params []interface{}

	if aid != 0 {
		conditions = append(conditions, "a.applocationid = ?")
		params = append(params, aid)
	}
	if partnerid != 0 {
		conditions = append(conditions, "a.partnerid = ?")
		params = append(params, partnerid)
	}
	if uid != 0 {
		conditions = append(conditions, "a.userid = ?")
		params = append(params, uid)
	}

	if keyword != "" {
		k := "%" + strings.ToLower(keyword) + "%"
		conditions = append(conditions, "(LOWER(a.firstname) LIKE ? OR LOWER(a.lastname) LIKE ? OR LOWER(a.contactno) LIKE ? OR LOWER(CAST(a.userid AS CHAR)) LIKE ?)")
		params = append(params, k, k, k, k)
	}

	q := baseQry + " AND " + strings.Join(conditions, " AND ")

	utils.Logger.Debugf("ACTIVE SUMMARY QUERY Executed")

	db.DB.Raw(q, params...).Scan(&summary)
	return summary
}
