package repository

import (
	"api_yal/internal/models"
	"gorm.io/gorm"
)

// appealRepositoryImpl реализация интерфейса AppealRepository
type appealRepositoryImpl struct {
	db *gorm.DB
}

// NewAppealRepository создает новый экземпляр репозитория обращений
func NewAppealRepository(db *gorm.DB) AppealRepository {
	return &appealRepositoryImpl{db: db}
}

// Create созда��т новое обращение
func (r *appealRepositoryImpl) Create(appeal *models.Appeal) error {
	return r.db.Create(appeal).Error
}

// GetByID возвращает обращение по ID
func (r *appealRepositoryImpl) GetByID(id uint) (*models.Appeal, error) {
	var appeal models.Appeal
	err := r.db.Preload("Author").Preload("Object").Preload("Feedback").Preload("Comment").Preload("AssignedTo").First(&appeal, id).Error
	if err != nil {
		return nil, err
	}
	return &appeal, nil
}

// Update обновляет существующее обращение
func (r *appealRepositoryImpl) Update(appeal *models.Appeal) error {
	return r.db.Save(appeal).Error
}

// Delete помечает обращение как удаленное
func (r *appealRepositoryImpl) Delete(id uint) error {
	return r.db.Delete(&models.Appeal{}, id).Error
}

// List возвращает список обращений с пагинацией
func (r *appealRepositoryImpl) List(offset, limit int) ([]models.Appeal, error) {
	var appeals []models.Appeal
	err := r.db.Preload("Author").Preload("Object").Offset(offset).Limit(limit).Find(&appeals).Error
	if err != nil {
		return nil, err
	}
	return appeals, nil
}

// Count возвращает общее количество обращений
func (r *appealRepositoryImpl) Count() (int64, error) {
	var count int64
	err := r.db.Model(&models.Appeal{}).Count(&count).Error
	return count, err
}

// ListByStatus возвращает обращения по статусу
func (r *appealRepositoryImpl) ListByStatus(status models.AppealStatus, offset, limit int) ([]models.Appeal, error) {
	var appeals []models.Appeal
	err := r.db.Preload("Author").Preload("Object").Where("status = ?", status).Offset(offset).Limit(limit).Find(&appeals).Error
	if err != nil {
		return nil, err
	}
	return appeals, nil
}

// ListByType возвращает обращения по типу
func (r *appealRepositoryImpl) ListByType(typeAppeal models.AppealType, offset, limit int) ([]models.Appeal, error) {
	var appeals []models.Appeal
	err := r.db.Preload("Author").Preload("Object").Where("type = ?", typeAppeal).Offset(offset).Limit(limit).Find(&appeals).Error
	if err != nil {
		return nil, err
	}
	return appeals, nil
}

// ListByPriority возвращает обращения по приоритету
func (r *appealRepositoryImpl) ListByPriority(priority models.AppealPriority, offset, limit int) ([]models.Appeal, error) {
	var appeals []models.Appeal
	err := r.db.Preload("Author").Preload("Object").Where("priority = ?", priority).Offset(offset).Limit(limit).Find(&appeals).Error
	if err != nil {
		return nil, err
	}
	return appeals, nil
}

// Search находит обращения по заголовку или сообщению
func (r *appealRepositoryImpl) Search(query string, offset, limit int) ([]models.Appeal, error) {
	var appeals []models.Appeal
	err := r.db.Preload("Author").Preload("Object").Where("title LIKE ? OR message LIKE ?", "%"+query+"%", "%"+query+"%").Offset(offset).Limit(limit).Find(&appeals).Error
	if err != nil {
		return nil, err
	}
	return appeals, nil
}

// GetAuthor возвращает автора обращения
func (r *appealRepositoryImpl) GetAuthor(appealID uint) (*models.Account, error) {
	var appeal models.Appeal
	err := r.db.Select("author_id").First(&appeal, appealID).Error
	if err != nil {
		return nil, err
	}
	if appeal.AuthorID == nil {
		return nil, gorm.ErrRecordNotFound
	}
	return r.GetAccountByID(*appeal.AuthorID)
}

// GetObject возвращает объект, связанный с обращением
func (r *appealRepositoryImpl) GetObject(appealID uint) (*models.Object, error) {
	var appeal models.Appeal
	err := r.db.Select("object_id").First(&appeal, appealID).Error
	if err != nil {
		return nil, err
	}
	if appeal.ObjectID == nil {
		return nil, gorm.ErrRecordNotFound
	}
	return r.GetObjectByID(*appeal.ObjectID)
}

// GetFeedback возвращает отзыв, связанный с обращением
func (r *appealRepositoryImpl) GetFeedback(appealID uint) (*models.Feedback, error) {
	var appeal models.Appeal
	err := r.db.Select("feedback_id").First(&appeal, appealID).Error
	if err != nil {
		return nil, err
	}
	if appeal.FeedbackID == nil {
		return nil, gorm.ErrRecordNotFound
	}
	return r.GetFeedbackByID(*appeal.FeedbackID)
}

// GetComment возвращает комментарий, связанный с обращением
func (r *appealRepositoryImpl) GetComment(appealID uint) (*models.Comment, error) {
	var appeal models.Appeal
	err := r.db.Select("comment_id").First(&appeal, appealID).Error
	if err != nil {
		return nil, err
	}
	if appeal.CommentID == nil {
		return nil, gorm.ErrRecordNotFound
	}
	return r.GetCommentByID(*appeal.CommentID)
}

// AssignTo назначает ответственного за обработку обращения
func (r *appealRepositoryImpl) AssignTo(appealID uint, assignedToID *uint) error {
	return r.db.Model(&models.Appeal{}).Where("id = ?", appealID).Update("assigned_to_id", assignedToID).Error
}

// UpdateStatus обновляет статус обращения
func (r *appealRepositoryImpl) UpdateStatus(appealID uint, status models.AppealStatus) error {
	return r.db.Model(&models.Appeal{}).Where("id = ?", appealID).Update("status", status).Error
}

// CreateHistory создает запись в истории изменений статуса
func (r *appealRepositoryImpl) CreateHistory(history *models.AppealHistory) error {
	return r.db.Create(history).Error
}

// ListHistory возвращает историю изменений статуса обращения
func (r *appealRepositoryImpl) ListHistory(appealID uint, offset, limit int) ([]models.AppealHistory, error) {
	var histories []models.AppealHistory
	err := r.db.Preload("User").Where("appeal_id = ?", appealID).Offset(offset).Limit(limit).Find(&histories).Error
	if err != nil {
		return nil, err
	}
	return histories, nil
}

// Вспомогательные методы для получения связанных сущностей
func (r *appealRepositoryImpl) GetAccountByID(id uint) (*models.Account, error) {
	var account models.Account
	err := r.db.First(&account, id).Error
	if err != nil {
		return nil, err
	}
	return &account, nil
}

func (r *appealRepositoryImpl) GetObjectByID(id uint) (*models.Object, error) {
	var object models.Object
	err := r.db.First(&object, id).Error
	if err != nil {
		return nil, err
	}
	return &object, nil
}

func (r *appealRepositoryImpl) GetFeedbackByID(id uint) (*models.Feedback, error) {
	var feedback models.Feedback
	err := r.db.First(&feedback, id).Error
	if err != nil {
		return nil, err
	}
	return &feedback, nil
}

func (r *appealRepositoryImpl) GetCommentByID(id uint) (*models.Comment, error) {
	var comment models.Comment
	err := r.db.First(&comment, id).Error
	if err != nil {
		return nil, err
	}
	return &comment, nil
}