package service

import (
	"bytes"
	"context"
	"errors"

	"gorm.io/gorm"

	airrescuechecklist "wucher/internal/domain/air_rescue_checklist"
)

type AirRescuerChecklistService struct {
	repo airrescuechecklist.Repository
}

func NewAirRescuerChecklistService(repo airrescuechecklist.Repository) *AirRescuerChecklistService {
	return &AirRescuerChecklistService{repo: repo}
}

func (s *AirRescuerChecklistService) CreateBulk(ctx context.Context, rows []airrescuechecklist.ChecklistCreateInput) error {
	if len(rows) == 0 {
		return nil
	}
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		for i := range rows {
			header := &rows[i].Checklist
			if header.Position <= 0 {
				nextPos, err := txRepo.NextChecklistPosition(ctx, header.HEMSBaseID, header.ScopeCode)
				if err != nil {
					return err
				}
				header.Position = nextPos
			}
			if err := txRepo.CreateChecklist(ctx, header); err != nil {
				return err
			}

			var nextItemPos int
			itemNextInitialized := false
			for j := range rows[i].Items {
				item := &rows[i].Items[j]
				item.ChecklistID = append([]byte(nil), header.ID...)
				if item.Position <= 0 {
					if !itemNextInitialized {
						pos, err := txRepo.NextChecklistItemPosition(ctx, header.ID)
						if err != nil {
							return err
						}
						nextItemPos = pos
						itemNextInitialized = true
					}
					item.Position = nextItemPos
					nextItemPos++
				}
				if err := txRepo.CreateChecklistItem(ctx, item); err != nil {
					return err
				}
			}
		}
		return nil
	})
}

func (s *AirRescuerChecklistService) UpdateChecklist(ctx context.Context, row *airrescuechecklist.AirRescuerChecklist) error {
	if row == nil {
		return gorm.ErrInvalidData
	}
	row.Position = normalizeChecklistPosition(row.Position)
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, row.ID); err != nil {
			return err
		}
		return txRepo.UpdateChecklist(ctx, row)
	})
}

func (s *AirRescuerChecklistService) UpdateChecklistWithNewItems(
	ctx context.Context,
	row *airrescuechecklist.AirRescuerChecklist,
	newItems []airrescuechecklist.AirRescuerChecklistItem,
) error {
	if row == nil && len(newItems) == 0 {
		return gorm.ErrInvalidData
	}

	checklistID := []byte(nil)
	if row != nil {
		row.Position = normalizeChecklistPosition(row.Position)
		checklistID = append([]byte(nil), row.ID...)
	}
	if len(checklistID) == 0 && len(newItems) > 0 {
		checklistID = append([]byte(nil), newItems[0].ChecklistID...)
	}
	if len(checklistID) == 0 {
		return gorm.ErrInvalidData
	}

	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, checklistID); err != nil {
			return err
		}

		if row != nil {
			if err := txRepo.UpdateChecklist(ctx, row); err != nil {
				return err
			}
		}

		if len(newItems) == 0 {
			return nil
		}
		nextItemPos := 0
		itemNextInitialized := false
		for i := range newItems {
			newItems[i].ChecklistID = append([]byte(nil), checklistID...)
			if newItems[i].Position <= 0 {
				if !itemNextInitialized {
					pos, err := txRepo.NextChecklistItemPosition(ctx, checklistID)
					if err != nil {
						return err
					}
					nextItemPos = pos
					itemNextInitialized = true
				}
				newItems[i].Position = nextItemPos
				nextItemPos++
			}
		}
		return txRepo.CreateChecklistItems(ctx, newItems)
	})
}

func (s *AirRescuerChecklistService) DeleteChecklist(ctx context.Context, id []byte, deletedBy []byte) error {
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, id); err != nil {
			return err
		}
		if err := txRepo.SoftDeleteChecklist(ctx, id, deletedBy); err != nil {
			return err
		}
		return txRepo.SoftDeleteItemsByChecklistID(ctx, id, deletedBy)
	})
}

func (s *AirRescuerChecklistService) CreateChecklistItem(ctx context.Context, row *airrescuechecklist.AirRescuerChecklistItem) error {
	if row == nil {
		return gorm.ErrInvalidData
	}
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, row.ChecklistID); err != nil {
			return err
		}
		if row.Position <= 0 {
			nextPos, err := txRepo.NextChecklistItemPosition(ctx, row.ChecklistID)
			if err != nil {
				return err
			}
			row.Position = nextPos
		}
		return txRepo.CreateChecklistItem(ctx, row)
	})
}

func (s *AirRescuerChecklistService) CreateChecklistItems(ctx context.Context, checklistID []byte, rows []airrescuechecklist.AirRescuerChecklistItem) error {
	if len(rows) == 0 {
		return nil
	}
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, checklistID); err != nil {
			return err
		}
		nextItemPos := 0
		itemNextInitialized := false
		for i := range rows {
			rows[i].ChecklistID = append([]byte(nil), checklistID...)
			if rows[i].Position <= 0 {
				if !itemNextInitialized {
					pos, err := txRepo.NextChecklistItemPosition(ctx, checklistID)
					if err != nil {
						return err
					}
					nextItemPos = pos
					itemNextInitialized = true
				}
				rows[i].Position = nextItemPos
				nextItemPos++
			}
		}
		return txRepo.CreateChecklistItems(ctx, rows)
	})
}

func (s *AirRescuerChecklistService) ReorderChecklistItems(
	ctx context.Context,
	checklistID []byte,
	rows []airrescuechecklist.ChecklistItemReorderInput,
	updatedBy []byte,
) error {
	if len(rows) == 0 {
		return gorm.ErrInvalidData
	}

	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistByID(ctx, checklistID); err != nil {
			return err
		}

		seenItemIDs := make(map[string]struct{}, len(rows))
		for i := range rows {
			itemID := rows[i].ID
			if len(itemID) == 0 {
				return gorm.ErrInvalidData
			}
			itemKey := string(itemID)
			if _, exists := seenItemIDs[itemKey]; exists {
				return errors.New("duplicate item_uuid in reorder payload")
			}
			seenItemIDs[itemKey] = struct{}{}

			current, err := txRepo.LockChecklistItemByID(ctx, itemID)
			if err != nil {
				return err
			}
			if !bytes.Equal(current.ChecklistID, checklistID) {
				return gorm.ErrRecordNotFound
			}

			if err := txRepo.UpdateChecklistItem(ctx, &airrescuechecklist.AirRescuerChecklistItem{
				ID:        append([]byte(nil), itemID...),
				Name:      current.Name,
				Position:  normalizeChecklistPosition(rows[i].Position),
				UpdatedBy: append([]byte(nil), updatedBy...),
			}); err != nil {
				return err
			}
		}
		return nil
	})
}

func (s *AirRescuerChecklistService) UpdateChecklistItem(ctx context.Context, row *airrescuechecklist.AirRescuerChecklistItem) error {
	if row == nil {
		return gorm.ErrInvalidData
	}
	row.Position = normalizeChecklistPosition(row.Position)
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistItemByID(ctx, row.ID); err != nil {
			return err
		}
		return txRepo.UpdateChecklistItem(ctx, row)
	})
}

func (s *AirRescuerChecklistService) DeleteChecklistItem(ctx context.Context, id []byte, deletedBy []byte) error {
	return s.repo.InTx(ctx, func(txRepo airrescuechecklist.TxRepository) error {
		if _, err := txRepo.LockChecklistItemByID(ctx, id); err != nil {
			return err
		}
		return txRepo.SoftDeleteChecklistItem(ctx, id, deletedBy)
	})
}

func (s *AirRescuerChecklistService) GetChecklistByID(ctx context.Context, id []byte) (*airrescuechecklist.ChecklistHeaderView, error) {
	return s.repo.GetChecklistByID(ctx, id)
}

func (s *AirRescuerChecklistService) GetChecklistItemByID(ctx context.Context, id []byte) (*airrescuechecklist.ChecklistItemView, error) {
	return s.repo.GetChecklistItemByID(ctx, id)
}

func (s *AirRescuerChecklistService) ListChecklistHeaders(ctx context.Context, filter airrescuechecklist.ChecklistHeaderFilter) ([]airrescuechecklist.ChecklistHeaderView, error) {
	return s.repo.ListChecklistHeaders(ctx, filter)
}

func (s *AirRescuerChecklistService) ListChecklistItemsByChecklistIDs(ctx context.Context, checklistIDs [][]byte) ([]airrescuechecklist.ChecklistItemView, error) {
	return s.repo.ListChecklistItemsByChecklistIDs(ctx, checklistIDs)
}

func (s *AirRescuerChecklistService) ListHEMSBases(ctx context.Context, filter airrescuechecklist.HEMSBaseFilter) ([]airrescuechecklist.HEMSBaseView, error) {
	return s.repo.ListHEMSBases(ctx, filter)
}

func normalizeChecklistPosition(v int) int {
	if v <= 0 {
		return 1
	}
	return v
}
