package mysql

import (
	"context"
	"errors"
	"time"

	"gorm.io/gorm"

	"wucher/internal/domain/operation"
)

type HEMSOperationalDataRepository struct {
	db *gorm.DB
}

func NewHEMSOperationalDataRepository(db *gorm.DB) *HEMSOperationalDataRepository {
	return &HEMSOperationalDataRepository{db: db}
}

func (r *HEMSOperationalDataRepository) Create(ctx context.Context, row *operation.HEMSOperationalData) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		forcePresents := append([]operation.OperationalDataForcePresent(nil), row.ForcePresents...)
		files := append([]operation.OperationalFile(nil), row.Files...)
		row.ForcePresents = nil
		row.Files = nil

		if err := tx.Create(row).Error; err != nil {
			return err
		}

		for i := range forcePresents {
			forcePresents[i].OperationalDataID = row.ID
			if err := tx.Create(&forcePresents[i]).Error; err != nil {
				return err
			}
		}
		for i := range files {
			files[i].OperationalDataID = row.ID
			if err := tx.Create(&files[i]).Error; err != nil {
				return err
			}
		}
		row.ForcePresents = forcePresents
		row.Files = files
		return nil
	})
}

func (r *HEMSOperationalDataRepository) GetByID(ctx context.Context, id []byte) (*operation.HEMSOperationalData, error) {
	var row operation.HEMSOperationalData
	err := r.db.WithContext(ctx).
		Preload("ForcePresents").
		Preload("Files").
		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 *HEMSOperationalDataRepository) Update(ctx context.Context, row *operation.HEMSOperationalData) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		forcePresents := append([]operation.OperationalDataForcePresent(nil), row.ForcePresents...)
		files := append([]operation.OperationalFile(nil), row.Files...)
		row.ForcePresents = nil
		row.Files = nil

		if err := tx.Model(&operation.HEMSOperationalData{}).
			Where("id = ? AND deleted_at IS NULL", row.ID).
			Updates(map[string]any{
				"time":                row.Time,
				"land_id":             nullableBytes(row.LandID),
				"vocation_id":         nullableBytes(row.VocationID),
				"state_id":            nullableBytes(row.StateID),
				"location":            row.Location,
				"postcode":            row.Postcode,
				"notes":               row.Notes,
				"darkness":            row.Darkness,
				"false_information":   row.FalseInformation,
				"fog":                 row.Fog,
				"precipitation":       row.Precipitation,
				"mountain_use":        row.MountainUse,
				"wind":                row.Wind,
				"nfo_search":          row.NFOSearch,
				"landing_site_search": row.LandingSiteSearch,
				"temperature":         row.Temperature,
				"altitude":            row.Altitude,
				"terrain_index":       row.TerrainIndex,
				"rope_recovery":       row.RopeRecovery,
				"night_flight":        row.NightFlight,
				"instrument_flight":   row.InstrumentFlight,
				"additional_info":     row.AdditionalInfo,
				"updated_by":          row.UpdatedBy,
				"updated_at":          time.Now().UTC(),
			}).Error; err != nil {
			return err
		}

		if err := tx.Where("operational_data_id = ?", row.ID).Delete(&operation.OperationalDataForcePresent{}).Error; err != nil {
			return err
		}
		for i := range forcePresents {
			forcePresents[i].OperationalDataID = row.ID
			if err := tx.Create(&forcePresents[i]).Error; err != nil {
				return err
			}
		}
		if err := tx.Where("operational_data_id = ?", row.ID).Delete(&operation.OperationalFile{}).Error; err != nil {
			return err
		}
		for i := range files {
			files[i].OperationalDataID = row.ID
			if err := tx.Create(&files[i]).Error; err != nil {
				return err
			}
		}
		row.ForcePresents = forcePresents
		row.Files = files
		return nil
	})
}

func (r *HEMSOperationalDataRepository) Delete(ctx context.Context, id []byte, deletedBy []byte) error {
	now := time.Now().UTC()
	updates := map[string]any{
		"deleted_at": now,
		"deleted_by": deletedBy,
		"updated_by": deletedBy,
		"updated_at": now,
	}
	return r.db.WithContext(ctx).
		Model(&operation.HEMSOperationalData{}).
		Where("id = ? AND deleted_at IS NULL", id).
		Updates(updates).Error
}

func (r *HEMSOperationalDataRepository) List(ctx context.Context, filter, sort string, limit, offset int) ([]operation.HEMSOperationalData, int64, error) {
	var rows []operation.HEMSOperationalData
	var total int64

	base := r.db.WithContext(ctx).Model(&operation.HEMSOperationalData{}).Where("deleted_at IS NULL")
	if filter != "" {
		like := "%" + filter + "%"
		base = base.Where("location LIKE ? OR notes LIKE ? OR additional_info LIKE ? OR terrain_index LIKE ?", like, like, like, like)
	}

	query := base.Preload("ForcePresents").Preload("Files")
	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.Find(&rows).Error; err != nil {
		return nil, 0, err
	}
	return rows, total, nil
}

func nullableBytes(raw []byte) any {
	if len(raw) == 0 {
		return nil
	}
	return raw
}
