package repositories

import (
	"time"

	"gorm.io/gorm"

	"github.com/treytartt/honeydue-api/internal/models"
)

// DocumentFilter contains optional filter parameters for listing documents.
type DocumentFilter struct {
	ResidenceID  *uint
	DocumentType string
	IsActive     *bool
	ExpiringSoon *int
	Search       string
}

// DocumentRepository handles database operations for documents
type DocumentRepository struct {
	db *gorm.DB
}

// NewDocumentRepository creates a new document repository
func NewDocumentRepository(db *gorm.DB) *DocumentRepository {
	return &DocumentRepository{db: db}
}

// FindByID finds a document by ID with preloaded relations
func (r *DocumentRepository) FindByID(id uint) (*models.Document, error) {
	var document models.Document
	err := r.db.Preload("CreatedBy").
		Preload("Task").
		Preload("Images").
		Where("id = ? AND is_active = ?", id, true).
		First(&document).Error
	if err != nil {
		return nil, err
	}
	return &document, nil
}

// FindByResidence finds all documents for a residence
func (r *DocumentRepository) FindByResidence(residenceID uint) ([]models.Document, error) {
	var documents []models.Document
	err := r.db.Preload("CreatedBy").
		Preload("Images").
		Where("residence_id = ? AND is_active = ?", residenceID, true).
		Order("created_at DESC").
		Find(&documents).Error
	return documents, err
}

// FindByUser finds all documents accessible to a user.
// A default limit of 500 is applied to prevent unbounded result sets.
func (r *DocumentRepository) FindByUser(residenceIDs []uint) ([]models.Document, error) {
	var documents []models.Document
	err := r.db.Preload("CreatedBy").
		Preload("Residence").
		Preload("Images").
		Where("residence_id IN ? AND is_active = ?", residenceIDs, true).
		Order("created_at DESC").
		Limit(500).
		Find(&documents).Error
	return documents, err
}

// FindByUserFiltered finds documents accessible to a user with optional filters.
func (r *DocumentRepository) FindByUserFiltered(residenceIDs []uint, filter *DocumentFilter) ([]models.Document, error) {
	query := r.db.Preload("CreatedBy").
		Preload("Residence").
		Preload("Images").
		Where("residence_id IN ?", residenceIDs)

	// Default behavior is active-only unless explicitly overridden.
	if filter == nil || filter.IsActive == nil {
		query = query.Where("is_active = ?", true)
	}

	if filter != nil {
		if filter.DocumentType != "" {
			query = query.Where("document_type = ?", filter.DocumentType)
		}
		if filter.IsActive != nil {
			query = query.Where("is_active = ?", *filter.IsActive)
		}
		if filter.ExpiringSoon != nil {
			now := time.Now().UTC()
			threshold := now.AddDate(0, 0, *filter.ExpiringSoon)
			query = query.Where("expiry_date IS NOT NULL AND expiry_date > ? AND expiry_date <= ?", now, threshold)
		}
		if filter.Search != "" {
			escaped := escapeLikeWildcards(filter.Search)
			searchPattern := "%" + escaped + "%"
			query = query.Where("(title ILIKE ? OR description ILIKE ?)", searchPattern, searchPattern)
		}
	}

	var documents []models.Document
	err := query.Order("created_at DESC").Limit(500).Find(&documents).Error
	return documents, err
}

// FindWarranties finds all warranty documents
func (r *DocumentRepository) FindWarranties(residenceIDs []uint) ([]models.Document, error) {
	var documents []models.Document
	err := r.db.Preload("CreatedBy").
		Preload("Residence").
		Preload("Images").
		Where("residence_id IN ? AND is_active = ? AND document_type = ?",
			residenceIDs, true, models.DocumentTypeWarranty).
		Order("expiry_date ASC NULLS LAST").
		Find(&documents).Error
	return documents, err
}

// FindExpiringWarranties finds warranties expiring within the specified days
func (r *DocumentRepository) FindExpiringWarranties(residenceIDs []uint, days int) ([]models.Document, error) {
	threshold := time.Now().UTC().AddDate(0, 0, days)
	now := time.Now().UTC()

	var documents []models.Document
	err := r.db.Preload("CreatedBy").
		Preload("Residence").
		Preload("Images").
		Where("residence_id IN ? AND is_active = ? AND document_type = ? AND expiry_date > ? AND expiry_date <= ?",
			residenceIDs, true, models.DocumentTypeWarranty, now, threshold).
		Order("expiry_date ASC").
		Find(&documents).Error
	return documents, err
}

// Create creates a new document
func (r *DocumentRepository) Create(document *models.Document) error {
	return r.db.Create(document).Error
}

// Update updates a document
// Uses Omit to exclude associations that could interfere with Save
func (r *DocumentRepository) Update(document *models.Document) error {
	return r.db.Omit("CreatedBy", "Task", "Images", "Residence").Save(document).Error
}

// Delete soft-deletes a document
func (r *DocumentRepository) Delete(id uint) error {
	return r.db.Model(&models.Document{}).
		Where("id = ?", id).
		Update("is_active", false).Error
}

// Activate activates a document
func (r *DocumentRepository) Activate(id uint) error {
	return r.db.Model(&models.Document{}).
		Where("id = ?", id).
		Update("is_active", true).Error
}

// Deactivate deactivates a document
func (r *DocumentRepository) Deactivate(id uint) error {
	return r.db.Model(&models.Document{}).
		Where("id = ?", id).
		Update("is_active", false).Error
}

// CountByResidence counts documents in a residence
func (r *DocumentRepository) CountByResidence(residenceID uint) (int64, error) {
	var count int64
	err := r.db.Model(&models.Document{}).
		Where("residence_id = ? AND is_active = ?", residenceID, true).
		Count(&count).Error
	return count, err
}

// CountByResidenceIDs counts all active documents across multiple residences in a single query.
// Returns the total count of active documents for the given residence IDs.
func (r *DocumentRepository) CountByResidenceIDs(residenceIDs []uint) (int64, error) {
	if len(residenceIDs) == 0 {
		return 0, nil
	}
	var count int64
	err := r.db.Model(&models.Document{}).
		Where("residence_id IN ? AND is_active = ?", residenceIDs, true).
		Count(&count).Error
	return count, err
}

// FindByIDIncludingInactive finds a document by ID including inactive ones
func (r *DocumentRepository) FindByIDIncludingInactive(id uint, document *models.Document) error {
	return r.db.Preload("CreatedBy").Preload("Images").First(document, id).Error
}

// CreateDocumentImage creates a new document image
func (r *DocumentRepository) CreateDocumentImage(image *models.DocumentImage) error {
	return r.db.Create(image).Error
}

// DeleteDocumentImage deletes a document image
func (r *DocumentRepository) DeleteDocumentImage(id uint) error {
	return r.db.Delete(&models.DocumentImage{}, id).Error
}

// DeleteDocumentImages deletes all images for a document
func (r *DocumentRepository) DeleteDocumentImages(documentID uint) error {
	return r.db.Where("document_id = ?", documentID).Delete(&models.DocumentImage{}).Error
}

// FindImageByID finds a document image by ID
func (r *DocumentRepository) FindImageByID(id uint) (*models.DocumentImage, error) {
	var image models.DocumentImage
	err := r.db.First(&image, id).Error
	if err != nil {
		return nil, err
	}
	return &image, nil
}
