package mysql

import (
	"context"
	"errors"
	"strings"

	"gorm.io/gorm"

	filemanager "wucher/internal/domain/file_manager"
	"wucher/internal/domain/helicopter"
	helicopterfile "wucher/internal/domain/helicopter_file"
)

type HelicopterFileRepository struct {
	db *gorm.DB
}

func NewHelicopterFileRepository(db *gorm.DB) *HelicopterFileRepository {
	return &HelicopterFileRepository{db: db}
}

func (r *HelicopterFileRepository) Create(ctx context.Context, row *helicopterfile.HelicopterFile) error {
	return r.db.WithContext(ctx).Create(row).Error
}

func (r *HelicopterFileRepository) Update(ctx context.Context, row *helicopterfile.HelicopterFile) error {
	return r.db.WithContext(ctx).Save(row).Error
}

func (r *HelicopterFileRepository) Delete(ctx context.Context, id []byte) error {
	if err := ensureNoReferenceBeforeDelete(ctx, r.db, "helicopter_files", id); err != nil {
		return err
	}
	return mapDeleteConstraintError(r.db.WithContext(ctx).Delete(&helicopterfile.HelicopterFile{}, "id = ?", id).Error)
}

func (r *HelicopterFileRepository) GetByID(ctx context.Context, id []byte) (*helicopterfile.HelicopterFile, error) {
	var row helicopterfile.HelicopterFile
	err := r.db.WithContext(ctx).
		Preload("Helicopter").
		Preload("FileAttachment").
		Preload("FileAttachment.File").
		Preload("SourceFile").
		Where("id = ?", id).
		First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &row, err
}

func (r *HelicopterFileRepository) List(ctx context.Context, filter string, sort string, limit, offset int) ([]helicopterfile.HelicopterFile, int64, error) {
	rows := make([]helicopterfile.HelicopterFile, 0)
	var total int64

	base := r.db.WithContext(ctx).Model(&helicopterfile.HelicopterFile{})
	if strings.TrimSpace(filter) != "" {
		like := "%" + strings.ToLower(strings.TrimSpace(filter)) + "%"
		base = base.Where(
			"LOWER(section) LIKE ? OR LOWER(HEX(id)) LIKE ? OR LOWER(HEX(helicopter_id)) LIKE ? OR LOWER(HEX(file_attachment_id)) LIKE ? OR LOWER(HEX(source_file_id)) LIKE ? OR CAST(is_mandatory AS CHAR) LIKE ?",
			like, like, like, like, like, like,
		)
	}

	query := base
	if sort != "" {
		query = query.Order(sort)
	}
	if err := base.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if limit > 0 {
		query = query.Limit(limit).Offset(offset)
	}
	if err := query.
		Preload("Helicopter").
		Preload("FileAttachment").
		Preload("FileAttachment.File").
		Preload("SourceFile").
		Find(&rows).Error; err != nil {
		return nil, 0, err
	}
	return rows, total, nil
}

func (r *HelicopterFileRepository) ListPendingAttachments(ctx context.Context, limit int) ([]helicopterfile.HelicopterFile, error) {
	rows := make([]helicopterfile.HelicopterFile, 0)
	query := r.db.WithContext(ctx).
		Preload("Helicopter").
		Preload("SourceFile").
		Where("source_file_id IS NOT NULL AND file_attachment_id IS NULL").
		Order("created_at ASC")
	if limit > 0 {
		query = query.Limit(limit)
	}
	if err := query.Find(&rows).Error; err != nil {
		return nil, err
	}
	return rows, nil
}

func (r *HelicopterFileRepository) ListPendingAttachmentsBySourceFileID(ctx context.Context, sourceFileID []byte, limit int) ([]helicopterfile.HelicopterFile, error) {
	rows := make([]helicopterfile.HelicopterFile, 0)
	if len(sourceFileID) != 16 {
		return rows, nil
	}
	query := r.db.WithContext(ctx).
		Preload("Helicopter").
		Preload("SourceFile").
		Where("source_file_id = ? AND file_attachment_id IS NULL", sourceFileID).
		Order("created_at ASC")
	if limit > 0 {
		query = query.Limit(limit)
	}
	if err := query.Find(&rows).Error; err != nil {
		return nil, err
	}
	return rows, nil
}

func (r *HelicopterFileRepository) ListByHelicopter(ctx context.Context, helicopterID []byte) ([]helicopterfile.HelicopterFile, error) {
	rows := make([]helicopterfile.HelicopterFile, 0)
	if len(helicopterID) != 16 {
		return rows, nil
	}
	err := r.db.WithContext(ctx).
		Preload("Helicopter").
		Preload("FileAttachment").
		Preload("FileAttachment.File").
		Preload("SourceFile").
		Where("helicopter_id = ?", helicopterID).
		Order("section ASC, position ASC, created_at ASC").
		Find(&rows).Error
	return rows, err
}

func (r *HelicopterFileRepository) ListByHelicopterAndSection(ctx context.Context, helicopterID []byte, section string) ([]helicopterfile.HelicopterFile, error) {
	rows := make([]helicopterfile.HelicopterFile, 0)
	if len(helicopterID) != 16 || strings.TrimSpace(section) == "" {
		return rows, nil
	}
	err := r.db.WithContext(ctx).
		Preload("Helicopter").
		Preload("FileAttachment").
		Preload("FileAttachment.File").
		Preload("SourceFile").
		Where("helicopter_id = ? AND section = ?", helicopterID, strings.TrimSpace(section)).
		Order("position ASC, created_at ASC").
		Find(&rows).Error
	return rows, err
}

func (r *HelicopterFileRepository) HelicopterExists(ctx context.Context, helicopterID []byte) (bool, error) {
	if len(helicopterID) != 16 {
		return false, nil
	}
	var total int64
	err := r.db.WithContext(ctx).
		Model(&helicopter.Helicopter{}).
		Where("id = ?", helicopterID).
		Count(&total).Error
	return total > 0, err
}

func (r *HelicopterFileRepository) AttachmentExists(ctx context.Context, attachmentID []byte) (bool, error) {
	if len(attachmentID) != 16 {
		return false, nil
	}
	var total int64
	err := r.db.WithContext(ctx).
		Model(&filemanager.Attachment{}).
		Where("id = ?", attachmentID).
		Count(&total).Error
	return total > 0, err
}
