package repository

import (
	"context"
	"time"

	"gorm.io/gorm"

	"git.jabali-panel.com/shukivaknin/jabali2/panel-api/internal/models"
)

// MalwareQuarantineRepository owns the malware_quarantine table (M33).
//
// Active rows = deleted_at IS NULL AND restored_at IS NULL. Restore
// flips restored_at + restored_by + restored_reason; Delete flips
// deleted_at. Hard-purge happens via the retention timer (Step 10).
type MalwareQuarantineRepository interface {
	Create(ctx context.Context, q *models.MalwareQuarantine) error
	Get(ctx context.Context, id string) (*models.MalwareQuarantine, error)
	ListActive(ctx context.Context, limit, offset int) ([]models.MalwareQuarantine, int64, error)
	Restore(ctx context.Context, id, restoredBy, reason string) error
	MarkDeleted(ctx context.Context, id string) error
	PurgeOlderThan(ctx context.Context, cutoff time.Time) (int64, error)
}

type malwareQuarantineRepo struct{ db *gorm.DB }

// NewMalwareQuarantineRepository returns a GORM-backed quarantine repo.
func NewMalwareQuarantineRepository(db *gorm.DB) MalwareQuarantineRepository {
	return &malwareQuarantineRepo{db: db}
}

func (r *malwareQuarantineRepo) Create(ctx context.Context, q *models.MalwareQuarantine) error {
	if err := r.db.WithContext(ctx).Create(q).Error; err != nil {
		return translate(err)
	}
	return nil
}

func (r *malwareQuarantineRepo) Get(ctx context.Context, id string) (*models.MalwareQuarantine, error) {
	var out models.MalwareQuarantine
	if err := r.db.WithContext(ctx).First(&out, "id = ?", id).Error; err != nil {
		return nil, translate(err)
	}
	return &out, nil
}

func (r *malwareQuarantineRepo) ListActive(ctx context.Context, limit, offset int) ([]models.MalwareQuarantine, int64, error) {
	var (
		out   []models.MalwareQuarantine
		total int64
	)
	q := r.db.WithContext(ctx).
		Model(&models.MalwareQuarantine{}).
		Where("deleted_at IS NULL AND restored_at IS NULL")
	if err := q.Count(&total).Error; err != nil {
		return nil, 0, translate(err)
	}
	if limit <= 0 {
		limit = 50
	}
	if err := q.Order("detected_at DESC").
		Limit(limit).Offset(offset).
		Find(&out).Error; err != nil {
		return nil, 0, translate(err)
	}
	return out, total, nil
}

func (r *malwareQuarantineRepo) Restore(ctx context.Context, id, restoredBy, reason string) error {
	now := time.Now().UTC()
	res := r.db.WithContext(ctx).
		Model(&models.MalwareQuarantine{}).
		Where("id = ? AND restored_at IS NULL AND deleted_at IS NULL", id).
		Updates(map[string]any{
			"restored_at":     now,
			"restored_by":     restoredBy,
			"restored_reason": reason,
		})
	if res.Error != nil {
		return translate(res.Error)
	}
	if res.RowsAffected == 0 {
		return ErrNotFound
	}
	return nil
}

func (r *malwareQuarantineRepo) MarkDeleted(ctx context.Context, id string) error {
	now := time.Now().UTC()
	res := r.db.WithContext(ctx).
		Model(&models.MalwareQuarantine{}).
		Where("id = ? AND deleted_at IS NULL", id).
		Update("deleted_at", now)
	if res.Error != nil {
		return translate(res.Error)
	}
	if res.RowsAffected == 0 {
		return ErrNotFound
	}
	return nil
}

// PurgeOlderThan hard-deletes rows whose detected_at is before cutoff
// AND that are already either restored or marked-deleted. Active rows
// older than retention are kept — operator must restore or delete them
// first; we never silently lose evidence.
func (r *malwareQuarantineRepo) PurgeOlderThan(ctx context.Context, cutoff time.Time) (int64, error) {
	res := r.db.WithContext(ctx).
		Where("detected_at < ? AND (deleted_at IS NOT NULL OR restored_at IS NOT NULL)", cutoff).
		Delete(&models.MalwareQuarantine{})
	if res.Error != nil {
		return 0, translate(res.Error)
	}
	return res.RowsAffected, nil
}
