package repositories_astrologer

import (
	dto "astrology-api/dto_astrologer/activity"
	models "astrology-api/models/astrologermodel"
	"errors"

	"gorm.io/gorm"
)

//////////////////////////////////////////////////////////////
// Repository Struct
//////////////////////////////////////////////////////////////

type activityRepository struct {
	db *gorm.DB
}

//////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////

func NewActivityRepository(db *gorm.DB) ActivityRepository {
	return &activityRepository{
		db: db,
	}
}

//////////////////////////////////////////////////////////////
// Dummy Reference
//////////////////////////////////////////////////////////////

var (
	_ = models.AstrologerWaitingQueue{}
)

//////////////////////////////////////////////////////////////
// Add Waiting User
//////////////////////////////////////////////////////////////

func (r *activityRepository) AddWaiting(
	waiting *models.AstrologerWaitingQueue,
) error {

	return r.db.Transaction(func(tx *gorm.DB) error {

		if waiting == nil {
			return errors.New("waiting data is nil")
		}

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

		return nil
	})
}

//////////////////////////////////////////////////////////////
// Update Waiting User
//////////////////////////////////////////////////////////////

func (r *activityRepository) UpdateWaiting(
	waiting *models.AstrologerWaitingQueue,
) error {

	if waiting == nil {
		return errors.New("waiting data is nil")
	}

	return r.db.
		Model(&models.AstrologerWaitingQueue{}).
		Where("id = ?", waiting.ID).
		Updates(waiting).
		Error
}

//////////////////////////////////////////////////////////////
// Delete Waiting
//////////////////////////////////////////////////////////////

func (r *activityRepository) DeleteWaiting(
	queueID uint,
) error {

	return r.db.
		Model(&models.AstrologerWaitingQueue{}).
		Where("id = ?", queueID).
		Updates(map[string]interface{}{
			"status": "CANCELLED",
		}).
		Error
}

//////////////////////////////////////////////////////////////
// Get Waiting By ID
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetWaitingByID(
	queueID uint,
) (*models.AstrologerWaitingQueue, error) {

	var waiting models.AstrologerWaitingQueue

	err := r.db.
		Preload("User").
		Preload("Astrologer").
		Where("id = ?", queueID).
		First(&waiting).
		Error

	if err != nil {

		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("waiting record not found")
		}

		return nil, err
	}

	return &waiting, nil
}

//////////////////////////////////////////////////////////////
// Get Waiting By User
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetWaitingByUser(
	astrologerID uint,
	userID uint,
	waitingType string,
) (*models.AstrologerWaitingQueue, error) {

	var waiting models.AstrologerWaitingQueue

	err := r.db.
		Where("astrologer_id = ?", astrologerID).
		Where("user_id = ?", userID).
		Where("waiting_type = ?", waitingType).
		Where("status = ?", "WAITING").
		First(&waiting).Error

	if err != nil {

		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}

		return nil, err
	}

	return &waiting, nil
}

//////////////////////////////////////////////////////////////
// Waiting List
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetWaitingList(
	astrologerID uint,
	request dto.PaginationRequest,
) ([]dto.WaitingUserResponse, int64, error) {

	var list []dto.WaitingUserResponse
	var total int64

	query := r.db.
		Table("astrologer_waiting_queue awq").
		Joins("JOIN users u ON u.id = awq.user_id").
		Where("awq.astrologer_id = ?", astrologerID).
		Where("awq.status IN ?", []string{"WAITING", "NOTIFIED", "CONNECTING"})

	if request.Search != "" {

		search := "%" + request.Search + "%"

		query = query.Where(
			"(u.name LIKE ? OR u.mobileNo LIKE ?)",
			search,
			search,
		)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (request.Page - 1) * request.Limit

	err := query.
		Select(`
			awq.id as queue_id,
			u.id as user_id,
			u.name,
			u.gender,
			u.profile as profile_image,
			awq.waiting_type,
			awq.queue_position,
			awq.status,
			TIMESTAMPDIFF(
				MINUTE,
				awq.requested_at,
				NOW()
			) as waiting_since
		`).
		Order("awq.requested_at ASC, awq.id ASC").
		Limit(request.Limit).
		Offset(offset).
		Scan(&list).Error

	if err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

//////////////////////////////////////////////////////////////
// Update Queue Position
//////////////////////////////////////////////////////////////

func (r *activityRepository) UpdateQueuePositions(
	astrologerID uint,
	waitingType string,
) error {

	var waitingList []models.AstrologerWaitingQueue

	err := r.db.
		Where("astrologer_id = ?", astrologerID).
		Where("waiting_type = ?", waitingType).
		Where("status = ?", "WAITING").
		Order("requested_at ASC, id ASC").
		Find(&waitingList).Error

	if err != nil {
		return err
	}

	tx := r.db.Begin()

	for index, row := range waitingList {

		err := tx.
			Model(&models.AstrologerWaitingQueue{}).
			Where("id = ?", row.ID).
			Update(
				"queue_position",
				index+1,
			).Error

		if err != nil {

			tx.Rollback()

			return err
		}
	}

	return tx.Commit().Error
}

//////////////////////////////////////////////////////////////
// Next Waiting User
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetNextWaitingUser(
	astrologerID uint,
	waitingType string,
) (*models.AstrologerWaitingQueue, error) {

	var waiting models.AstrologerWaitingQueue

	err := r.db.
		Where("astrologer_id = ?", astrologerID).
		Where("waiting_type = ?", waitingType).
		Where("status = ?", "WAITING").
		Order("queue_position ASC").
		First(&waiting).Error

	if err != nil {

		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}

		return nil, err
	}

	return &waiting, nil
}

//////////////////////////////////////////////////////////////
// Get Chat History
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetChatHistory(
	astrologerID uint,
	request dto.PaginationRequest,
) ([]dto.ChatHistoryItem, int64, error) {

	var (
		list  []dto.ChatHistoryItem
		total int64
	)

	//------------------------------------------
	// Default Pagination
	//------------------------------------------

	if request.Page <= 0 {
		request.Page = 1
	}

	if request.Limit <= 0 {
		request.Limit = 10
	}

	offset := (request.Page - 1) * request.Limit

	//------------------------------------------
	// Base Query
	//------------------------------------------

	query := r.db.
		Table("user_chat_histories uch").
		Joins("INNER JOIN users u ON u.id = uch.userId").
		Where("uch.astrologerId = ?", astrologerID)

	//------------------------------------------
	// Status Filter
	//------------------------------------------

	if request.Status != "" &&
		request.Status != "ALL" {

		query = query.Where(
			"uch.chat_status = ?",
			request.Status,
		)
	}

	//------------------------------------------
	// Search
	//------------------------------------------

	if request.Search != "" {

		search := "%" + request.Search + "%"

		query = query.Where(
			`(
				u.name LIKE ?
				OR
				u.mobileNo LIKE ?
			)`,
			search,
			search,
		)
	}

	//------------------------------------------
	// Total Records
	//------------------------------------------

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	//------------------------------------------
	// Records
	//------------------------------------------

	err := query.
		Select(`
			uch.id                              AS chat_id,
			uch.chat_history_number             AS chat_number,
			u.id                               AS user_id,
			u.name                             AS user_name,
			u.userCode                         AS user_code,
			u.profile                     AS profile_image,
			u.gender                           AS gender,
			uch.chat_duration                  AS duration,
			uch.chat_status                    AS chat_status,
			uch.deduction_amount               AS amount,
			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			)                                  AS started_at,
			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			)                                  AS ended_at
		`).
		Order("uch.id DESC").
		Limit(request.Limit).
		Offset(offset).
		Scan(&list).Error

	if err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

//////////////////////////////////////////////////////////////
// Get Chat Detail
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetChatDetail(
	astrologerID uint,
	chatID uint,
) (*dto.ChatDetailResponse, error) {

	var response dto.ChatDetailResponse

	err := r.db.
		Table("user_chat_histories uch").
		Joins("INNER JOIN users u ON u.id = uch.userId").
		Joins("INNER JOIN astrologers a ON a.id = uch.astrologerId").
		Where("uch.id = ?", chatID).
		Where("uch.astrologerId = ?", astrologerID).
		Select(`
			uch.id                                  AS chat_id,
			u.id                                    AS user_id,
			u.name                                  AS user_name,
			u.userCode                              AS user_code,
			u.gender                                AS gender,
			u.birthDate                             AS dob,
			u.birthPlace                            AS pob,

			uch.chat_duration                       AS duration,

			a.chatRate                              AS rate,

			uch.deduction_amount                    AS amount,

			uch.chat_status                         AS chat_status,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			)                                       AS started_at,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			)                                       AS ended_at
		`).
		Scan(&response).Error

	if err != nil {
		return nil, err
	}

	//-----------------------------------------
	// Record Exists?
	//-----------------------------------------

	if response.ChatID == 0 {
		return nil, errors.New("chat history not found")
	}

	return &response, nil
}

//////////////////////////////////////////////////////////////
// Get Call History
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetCallHistory(
	astrologerID uint,
	request dto.PaginationRequest,
) ([]dto.CallHistoryItem, int64, error) {

	var (
		list  []dto.CallHistoryItem
		total int64
	)

	//------------------------------------------
	// Default Pagination
	//------------------------------------------

	if request.Page <= 0 {
		request.Page = 1
	}

	if request.Limit <= 0 {
		request.Limit = 10
	}

	offset := (request.Page - 1) * request.Limit

	//------------------------------------------
	// Base Query
	//------------------------------------------

	query := r.db.
		Table("user_call_histories uch").
		Joins("INNER JOIN users u ON u.id = uch.userId").
		Where("uch.astrologerId = ?", astrologerID)

	//------------------------------------------
	// Status Filter
	//------------------------------------------

	if request.Status != "" &&
		request.Status != "ALL" {

		query = query.Where(
			"uch.callStatus = ?",
			request.Status,
		)
	}

	//------------------------------------------
	// Search
	//------------------------------------------

	if request.Search != "" {

		search := "%" + request.Search + "%"

		query = query.Where(
			"(u.name LIKE ? OR u.mobileNo LIKE ?)",
			search,
			search,
		)
	}

	//------------------------------------------
	// Total Count
	//------------------------------------------

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	//------------------------------------------
	// Records
	//------------------------------------------

	err := query.
		Select(`
			uch.id                               AS call_id,
			uch.callHistoryNumber                AS call_number,
			u.id                                AS user_id,
			u.name                              AS user_name,
			u.userCode                          AS user_code,
			u.profile                      AS profile_image,
			u.gender                            AS gender,
			uch.callDuration                    AS duration,
			uch.callStatus                      AS call_status,
			uch.deductionAmount                 AS amount,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			) AS started_at,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			) AS ended_at
		`).
		Order("uch.id DESC").
		Limit(request.Limit).
		Offset(offset).
		Scan(&list).Error

	if err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

//////////////////////////////////////////////////////////////
// Get Call Detail
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetCallDetail(
	astrologerID uint,
	callID uint,
) (*dto.CallDetailResponse, error) {

	var response dto.CallDetailResponse

	err := r.db.
		Table("user_call_histories uch").
		Joins("INNER JOIN users u ON u.id = uch.userId").
		Joins("INNER JOIN astrologers a ON a.id = uch.astrologerId").
		Where("uch.id = ?", callID).
		Where("uch.astrologerId = ?", astrologerID).
		Select(`
			uch.id                             AS call_id,

			u.id                               AS user_id,

			u.name                             AS user_name,

			u.userCode                         AS user_code,

			u.gender                           AS gender,

			u.birthDate                        AS dob,

			u.birthPlace                       AS pob,

			uch.callDuration                   AS duration,

			a.audioCallRate                    AS rate,

			uch.deductionAmount                AS amount,

			uch.callStatus                     AS call_status,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			) AS started_at,

			DATE_FORMAT(
				uch.created_at,
				'%d-%m-%Y %h:%i %p'
			) AS ended_at
		`).
		Scan(&response).Error

	if err != nil {
		return nil, err
	}

	if response.CallID == 0 {
		return nil, errors.New("call history not found")
	}

	return &response, nil
}

//////////////////////////////////////////////////////////////
// Get AstroMall Orders
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetAstromallOrders(
	astrologerID uint,
	request dto.PaginationRequest,
) ([]dto.AstromallOrderItem, int64, error) {

	var (
		list  []dto.AstromallOrderItem
		total int64
	)

	//------------------------------------------
	// Default Pagination
	//------------------------------------------

	if request.Page <= 0 {
		request.Page = 1
	}

	if request.Limit <= 0 {
		request.Limit = 10
	}

	offset := (request.Page - 1) * request.Limit

	//------------------------------------------
	// Base Query
	//------------------------------------------

	query := r.db.
		Table("astromall_orders ao").
		Joins("INNER JOIN users u ON u.id = ao.user_id").
		Joins("INNER JOIN astromall_products ap ON ap.id = ao.product_id").
		Where("ao.astrologer_id = ?", astrologerID)

	//------------------------------------------
	// Status Filter
	//------------------------------------------

	if request.Status != "" &&
		request.Status != "ALL" {

		query = query.Where(
			"ao.status = ?",
			request.Status,
		)
	}

	//------------------------------------------
	// Search
	//------------------------------------------

	if request.Search != "" {

		search := "%" + request.Search + "%"

		query = query.Where(
			`(
				u.name LIKE ?
				OR
				ap.name LIKE ?
			)`,
			search,
			search,
		)
	}

	//------------------------------------------
	// Total Records
	//------------------------------------------

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	//------------------------------------------
	// Records
	//------------------------------------------

	err := query.
		Select(`
			ao.id                               AS order_id,
			CONCAT('ORD',ao.id)                 AS order_number,

			u.id                               AS user_id,
			u.name                             AS user_name,
			u.userCode                         AS user_code,

			ap.name                            AS product_name,
			ap.productImage                    AS product_image,

			ao.quantity                        AS quantity,

			ao.total_amount                    AS order_amount,

			ao.astrologer_earning              AS astrologer_earning,

			ao.admin_commission                AS commission,

			ao.status                          AS status,

			DATE_FORMAT(
				ao.created_at,
				'%d-%m-%Y %h:%i %p'
			) AS order_date
		`).
		Order("ao.id DESC").
		Limit(request.Limit).
		Offset(offset).
		Scan(&list).Error

	if err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

//////////////////////////////////////////////////////////////
// Get AstroMall Order Detail
//////////////////////////////////////////////////////////////

func (r *activityRepository) GetAstromallOrderDetail(
	astrologerID uint,
	orderID uint,
) (*dto.AstromallOrderItem, error) {

	var response dto.AstromallOrderItem

	err := r.db.
		Table("astromall_orders ao").
		Joins("INNER JOIN users u ON u.id = ao.user_id").
		Joins("INNER JOIN astromall_products ap ON ap.id = ao.product_id").
		Where("ao.id = ?", orderID).
		Where("ao.astrologer_id = ?", astrologerID).
		Select(`ao.id  AS order_id, 
		    CONCAT('ORD',ao.id)  AS order_number, u.id AS user_id, u.name  AS user_name,
			u.userCode  AS user_code,
			u.profile  AS profile_image,
			u.gender  AS gender, ap.id  AS product_id, ap.name AS product_name,
			ap.productImage  AS product_image,
			ap.description  AS product_description,
			ao.quantity  AS quantity,
			ao.total_amount  AS order_amount,
			ao.astrologer_earning AS astrologer_earning,
			ao.admin_commission  AS commission,
			ao.status AS status,
			DATE_FORMAT(ao.created_at,'%d-%m-%Y %h:%i %p'
			) AS order_date
		`).
		Scan(&response).Error

	if err != nil {
		return nil, err
	}

	if response.OrderID == 0 {
		return nil, errors.New("order not found")
	}

	return &response, nil
}
