package repositories

import (
	"fmt"
	"strings"

	"nearle/models"

	"gorm.io/gorm"
)

type UserRepository interface {
	GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error)
	GetUserByID(uid int) (models.UserInfo, error)
	Login(user models.User) (models.UserInfo, error)
	FindUserID(authname, contactno string, configid int) (int, error)
	GetTenantUserByID(userid int) (models.TenantUserInfo, error)
	UpdateStaff(user models.User) error
	GetUserByAuthname(authname string, configid int) (int, string, string)
	GetUserByContactNo(contactno string, configid int) (int, string, string)
	UpdateFCMToken(userid int, token string) error
	GetTenantUserById(userid int) models.TenantUserInfo
	CreateUser(user models.User) (int, error)
	GetUserById(uid int) (models.UserInfo, error)
	GetUserLogin(field, value string, configid int) (int, string, string, int)
	UpdateUserFcmToken(uid int, token string) error
	GetLocationStatus(locationid int) string
	DeleteUser(userid int) error
}

type userRepository struct {
	db *gorm.DB
}

func NewUserRepository(db *gorm.DB) UserRepository {
	return &userRepository{db: db}
}

func (r *userRepository) GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error) {
	var users []models.UserInfo
	var params []interface{}
	var queryBuilder strings.Builder

	offset := (pageno - 1) * pagesize

	queryBuilder.WriteString(`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.address, a.suburb, a.city, a.state, a.postcode,
		a.userfcmtoken, a.pin, a.deviceid, a.devicetype, a.tenantid, a.status, a.shiftid,
		a.applocationid, b.locationname AS applocation, b.latitude AS applatitude, concat(c.starttime, ' - ', c.endtime) as shiftname,
		b.longitude AS applongitude, b.radius AS appradius
		FROM app_users a
		LEFT JOIN app_location b ON a.applocationid = b.applocationid
		LEFT JOIN ridershifts c ON a.shiftid = c.shiftid
		WHERE 1=1`)

	if roleID != 0 {
		queryBuilder.WriteString(" AND a.roleid = ?")
		params = append(params, roleID)
	}

	if tenantID != 0 {
		queryBuilder.WriteString(" AND a.tenantid = ?")
		params = append(params, tenantID)
	}

	if keyword != "" {
		queryBuilder.WriteString(` AND (
			LOWER(a.firstname) LIKE ? OR 
			LOWER(a.contactno) LIKE ? OR 
			LOWER(a.suburb) LIKE ?
		)`)
		search := "%" + strings.ToLower(keyword) + "%"
		params = append(params, search, search, search)
	}

	queryBuilder.WriteString(" ORDER BY a.userid DESC LIMIT ? OFFSET ?")
	params = append(params, pagesize, offset)

	print(queryBuilder.String())

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

	for i := range users {
		users[i].Status = strings.ToLower(users[i].Status)
	}

	return users, nil
}

func (r *userRepository) GetUserByID(uid int) (models.UserInfo, error) {
	var user models.UserInfo

	q := `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.password,a.address,a.suburb,a.city,a.state,a.postcode,
			a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.shiftid,a.locationid,
			a.applocationid,b.locationname as applocation,b.latitude as applatitude,b.longitude as applongitude, b.radius as appradius,
			concat(c.starttime, ' - ', c.endtime) as shiftname, a.status
		FROM app_users a
		INNER JOIN app_location b ON a.applocationid = b.applocationid
		LEFT JOIN ridershifts c ON a.shiftid = c.shiftid
		WHERE a.userid = ?`

	if err := r.db.Raw(q, uid).Scan(&user).Error; err != nil {
		return models.UserInfo{}, err
	}

	user.Status = strings.ToLower(user.Status)

	return user, nil
}

func (r *userRepository) Login(user models.User) (models.UserInfo, error) {
	var uid int
	var userInfo models.UserInfo

	var q string
	if user.Authname != "" {
		q = `SELECT a.userid FROM app_users a 
			  WHERE a.authname = ? AND a.configid = ?`
		if err := r.db.Raw(q, user.Authname, user.Configid).Scan(&uid).Error; err != nil {
			return models.UserInfo{}, err
		}
	} else {
		q = `SELECT a.userid FROM app_users a 
			  WHERE a.contactno = ? AND a.configid = ?`
		if err := r.db.Raw(q, user.Contactno, user.Configid).Scan(&uid).Error; err != nil {
			return models.UserInfo{}, err
		}
	}

	if uid == 0 {
		return models.UserInfo{}, gorm.ErrRecordNotFound
	}

	// ✅ Update FCM token in app_users table if provided
	if user.Userfcmtoken != "" {
		if err := r.db.Table("app_users").
			Where("userid = ?", uid).
			Update("userfcmtoken", user.Userfcmtoken).Error; err != nil {
			return models.UserInfo{}, err
		}
	}

	userInfo, err := r.GetUserByID(uid)
	if err != nil {
		return models.UserInfo{}, err
	}

	return userInfo, nil
}


func (r *userRepository) FindUserID(authname, contactno string, configid int) (int, error) {
	var uid int
	var query string

	if authname != "" {
		query = `SELECT a.userid FROM app_users a WHERE a.authname = ? AND a.configid = ?`
		if err := r.db.Raw(query, authname, configid).Scan(&uid).Error; err != nil {
			return 0, err
		}
	} else {
		query = `SELECT a.userid FROM app_users a WHERE a.contactno = ? AND a.configid = ?`
		if err := r.db.Raw(query, contactno, configid).Scan(&uid).Error; err != nil {
			return 0, err
		}
	}
	return uid, nil
}

func (r *userRepository) GetTenantUserByID(userid int) (models.TenantUserInfo, error) {
	var info models.TenantUserInfo
	query := `SELECT a.userid, a.authname, a.contactno, a.tenantid, t.tenantname
	          FROM app_users a
	          LEFT JOIN tenants t ON a.tenantid = t.tenantid
	          WHERE a.userid = ?`

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

func (r *userRepository) UpdateStaff(user models.User) error {
	return r.db.Table("app_users").Where("userid = ?", user.Userid).Updates(&user).Error
}

func (r *userRepository) GetUserByAuthname(authname string, configid int) (int, string, string) {
	var uid int
	var password, status string
	query := `SELECT userid, password, status FROM app_users WHERE authname = ? AND configid = ?`
	r.db.Raw(query, authname, configid).Row().Scan(&uid, &password, &status)
	return uid, password, status
}

func (r *userRepository) GetUserByContactNo(contactno string, configid int) (int, string, string) {
	var uid int
	var password, status string
	query := `SELECT userid, password, status FROM app_users WHERE contactno = ? AND configid = ?`
	r.db.Raw(query, contactno, configid).Row().Scan(&uid, &password, &status)
	return uid, password, status
}

func (r *userRepository) UpdateFCMToken(userid int, token string) error {
	query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?`
	return r.db.Exec(query, token, userid).Error
}

func (r *userRepository) GetTenantUserById(userid int) models.TenantUserInfo {
	var info models.TenantUserInfo

	// app_location is LEFT JOINed (not INNER) so a user with no matching
	// tenant/applocation row — e.g. a super admin, tenantid=0 — still comes
	// back with their own fields (issuperadmin included) instead of the
	// whole query silently returning zero rows.
	query := `
		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.locationid,a.applocationid,
			a.issuperadmin,
			b.partnerid,b.moduleid,b.categoryid as categoryid,b.subcategoryid as subcategoryid,
			b.applocationid,b.tenantname,b.address as tenantaddress,b.state as tenantstate,b.city as tenantcity,
			b.postcode as tenantpostcode,b.latitude as tenantlat,b.longitude as tenantlong,c.locationname AS applocation,
			c.latitude as applatitude,c.longitude as applongitude,c.radius as appradius, d.categoryname, e.locationname
			from app_users a
			LEFT JOIN tenants b ON a.tenantid=b.tenantid
			LEFT JOIN app_location c on c.applocationid=b.applocationid
			LEFT JOIN app_category d ON b.categoryid=d.categoryid
			LEFT JOIN tenantlocations e ON a.locationid=e.locationid
		WHERE a.userid = ?
	`

	r.db.Raw(query, userid).Scan(&info)
	print(query)
	return info
}

func (r *userRepository) CreateUser(user models.User) (int, error) {
	tx := r.db.Begin()

	if err := tx.Table("app_users").Create(&user).Error; err != nil {
		tx.Rollback()
		return 0, err
	}

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

	return user.Userid, nil
}

func (r *userRepository) GetUserById(uid int) (models.UserInfo, error) {
	var user 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.password,a.address,a.suburb,a.city,a.state,a.postcode,
			a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.shiftid,
			a.applocationid,b.locationname as applocation,b.latitude as applatitude,b.longitude as applongitude, b.radius as appradius , concat(c.starttime, ' - ', c.endtime) as shiftname, a.status                      
			FROM app_users a
			INNER JOIN app_location b on a.applocationid=b.applocationid
			LEFT JOIN ridershifts c ON a.shiftid = c.shiftid
			WHERE a.userid= ?`

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

	user.Status = strings.ToLower(user.Status)

	return user, nil
}

func (r *userRepository) GetUserLogin(field, value string, configid int) (int, string, string, int) {
	var uid, roleid int
	var password, status string

	query := fmt.Sprintf(`
		SELECT userid, password, status, roleid
		FROM app_users
		WHERE %s = ? AND configid = ?`, field)

	r.db.Raw(query, value, configid).Row().Scan(&uid, &password, &status, &roleid)

	return uid, password, status, roleid
}

func (r *userRepository) UpdateUserFcmToken(userid int, fcmToken string) error {
	query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?`
	return r.db.Exec(query, fcmToken, userid).Error
}

func (r *userRepository) GetLocationStatus(locationid int) string {
	var status string
	query := `SELECT status FROM tenantlocations WHERE locationid = ?`
	r.db.Raw(query, locationid).Row().Scan(&status)
	return status
}

func (r *userRepository) DeleteUser(userid int) error {
	return r.db.Table("app_users").Where("userid = ?", userid).Delete(&models.User{}).Error
}


