package repository

import (
	"bpmsapi/dto"
	"bpmsapi/entity"
	"errors"
	"strings"

	//"fmt"
	"log"

	//"bpmsapi/dto"
	"github.com/jmoiron/sqlx"
)

type UserActions interface {
	FetchUserbyMobno(mobno string) (*entity.User, error)
	InsertNewCustomer(tx *sqlx.Tx, input entity.Signupuser) (string, error)
	ProblemCategoryFetch(input entity.FetchProblemCategoryInput) ([]entity.FetchProblemCategory, error)
	FetchAllUsers(input entity.GetAllUsersInput) (*entity.GetAllUsers, error)
	UserApproval(input entity.ValidateCustomer) (bool, error)
	UpdateProfile(tx *sqlx.Tx, input entity.UpdateProfileInput, image string) (bool, error)
}

type UserRepository struct {
	db *sqlx.DB
}

// auth-jwt
func UserRepositoryInit(db *sqlx.DB) *UserRepository {
	return &UserRepository{
		db: db,
	}
}

func (userRepo *UserRepository) Begin() (*sqlx.Tx, error) {
	tx, err := userRepo.db.Beginx()
	if err != nil {
		return nil, err
	}
	return tx, nil
}
func (userRepo *UserRepository) FetchUserbyMobno(mobileno string) (*entity.User, error) {
	userResponse := entity.User{}
	err := userRepo.db.Get(&userResponse, "SELECT u.User_Id, u.User_Name, u.User_Image, u.Address_Type, u.Apartment_Id, u.User_Mobno, u.User_Email, u.Apartment_Purchase_Date, u.Alternate_No, u.Referral_Code, u.Address_Id, u.Account_Status, u.Approved_By, GROUP_CONCAT(ba.Apartment_Id) as Mapped_Apartments FROM BPMS_USER u LEFT OUTER JOIN BPMS_USER_APARTMENT_MAPPING ba on u.User_Id = ba.User_Id where u.User_Mobno = ? group by u.User_Id", mobileno)
	if err != nil {
		log.Println("UserRepository: FetchUserbyMobno: Input: ", mobileno, " Error ", err.Error())
		return nil, err
	} else {
		return &userResponse, nil
	}

}

func (userRepo *UserRepository) InsertNewCustomer(tx *sqlx.Tx, input entity.Signupuser) (string, error) {
	log.Println("email_id", input.User_Emailid)
	log.Println("door_no", input.Doorno)
	query := "INSERT INTO `BPMS_USER`(`User_Name`, `Address_Type`, `Apartment_Id`, `User_Mobno`, `User_Email`, `Apartment_Purchase_Date`, `Alternate_No`, `Address_Id`, `Account_Status`, `Notification_Token`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,?)"
	var userid int64
	lastinsertid, err := tx.Exec(query, input.User_Name, input.Address_Type, input.Apartment_Id, input.User_Mobno, input.User_Emailid, input.Apartment_Purchase_Date, input.Alternate_No, input.Address_Id, input.Account_Status, input.Notification_Token)
	if err != nil {
		tx.Rollback()
		log.Println("UserRepository: InsertNewCustomer: insertinguserinfo : name: ", input.User_Name, "Error ", err.Error())
		return "Signup not successful because of error", err
	} else {
		var errIns error
		userid, errIns = lastinsertid.LastInsertId()
		if errIns != nil {
			tx.Rollback()
			log.Println("UserRepository: InsertNewCustomer: gettinguserid : name: ", input.User_Name, "Error ", errIns.Error())
			return "Signup not successful because of error", errIns
		}
	}

	mappingQuery := "INSERT INTO `BPMS_USER_APARTMENT_MAPPING`( `User_Id`, `User_Type`, `Apartment_Id`, `Door_No`, `Block`, `GSTN`, `TIN`, `PAN`, `Billing_Address_Id`) VALUES (?,?,?,?,?,?,?,?,?)"
	_, errIn := tx.Exec(mappingQuery, userid, input.User_Type, input.Apartment_Id, input.Doorno, input.Block, input.GSTN, input.TIN, input.PAN, input.Billing_Address_Id)
	if errIn != nil {
		tx.Rollback()
		log.Println("UserRepository: InsertNewCustomer: name: ", input.User_Name, "Error ", errIn.Error())
		return "Signup not successful because of error", errIn
	}
	log.Println("UserRepository: InsertNewCustomer: name: ", input.User_Name, " userid:", userid)
	return "Signup successful", nil

}

func (userRepo *UserRepository) UserApproval(input entity.ValidateCustomer) (bool, error) {
	query := "UPDATE `BPMS_USER` SET `Account_Status`=:Account_Status, `Reject_Reason`=:Reject_Reason,`Approved_By`=:Emp_Id WHERE `User_Mobno`=:User_Mobno"
	res, errv := userRepo.db.NamedExec(query, input)
	if errv != nil {
		log.Println("UserRepository: UserApproval: Input: ", input.User_Mobno, "Error ", errv.Error())
		return false, errors.New(dto.ErrorMessageUserApproval)
	}
	rowAffected, erra := res.RowsAffected()
	if erra == nil {
		if rowAffected > 0 {
			return true, nil
		} else {
			log.Println("UserRepository: UserApproval: Input: ", input.User_Mobno, "Error ", dto.ErrorMessageNoUpdation)
			return false, errors.New(dto.ErrorMessageNoUpdation)
		}
	} else {
		log.Println("UserRepository: UserApproval: Input: ", input.User_Mobno, "Error ", erra.Error())
		return false, erra
	}
}

func (userRepo *UserRepository) ProblemCategoryFetch(input entity.FetchProblemCategoryInput) (problemcategory []entity.FetchProblemCategory, err error) {

	err = userRepo.db.Select(&problemcategory, "SELECT bst.Service_Id, bst.Service_Name, bs.Service_Description, bs.Service_Image_Large, bs.Service_Image_Small, bpct.Problem_Name, bpc.Problem_Id FROM BPMS_PROBLEM_CATEGORIES_TRANSLATIONS bpct INNER JOIN BPMS_PROBLEM_CATEGORIES bpc ON bpct.Problem_Id = bpc.Problem_Id LEFT JOIN BPMS_SERVICE_TRANSLATIONS bst ON bpc.Service_Id = bst.Service_Id and bst.Language_Code =? INNER JOIN BPMS_SERVICE bs ON bst.Service_Id=bs.Service_Id WHERE bpct.Language_Code =?", input.Language_Code, input.Language_Code)
	if err != nil {
		log.Println("UserRepository: ProblemCategorySearch: gettingproblemdetails: Error: ", err.Error())
		return nil, err
	} else {
		return problemcategory, nil
	}

}

func (userRepo *UserRepository) FetchAllUsers(input entity.GetAllUsersInput) (*entity.GetAllUsers, error) {
	// var pageLimit int = 15
	allusers := entity.GetAllUsers{}
	var err, errCount error
	alluser := []entity.GetAllUser{}
	count := allusers.Total_User

	if input.Apartment_Id == 0 {
		if strings.ToLower(input.Status) == "new" {
			err = userRepo.db.Select(&alluser, "SELECT bu.User_Id, bu.User_Name, bu.User_Email, bu.User_Mobno, bu.Alternate_No, bu.User_Image, bat.Apartment_Name, buam.Door_No, buam.Block, c.City_Name, s.State_Name, bu.Account_Status, bu.Apartment_Purchase_Date, buam.GSTN, buam.TIN, buam.PAN, buam.Billing_Address_Id, a.Address_Line1 as Billing_Address_Line1, a.Address_Line2 as Billing_Address_Line2, a.Area_Name as Billing_Area_Name, a.City as Billing_City, a.State as Billing_State, a.Pincode as Billing_Pincode, a.Country as Billing_Country, a.Latitude as Billing_Latitude, a.Longitude as Billing_Longitude, bu.Created_Timestamp FROM BPMS_USER bu LEFT JOIN BPMS_APARTMENT_TRANSLATIONS bat ON bu.Apartment_Id = bat.Apartment_Id AND bat.Language_Code =? INNER JOIN BPMS_USER_APARTMENT_MAPPING buam ON bu.Apartment_Id = buam.Apartment_Id and bu.User_Id = buam.User_Id INNER JOIN BPMS_APARTMENT ba ON bu.Apartment_Id = ba.Apartment_Id INNER JOIN BPMS_STATE_TRANSLATIONS s ON ba.State = s.State_Id AND s.Language_Code = ? INNER JOIN BPMS_CITY_TRANSLATIONS c ON ba.City =c.City_Id and c.Language_Code = ? LEFT JOIN BPMS_ADDRESS a ON buam.Billing_Address_Id = a.Address_Id  WHERE bu.Account_Status = 'Under Verification' limit ? offset ?  ", input.Language_Code, input.Language_Code, input.Language_Code, input.PageLimit, (input.Page-1)*input.PageLimit)

			errCount = userRepo.db.Get(&count, "SELECT COUNT(User_Id) AS Total_User FROM BPMS_USER WHERE Account_Status ='Under Verification'")

			if errCount != nil {
				log.Println("UserRepository: FetchAllUsers: fetchingalluserscount: Error: ", errCount.Error())
				return nil, errCount
			}

		} else if strings.ToLower(input.Status) == "all" {

			err = userRepo.db.Select(&alluser, "SELECT bu.User_Id, bu.User_Name, bu.User_Email, bu.User_Mobno, bu.Alternate_No, bu.User_Image, bat.Apartment_Name, buam.Door_No, buam.Block, c.City_Name, s.State_Name, bu.Account_Status, bu.Apartment_Purchase_Date, buam.GSTN, buam.TIN, buam.PAN, buam.Billing_Address_Id, a.Address_Line1 as Billing_Address_Line1, a.Address_Line2 as Billing_Address_Line2, a.Area_Name as Billing_Area_Name, a.City as Billing_City, a.State as Billing_State, a.Pincode as Billing_Pincode, a.Country as Billing_Country, a.Latitude as Billing_Latitude, a.Longitude as Billing_Longitude, bu.Created_Timestamp, IFNULL(COUNT(bsr.Srq_Id), 0) AS Total_Srq  FROM BPMS_USER bu LEFT JOIN BPMS_APARTMENT_TRANSLATIONS bat ON bu.Apartment_Id = bat.Apartment_Id AND bat.Language_Code =? INNER JOIN BPMS_USER_APARTMENT_MAPPING buam ON bu.Apartment_Id = buam.Apartment_Id and bu.User_Id = buam.User_Id INNER JOIN BPMS_APARTMENT ba ON bu.Apartment_Id = ba.Apartment_Id INNER JOIN BPMS_STATE_TRANSLATIONS s ON ba.State = s.State_Id AND s.Language_Code = ? INNER JOIN BPMS_CITY_TRANSLATIONS c ON ba.City =c.City_Id and c.Language_Code = ? LEFT JOIN BPMS_ADDRESS a ON buam.Billing_Address_Id = a.Address_Id LEFT JOIN BPMS_SERVICE_REQUEST bsr ON bu.User_Id = bsr.User_Id AND bsr.Srq_Stage_8 = 0 WHERE bu.Account_Status !='Rejected' GROUP BY bu.User_Id limit ? offset ? ", input.Language_Code, input.Language_Code, input.Language_Code, input.PageLimit, int((input.Page-1)*input.PageLimit))

			errCount = userRepo.db.Get(&count, "SELECT COUNT(User_Id) AS Total_User FROM BPMS_USER WHERE Account_Status !='Rejected'")
			if errCount != nil {
				log.Println("UserRepository: FetchAllUsers: fetchingalluserscount: Error:", errCount.Error())
				return nil, errCount
			}

		}

	} else {
		if strings.ToLower(input.Status) == "new" {
			err = userRepo.db.Select(&alluser, "SELECT bu.User_Id, bu.User_Name, bu.User_Email, bu.User_Mobno, bu.Alternate_No, bu.User_Image, bat.Apartment_Name, buam.Door_No, buam.Block, c.City_Name, s.State_Name, bu.Account_Status, bu.Apartment_Purchase_Date, buam.GSTN, buam.TIN, buam.PAN, buam.Billing_Address_Id, a.Address_Line1 as Billing_Address_Line1, a.Address_Line2 as Billing_Address_Line2, a.Area_Name as Billing_Area_Name, a.City as Billing_City, a.State as Billing_State, a.Pincode as Billing_Pincode, a.Country as Billing_Country, a.Latitude as Billing_Latitude, a.Longitude as Billing_Longitude, bu.Created_Timestamp FROM BPMS_USER bu LEFT JOIN BPMS_APARTMENT_TRANSLATIONS bat ON bu.Apartment_Id = bat.Apartment_Id AND bat.Language_Code =? INNER JOIN BPMS_USER_APARTMENT_MAPPING buam ON bu.Apartment_Id = buam.Apartment_Id and bu.User_Id = buam.User_Id INNER JOIN BPMS_APARTMENT ba ON bu.Apartment_Id = ba.Apartment_Id INNER JOIN BPMS_STATE_TRANSLATIONS s ON ba.State = s.State_Id AND s.Language_Code = ? INNER JOIN BPMS_CITY_TRANSLATIONS c ON ba.City =c.City_Id and c.Language_Code = ? LEFT JOIN BPMS_ADDRESS a ON buam.Billing_Address_Id = a.Address_Id WHERE bu.Account_Status = 'Under Verification' AND bu.Apartment_Id= ? limit ? offset ? ", input.Language_Code, input.Language_Code, input.Language_Code, input.Apartment_Id, input.PageLimit, (input.Page-1)*input.PageLimit)
			errCount = userRepo.db.Get(&count, "SELECT COUNT(User_Id) AS Total_User FROM BPMS_USER WHERE Account_Status ='Under Verification' AND Apartment_Id= ?", input.Apartment_Id)
			if errCount != nil {
				log.Println("UserRepository: FetchAllUsers: fetchingalluserscount: Input: Apartment_Id:", input.Apartment_Id, "  Error:", errCount.Error())
				return nil, errCount
			}

		} else if input.Status == "all" {

			err = userRepo.db.Select(&alluser, "SELECT bu.User_Id, bu.User_Name, bu.User_Email, bu.User_Mobno, bu.Alternate_No, bu.User_Image, bat.Apartment_Name, buam.Door_No, buam.Block, c.City_Name, s.State_Name, bu.Account_Status, bu.Apartment_Purchase_Date, buam.GSTN, buam.TIN, buam.PAN, buam.Billing_Address_Id, a.Address_Line1 as Billing_Address_Line1, a.Address_Line2 as Billing_Address_Line2, a.Area_Name as Billing_Area_Name, a.City as Billing_City, a.State as Billing_State, a.Pincode as Billing_Pincode, a.Country as Billing_Country, a.Latitude as Billing_Latitude, a.Longitude as Billing_Longitude, bu.Created_Timestamp, IFNULL(COUNT(bsr.Srq_Id), 0) AS Total_Srq  FROM BPMS_USER bu LEFT JOIN BPMS_APARTMENT_TRANSLATIONS bat ON bu.Apartment_Id = bat.Apartment_Id AND bat.Language_Code =? INNER JOIN BPMS_USER_APARTMENT_MAPPING buam ON bu.Apartment_Id = buam.Apartment_Id and bu.User_Id = buam.User_Id INNER JOIN BPMS_APARTMENT ba ON bu.Apartment_Id = ba.Apartment_Id INNER JOIN BPMS_STATE_TRANSLATIONS s ON ba.State = s.State_Id AND s.Language_Code = ? INNER JOIN BPMS_CITY_TRANSLATIONS c ON ba.City =c.City_Id and c.Language_Code = ? LEFT JOIN BPMS_ADDRESS a ON buam.Billing_Address_Id = a.Address_Id LEFT JOIN BPMS_SERVICE_REQUEST bsr ON bu.User_Id = bsr.User_Id AND bsr.Srq_Stage_8 = 0 WHERE bu.Account_Status !='Rejected' AND bu.Apartment_Id= ? GROUP BY bu.User_Id limit ? offset ? ", input.Language_Code, input.Language_Code, input.Language_Code, input.Apartment_Id, input.PageLimit, (input.Page-1)*input.PageLimit)

			errCount = userRepo.db.Get(&count, "SELECT COUNT(User_Id) AS Total_User FROM BPMS_USER WHERE Account_Status !='Rejected' AND Apartment_Id = ?", input.Apartment_Id)

			if errCount != nil {
				log.Println("UserRepository: FetchAllUsers: fetchingalluserscount: Input: Apartment_Id:", input.Apartment_Id, "  Error:", errCount.Error())
				return nil, errCount
			}
		}

	}

	if err != nil {
		log.Println("UserRepository: FetchAllUsers: fetchingallusers: Error: ", err.Error())
		return nil, err
	} else {
		allusers.Page = count / input.PageLimit
		if count%input.PageLimit != 0 {
			allusers.Page++
		}

		allusers.Total_User = count
		allusers.User_Info = alluser
		return &allusers, nil
	}

}
func (userRepo *UserRepository) UpdateProfile(tx *sqlx.Tx, input entity.UpdateProfileInput, image string) (bool, error) {

	query := "UPDATE `BPMS_USER` SET `User_Name` = IFNULL(?, `User_Name`), `User_Mobno` = IFNULL(?, `User_Mobno`), `User_Email` = IFNULL(?, `User_Email`) , `User_Image` = IFNULL(?, `User_Image`) WHERE `User_Id` = ?"

	_, errv := tx.Exec(query, NewNullString(input.User_Name), NewNullInt64(input.User_Mobno), NewNullString(input.User_Emailid), NewNullString(image), input.User_Id)

	if errv != nil {
		tx.Rollback()
		log.Println("UserRepository: UpdateProfile: updatinguserprofile: userid: ", input.User_Id, " : Error:", errv.Error())
		return false, errv
	}

	if input.Alternate_No != "" {
		queryMobno := "UPDATE `BPMS_USER` SET  `Alternate_No` = ?  WHERE `User_Id` = ?"
		_, err := tx.Exec(queryMobno, input.Alternate_No, input.User_Id)

		if err != nil {
			tx.Rollback()
			log.Println("UserRepository: UpdateProfile: updatingalternateNum: userid: ", input.User_Id, " : Error:", err.Error())
			return false, err
		}
	}
	log.Println("UserRepository: UpdateProfile: updatinguserprofile: userid: ", input.User_Id)
	return true, nil

}
