package repository

import (
	"bpmsapi/dto"
	"bpmsapi/entity"
	"database/sql"
	"errors"
	"fmt"

	"log"
	"strings"

	"github.com/jmoiron/sqlx"
)

type ProfileActions interface {
	EmployeeProfileUpdate(input entity.UpdateEmployeeProfileInput) (employeeprofile *entity.EmployeeProfile, err error)
	EmployeeApartmentConfig(input entity.EmpApartmentConfigInput) (string, error)
	EmployeeCreateLeave(input entity.CreateLeaveInput) (string, error)
	EmployeeCancelLeave(input entity.CancelLeaveInput) (string, error)
	DeleteEmployee(input entity.DeleteEmployeeInput) (string, error)
}

type ProfileRepository struct {
	tx *sqlx.Tx
}

func ProfileRepositoryInit(tx *sqlx.Tx) *ProfileRepository {
	return &ProfileRepository{
		tx: tx,
	}
}
func NewNullString(s string) sql.NullString {
	if len(s) == 0 {
		return sql.NullString{}
	}
	return sql.NullString{
		String: s,
		Valid:  true,
	}
}

func NewNullInt64(s int64) sql.NullInt64 {
	if s == 0 {
		return sql.NullInt64{}
	}
	return sql.NullInt64{
		Int64: s,
		Valid: true,
	}
}

func NewNullFloat32(s float64) sql.NullFloat64 {
	if s == 0 {
		return sql.NullFloat64{}
	}
	return sql.NullFloat64{
		Float64: s,
		Valid:   true,
	}
}
func (profileRepo *ProfileRepository) EmployeeProfileUpdate(input entity.UpdateEmployeeProfileInput, img_path string) (*entity.EmpProfile, error) {
	empProfile := entity.EmpProfile{}
	query := "UPDATE BPMS_EMPLOYEE be JOIN BPMS_ADDRESS ba  ON be.Address_Id = ba.Address_Id JOIN BPMS_ROLE_MAPPING brm ON be.Emp_Id = brm.Emp_Id SET be.Emp_Name=IFNULL(?, Emp_Name), be.Passcode=IFNULL(?, Passcode), be.Emp_Email=IFNULL(?, Emp_Email), be.Emp_Mobno=IFNULL(?, Emp_Mobno), be.Alternate_Mobno=IFNULL(?, Alternate_Mobno), be.Emp_Type=IFNULL(?, Emp_Type), be.Emp_DOR=IFNULL(?, Emp_DOR), be.Emp_Status=IFNULL(?, Emp_Status), be.Emp_Rejoining=IFNULL(?, Emp_Rejoining), be.Emp_Proof=IFNULL(?, Emp_Proof), be.Emp_Proof_Id=IFNULL(?, Emp_Proof_Id), be.Vendor_name=IFNULL(?, Vendor_name), ba.Country=IFNULL(?, Country), ba.State=IFNULL(?, State), ba.City=IFNULL(?, City), ba.Area_Name=IFNULL(?, Area_Name), ba.Address_Line1=IFNULL(?, Address_Line1), ba.Address_Line2=IFNULL(?, Address_Line2), ba.Pincode=IFNULL(?, Pincode), ba.Latitude=IFNULL(?, Latitude), ba.Longitude=IFNULL(?, Longitude), brm.Role_Id=IFNULL(?, Role_Id) WHERE be.Emp_Id=?"
	_, err := profileRepo.tx.Exec(query, NewNullString(input.Name), NewNullInt64(input.Passcode), NewNullString(input.Email), NewNullString(input.Primary_Contact), NewNullString(input.Emergency_Contact), NewNullString(input.Emp_Type), NewNullString(input.Date_of_Resigning), NewNullString(input.Emp_Status), NewNullString(input.Emp_Rejoining), NewNullString(input.Proof), NewNullString(input.Proof_ID), NewNullString(input.Employer_Name), NewNullString(input.Country), NewNullString(input.State), NewNullString(input.City), NewNullString(input.Area), NewNullString(input.Addressline1), NewNullString(input.Addressline2), NewNullString(input.Pincode), NewNullString(input.Latitude), NewNullString(input.Longitude), NewNullInt64(input.Role_Id), input.Emp_Id)
	if err != nil {
		profileRepo.tx.Rollback()
		log.Println("ProfileRepository: EmployeeProfileUpdate: updatingprofile: input: Emp_Id:", input.Emp_Id, " Error: ", err.Error())
		return nil, err
	}
	if img_path != "" {
		query := "UPDATE BPMS_EMPLOYEE SET Emp_Image=? WHERE Emp_Id=?"
		imgres, err := profileRepo.tx.Exec(query, img_path, input.Emp_Id)
		if err != nil {
			profileRepo.tx.Rollback()
			log.Println("ProfileRepository: EmployeeProfileUpdate: updatingimage: input: Emp_Id:", input.Emp_Id, " Error: ", err.Error())
			return nil, err
		}
		rowsAffectedimg, _ := imgres.RowsAffected()
		if rowsAffectedimg == 0 {
			profileRepo.tx.Rollback()
			log.Println("ProfileRepository: EmployeeProfileUpdate: updatingimage: rowsaffected")
			return nil, errors.New(dto.ErrorrowsAffectedUpdateEmpProfile)
		}
	}

	if input.Skilled_Services != "" {

		delService := "DELETE FROM `BPMS_SERVICE_EMP_MAPPING` WHERE Emp_Id = ?"
		rows, err1 := profileRepo.tx.Exec(delService, input.Emp_Id)

		if err1 != nil {
			profileRepo.tx.Rollback()
			log.Print("ProfileRepository: EmployeeProfileUpdate: deletingskilledservice: input : Emp_Id:", input.Emp_Id, "Error:", err1.Error())
			return nil, err1

		}
		rowsAffectedSkill, _ := rows.RowsAffected()
		if rowsAffectedSkill == 0 {
			profileRepo.tx.Rollback()
			log.Println("ProfileRepository: EmployeeProfileUpdate: unmappingservice: rowsaffected :emp_id:", input.Emp_Id)
			return nil, errors.New(dto.ErrorrowsAffectedService)
		}

		service_ids := strings.Split(input.Skilled_Services, ",")
		for _, service_id := range service_ids {
			serviceQuery := "INSERT INTO BPMS_SERVICE_EMP_MAPPING (Emp_Id, Service_Id) VALUES (?,?)"
			rowSkill, err2 := profileRepo.tx.Exec(serviceQuery, input.Emp_Id, service_id)
			if err2 != nil {
				profileRepo.tx.Rollback()
				log.Println("ProfileRepository: EmployeeProfileUpdate: mappingserviceid: Input: service_Id:", service_id, " Emp_Id:", input.Emp_Id, " Error: ", err2.Error())
				return nil, err2
			}
			rowsAffectedSkills, _ := rowSkill.RowsAffected()
			if rowsAffectedSkills == 0 {
				profileRepo.tx.Rollback()
				log.Println("ProfileRepository: EmployeeProfileUpdate: mappingserviceid: rowsaffected :emp_id:", input.Emp_Id)
				return nil, errors.New(dto.ErrorrowsAffectedUpdateEmpProfile)
			}
		}

	}
	var errIn1, errIn2 error
	profile_info := []entity.EmployeeProfile{}
	getQueryerr := profileRepo.tx.Select(&profile_info, "SELECT be.Emp_Id, be.Emp_Name, be.Passcode, be.Emp_Email, be.Emp_Mobno, be.Alternate_Mobno, be.Emp_Type, be.Emp_DOB, be.Emp_DOJ, be.Emp_DOR, be.Emp_Status, be.Emp_Rejoining, be.Emp_Image, be.Emp_Proof, be.Emp_Proof_Id, be.Vendor_name, be.Is_Available, be.Created_Timestamp, be.Recent_Login, be.Address_Id, ba.Country, ba.State, ba.City, ba.Area_Name, ba.Address_Line1, ba.Address_Line2, ba.Pincode, ba.Latitude, ba.Longitude, brm.Role_Id, brmt.Role_Name  FROM BPMS_EMPLOYEE be INNER JOIN BPMS_ADDRESS ba ON be.Address_Id = ba.Address_Id LEFT JOIN BPMS_ROLE_MAPPING brm ON be.Emp_Id = brm.Emp_Id LEFT JOIN BPMS_ROLE_MASTER_TRANSLATIONS brmt ON brm.Role_Id = brmt.Role_Id AND brmt.Language_Code = ?  WHERE be.Emp_Id =?", input.Language_Code, input.Emp_Id)
	if getQueryerr != nil {
		log.Print("ProfileRepository: EmployeeProfileUpdate: input : Emp_Id", input.Emp_Id, "Error :", getQueryerr.Error())
		return nil, getQueryerr
	} else {
		empProfile.EmployeeProfile = profile_info
		empProfile.Allocated_Apartments, errIn1 = profileRepo.FetchAllocatedApartments(input.Emp_Id, input.Language_Code)
		if errIn1 != nil {
			log.Print("ProfileRepository: EmployeeProfileUpdate: FetchAllocatedApartments: input : Emp_Id", input.Emp_Id, "Error :", errIn1.Error())
			return nil, errIn1
		}
		empProfile.Skilled_Services, errIn2 = profileRepo.FetchMappedService(input.Emp_Id, input.Language_Code)
		if errIn2 != nil {
			log.Print("ProfileRepository: EmployeeProfileUpdate: FetchMappedService: input : Emp_Id", input.Emp_Id, "Error :", errIn2.Error())
			return nil, errIn2
		}
		return &empProfile, nil
	}
}

func (profileRepo *ProfileRepository) FetchAllocatedApartments(empid int, language_code int) (emp_apartments []entity.EmpAllocatedApartment, err error) {
	err = profileRepo.tx.Select(&emp_apartments, "SELECT beam.Apartment_Id, bat.Apartment_Name  FROM BPMS_EMP_APARTMENT_MAPPING beam INNER JOIN BPMS_APARTMENT_TRANSLATIONS bat ON beam.Apartment_Id = bat.Apartment_Id AND bat.Language_Code = ? WHERE beam.Emp_Id = ?", language_code, empid)
	if err != nil {
		log.Print("ProfileRepository: FetchAllocatedApartments: input : EmpId ", empid, " Error:", err.Error())
		return nil, err
	} else {
		return emp_apartments, nil
	}
}

func (profileRepo *ProfileRepository) FetchMappedService(empid int, language_code int) (mapped_service []entity.ServiceInfo, err error) {
	err = profileRepo.tx.Select(&mapped_service, "SELECT bsem.Service_Id, bst.Service_Name FROM BPMS_SERVICE_EMP_MAPPING bsem INNER JOIN BPMS_SERVICE_TRANSLATIONS bst ON bsem.Service_Id = bst.Service_Id AND bst.Language_code =? WHERE bsem.Emp_Id = ?", language_code, empid)
	if err != nil {
		log.Print("ProfileRepository: FetchMappedService: input : EmpId ", empid, " Error:", err.Error())
		return nil, err
	} else {
		return mapped_service, nil
	}
}

func (profileRepo *ProfileRepository) EmployeeApartmentConfig(input entity.EmpApartmentConfigInput) (string, error) {

	if strings.ToLower(input.Action) == "unmapping" {

		apartment_ids := strings.Split(input.Apartment_Id, ",")
		for _, apartment_id := range apartment_ids {
			unMapping := "DELETE FROM `BPMS_EMP_APARTMENT_MAPPING` WHERE Emp_Id = ? AND Apartment_Id = ?"
			rows, err := profileRepo.tx.Exec(unMapping, input.Emp_Id, apartment_id)
			if err != nil {
				profileRepo.tx.Rollback()
				log.Print("ProfileRepository: EmployeeApartmentConfig: unmappingapartments: input : Emp_Id:", input.Emp_Id, " apartment_id:", apartment_id, "Error:", err.Error())
				return "Error while unmapping apartments for an employee", err

			}
			rowsAffected, _ := rows.RowsAffected()
			if rowsAffected == 0 {
				profileRepo.tx.Rollback()
				log.Println("ProfileRepository: AddApartment: rowsaffected")
				return "Invalid employee id", errors.New(dto.ErrorrowsAffectedAddApartment)
			}
		}

		return "Apartments unmapped successfully", nil
	} else if strings.ToLower(input.Action) == "mapping" {

		apartment_ids := strings.Split(input.Apartment_Id, ",")

		for _, apartment_id := range apartment_ids {
			query := "INSERT INTO `BPMS_EMP_APARTMENT_MAPPING` (`Emp_Id`, `Apartment_Id`) VALUES (?,?)"
			insert, err := profileRepo.tx.Exec(query, input.Emp_Id, apartment_id)
			if err != nil {
				log.Print("ProfileRepository: EmployeeApartmentConfig: mappingapartments input : Emp_Id:", input.Emp_Id, " apartment_id:", apartment_id, "Error:", err.Error())
				profileRepo.tx.Rollback()
				return "Error while mapping apartments for an employee", err
			}
			_, inserr := insert.LastInsertId()
			if inserr != nil {
				profileRepo.tx.Rollback()
				log.Println("ProfileRepository: EmployeeApartmentConfig: mappingapartments: Error: ", inserr.Error())
				return "Error while mapping apartments for an employee", nil
			}
		}

		return "Apartments mapped successfully", nil
	} else {
		//unmapping the apartment
		unmapping := "DELETE FROM `BPMS_EMP_APARTMENT_MAPPING` WHERE Emp_Id = ?"
		_, err1 := profileRepo.tx.Exec(unmapping, input.Emp_Id)
		if err1 != nil {
			profileRepo.tx.Rollback()
			log.Print("ProfileRepository: EmployeeApartmentConfig: unmappingapartments: input : Emp_Id:", input.Emp_Id, "Error:", err1.Error())
			return "Error while unmapping apartments for an employee", err1

		} else {
			apartment_ids := strings.Split(input.Apartment_Id, ",")
			for _, apartment_id := range apartment_ids {
				mappingQuery := "INSERT INTO `BPMS_EMP_APARTMENT_MAPPING` (`Emp_Id`, `Apartment_Id`) VALUES (?,?)"
				_, err2 := profileRepo.tx.Exec(mappingQuery, input.Emp_Id, apartment_id)
				if err2 != nil {
					profileRepo.tx.Rollback()
					log.Println("ProfileRepository: EmployeeApartmentConfig: mappingapartments : input : emp_id:", input.Emp_Id, ": apartmentid :", apartment_id, "Error", err2.Error())
					return "Error while mapping apartments for an employee", err2
				}
			}
			return "Apartments mapped successfully", nil
		}

	}

}
func (profileRepo *ProfileRepository) EmployeeCreateLeave(input entity.CreateLeaveInput) (string, error) {
	insertQuery := "INSERT INTO BPMS_EMPLOYEE_LEAVE_ENTRY (Emp_Id, Reason, No_Of_Days, From_Date, To_Date, Approved_By) VALUES (?,?, DATEDIFF(?, ?) + 1, ?, ?, ?)"
	_, err := profileRepo.tx.Exec(insertQuery, input.Emp_Id, input.Reason, input.To_Date, input.From_Date, input.From_Date, input.To_Date, input.Approved_By)
	if err != nil {
		log.Println("ProfileRepository: EmployeeCreateLeave: creatingempleave: Input: Emp_Id", input.Emp_Id, "Error ", err.Error())
		profileRepo.tx.Rollback()
		return "Error while applying leave request", err
	} else {

		return "Leave application created successfully", nil
	}
}

func (profileRepo *ProfileRepository) EmployeeCancelLeave(input entity.CancelLeaveInput) (string, error) {
	if input.Is_Cancel == 1 {
		updateQuery := "UPDATE BPMS_EMPLOYEE_LEAVE_ENTRY SET Is_Cancelled = 1 WHERE Emp_Id=? AND From_Date =? AND To_Date=?"
		res, err := profileRepo.tx.Exec(updateQuery, input.Emp_Id, input.From_Date, input.To_Date)
		if err != nil {
			profileRepo.tx.Rollback()
			log.Println("ProfileRepository: EmployeeCancelLeave: cancelempleave: Input: Emp_Id", input.Emp_Id, "Error ", err.Error())
			return "Error while canceling the leave request", err
		} else {
			rowsAffected, _ := res.RowsAffected()
			if rowsAffected == 0 {
				log.Println("ProfileRepository: EmployeeCancelLeave: cancelempleave: Input: Emp_Id", input.Emp_Id, "no rowsaffected")
				return "No leave applied for this employee", nil
			}

		}

		query := "UPDATE BPMS_EMPLOYEE SET Is_Available = 1 WHERE Emp_Id = ?"
		res1, err1 := profileRepo.tx.Exec(query, input.Emp_Id)
		if err1 != nil {
			profileRepo.tx.Rollback()
			log.Println("ProfileRepository: EmployeeCancelLeave: cancelempleave: Input: Emp_Id", input.Emp_Id, "Error ", err1.Error())
			return "Error while canceling the leave request", err1
		} else {
			rowsAffected1, _ := res1.RowsAffected()
			if rowsAffected1 == 0 {
				log.Println("ProfileRepository: EmployeeCancelLeave: cancelempleave: Input: Emp_Id", input.Emp_Id, "no rowsaffected")
				return "Employee is already available", nil
			}
		}
		return "Leave cancelled successfully", nil
	} else {
		log.Println("ProfileRepository: EmployeeCancelLeave: cancelempleave: Input: Emp_Id", input.Emp_Id)
		return "Change is not applicable ", nil
	}
}

func (profileRepo *ProfileRepository) DeleteEmployeeAccount(input entity.DeleteEmployeeInput) (resp string, err error) {
	fmt.Println(input.Employee_Id)
	if strings.ToLower(input.Role) == "admin" {
		resp, err = profileRepo.EmpDelete(input)
		if err != nil {
			log.Print("ProfileRepository: DeleteEmployee: deletingAdmin : input : Emp_Id", input.Employee_Id, " : Error :", err.Error())
			return "Error while deleting the admin account ", err
		} else {
			return resp, nil
		}

	} else if strings.ToLower(input.Role) == "technician" {
		var roleids []int
		errIn := profileRepo.tx.Select(&roleids, "SELECT `Role_Id` FROM `BPMS_ROLE_MAPPING` WHERE `Emp_Id` =?", input.Employee_Id)
		if errIn != nil {
			log.Println("ProfileRepository : DeleteEmployee : gettingemprole : Error :", errIn.Error(), ": employeeid :", input.Employee_Id)
			return "Error while fetching the role", errIn
		} else {
			for _, role := range roleids {
				if role == 2 {
					log.Println("ProfileRepository : DeleteEmployee : emproleadminmapped  : employeeid :", input.Employee_Id)
					return "Selected employee is mapped as admin. Please delete it from the admin list.", nil
				}
			}
			resp, err = profileRepo.EmpDelete(input)
			if err != nil {
				log.Print("ProfileRepository: DeleteEmployee: deletingTechnician : input : Emp_Id", input.Employee_Id, " : Error :", err.Error())
				return "Error while deleting the technician account ", err
			}
			return resp, nil

		}
	} else {
		return "invalid action", nil
	}

}

func (profileRepo *ProfileRepository) EmpDelete(input entity.DeleteEmployeeInput) (string, error) {
	fmt.Println("working")
	unmappingApartment := "DELETE FROM `BPMS_EMP_APARTMENT_MAPPING` WHERE `Emp_Id`=?"
	_, errApart := profileRepo.tx.Exec(unmappingApartment, input.Employee_Id)
	if errApart != nil {
		profileRepo.tx.Rollback()
		log.Println("ProfileRepository : EmpDelete : unmappingApartment : Error :", errApart.Error(), ": employeeid :", input.Employee_Id)
		return "Error while deleting the employee account ", errApart
	}

	unmappingService := "DELETE FROM `BPMS_SERVICE_EMP_MAPPING` WHERE `Emp_Id` =?"
	_, errServ := profileRepo.tx.Exec(unmappingService, input.Employee_Id)
	if errServ != nil {
		profileRepo.tx.Rollback()
		log.Println("ProfileRepository : EmpDelete : unmappingService : Error :", errServ.Error(), ": employeeid :", input.Employee_Id)
		return "Error while deleting the employee account ", errServ
	}
	// unmappingRole := "DELETE FROM `BPMS_ROLE_MAPPING` WHERE `Emp_Id`=?"
	// _, errRole := profileRepo.tx.Exec(unmappingRole, input.Employee_Id)
	// if errRole != nil {
	// 	profileRepo.tx.Rollback()
	// 	log.Println("ProfileRepository : EmpDelete : unmappingRole : Error :", errServ.Error(), ": employeeid :", input.Employee_Id)
	// 	return "Error while deleting the employee account ", errRole
	// }
	updateQuery := "UPDATE `BPMS_EMPLOYEE` SET `Emp_DOR`= ?, `Emp_Status`='inactive', `Is_Available`= 0  WHERE `Emp_Id` = ?"
	res, err := profileRepo.tx.Exec(updateQuery, input.Emp_DOR, input.Employee_Id)
	if err != nil {
		profileRepo.tx.Rollback()
		log.Println("ProfileRepository : EmpDelete : deletingemp : Error :", err.Error(), ": employeeid :", input.Employee_Id)
		return "Error while deleting the employee account ", err
	}
	rowsaffected, _ := res.RowsAffected()
	if rowsaffected == 0 {
		profileRepo.tx.Rollback()
		log.Println("ProfileRepository : EmpDelete : deletingemp : norowsaffected : employeeid :", input.Employee_Id)
		return "Error while deleting the employee account. ", errors.New(dto.ErrorrowsAffectedDeleteEmp)
	}

	return "Employee account deleted successfully", nil

}
