package mysql

import (
	"context"
	"errors"

	"gorm.io/gorm"

	"wucher/internal/domain/mcf"
)

type MCFRepository struct{ db *gorm.DB }

func NewMCFRepository(db *gorm.DB) *MCFRepository { return &MCFRepository{db: db} }

func (r *MCFRepository) Create(ctx context.Context, row *mcf.MaintenanceCheckFlight) error {
	return r.db.WithContext(ctx).Create(row).Error
}

func (r *MCFRepository) Update(ctx context.Context, row *mcf.MaintenanceCheckFlight) error {
	return r.db.WithContext(ctx).Save(row).Error
}

func (r *MCFRepository) Delete(ctx context.Context, id []byte, deletedBy []byte) error {
	return r.db.WithContext(ctx).Model(&mcf.MaintenanceCheckFlight{}).
		Where("id = ? AND deleted_at IS NULL", id).
		Updates(map[string]any{"deleted_at": gorm.Expr("NOW(3)"), "deleted_by": deletedBy, "updated_by": deletedBy}).Error
}

func (r *MCFRepository) GetByID(ctx context.Context, id []byte) (*mcf.MaintenanceCheckFlight, error) {
	var row mcf.MaintenanceCheckFlight
	err := r.db.WithContext(ctx).
		Where("id = ? AND deleted_at IS NULL", id).
		First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &row, err
}

func (r *MCFRepository) GetLatestByHelicopter(ctx context.Context, helicopterID []byte) (*mcf.MaintenanceCheckFlight, error) {
	var row mcf.MaintenanceCheckFlight
	err := r.db.WithContext(ctx).
		Where("helicopter_id = ? AND deleted_at IS NULL", helicopterID).
		Order("created_at DESC").
		First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &row, err
}

func (r *MCFRepository) ListByHelicopter(ctx context.Context, helicopterID []byte) ([]mcf.MaintenanceCheckFlight, error) {
	rows := make([]mcf.MaintenanceCheckFlight, 0)
	err := r.db.WithContext(ctx).
		Where("helicopter_id = ? AND deleted_at IS NULL", helicopterID).
		Order("created_at DESC").
		Find(&rows).Error
	return rows, err
}

// LatestByHelicopterIDs returns the latest (non-deleted) MCF per helicopter, keyed by
// helicopter id (raw bytes as string). Helicopters without an MCF are simply absent.
func (r *MCFRepository) LatestByHelicopterIDs(ctx context.Context, helicopterIDs [][]byte) (map[string]*mcf.MaintenanceCheckFlight, error) {
	out := make(map[string]*mcf.MaintenanceCheckFlight, len(helicopterIDs))
	if len(helicopterIDs) == 0 {
		return out, nil
	}
	rows := make([]mcf.MaintenanceCheckFlight, 0)
	err := r.db.WithContext(ctx).
		Where("helicopter_id IN ? AND deleted_at IS NULL AND id = (SELECT m2.id FROM maintenance_check_flights m2 WHERE m2.helicopter_id = maintenance_check_flights.helicopter_id AND m2.deleted_at IS NULL ORDER BY m2.created_at DESC, m2.id DESC LIMIT 1)", helicopterIDs).
		Find(&rows).Error
	if err != nil {
		return out, err
	}
	for i := range rows {
		out[string(rows[i].HelicopterID)] = &rows[i]
	}
	return out, nil
}

// HelicopterHasOpenMCF reports whether the helicopter has a draft MCF (created but not
// yet completed/signed). Enforces one open MCF per helicopter.
func (r *MCFRepository) HelicopterHasOpenMCF(ctx context.Context, helicopterID []byte) (bool, error) {
	var count int64
	err := r.db.WithContext(ctx).Model(&mcf.MaintenanceCheckFlight{}).
		Where("helicopter_id = ? AND deleted_at IS NULL AND completed_at IS NULL AND cancelled_at IS NULL", helicopterID).
		Count(&count).Error
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (r *MCFRepository) PendingHelicopterIDs(ctx context.Context, helicopterIDs [][]byte) (map[string]bool, error) {
	out := make(map[string]bool)
	if len(helicopterIDs) == 0 {
		return out, nil
	}
	type rowT struct {
		HelicopterID []byte `gorm:"column:helicopter_id"`
	}
	rows := make([]rowT, 0)
	err := r.db.WithContext(ctx).
		Table("maintenance_check_flights m").
		Select("m.helicopter_id").
		Where("m.deleted_at IS NULL AND m.cancelled_at IS NULL AND m.helicopter_id IN ? AND m.id = (SELECT m2.id FROM maintenance_check_flights m2 WHERE m2.helicopter_id = m.helicopter_id AND m2.deleted_at IS NULL AND m2.cancelled_at IS NULL ORDER BY m2.created_at DESC, m2.id DESC LIMIT 1) AND m.completed_at IS NULL", helicopterIDs).
		Scan(&rows).Error
	if err != nil {
		return out, err
	}
	for i := range rows {
		out[string(rows[i].HelicopterID)] = true
	}
	return out, nil
}
