package repository

import (
	"context"

	"eslogad-be/internal/entities"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type DispositionRouteRepository struct{ db *gorm.DB }

func NewDispositionRouteRepository(db *gorm.DB) *DispositionRouteRepository {
	return &DispositionRouteRepository{db: db}
}

func (r *DispositionRouteRepository) Create(ctx context.Context, e *entities.DispositionRoute) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}

// Upsert creates or updates a disposition route based on from_department_id and to_department_id
func (r *DispositionRouteRepository) Upsert(ctx context.Context, e *entities.DispositionRoute) error {
	db := DBFromContext(ctx, r.db)

	// Check if route exists
	var existing entities.DispositionRoute
	err := db.WithContext(ctx).
		Where("from_department_id = ? AND to_department_id = ?", e.FromDepartmentID, e.ToDepartmentID).
		First(&existing).Error

	if err == gorm.ErrRecordNotFound {
		// Create new route
		return db.WithContext(ctx).Create(e).Error
	} else if err != nil {
		return err
	}

	// Update existing route
	e.ID = existing.ID
	return db.WithContext(ctx).Model(&entities.DispositionRoute{}).
		Where("id = ?", existing.ID).
		Updates(e).Error
}

// BulkUpsert performs bulk create or update for multiple routes
func (r *DispositionRouteRepository) BulkUpsert(ctx context.Context, fromDeptID uuid.UUID, toDeptIDs []uuid.UUID, isActive bool, allowedActions entities.JSONB) (created int, updated int, err error) {
	db := DBFromContext(ctx, r.db)

	// Start transaction
	tx := db.WithContext(ctx).Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		}
	}()

	// Get existing routes for this from_department_id
	var existingRoutes []entities.DispositionRoute
	if err = tx.Where("from_department_id = ?", fromDeptID).Find(&existingRoutes).Error; err != nil {
		return 0, 0, err
	}

	// Create map of existing routes
	existingMap := make(map[uuid.UUID]entities.DispositionRoute)
	for _, route := range existingRoutes {
		existingMap[route.ToDepartmentID] = route
	}

	// Process each to_department_id
	for _, toDeptID := range toDeptIDs {
		route := entities.DispositionRoute{
			FromDepartmentID: fromDeptID,
			ToDepartmentID:   toDeptID,
			IsActive:         isActive,
			AllowedActions:   allowedActions,
		}

		if existing, exists := existingMap[toDeptID]; exists {
			// Update existing route
			route.ID = existing.ID
			if err = tx.Model(&entities.DispositionRoute{}).
				Where("id = ?", existing.ID).
				Updates(&route).Error; err != nil {
				return created, updated, err
			}
			updated++
			// Remove from map to track which routes to delete
			delete(existingMap, toDeptID)
		} else {
			// Create new route
			if err = tx.Create(&route).Error; err != nil {
				return created, updated, err
			}
			created++
		}
	}

	// Optionally deactivate routes that are no longer in the list
	// (routes that exist in DB but not in the new list)
	for _, oldRoute := range existingMap {
		if err = tx.Model(&entities.DispositionRoute{}).
			Where("id = ?", oldRoute.ID).
			Update("is_active", false).Error; err != nil {
			return created, updated, err
		}
	}

	// Commit transaction
	if err = tx.Commit().Error; err != nil {
		return 0, 0, err
	}

	return created, updated, nil
}
func (r *DispositionRouteRepository) Update(ctx context.Context, e *entities.DispositionRoute) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Model(&entities.DispositionRoute{}).Where("id = ?", e.ID).Updates(e).Error
}

func (r *DispositionRouteRepository) Get(ctx context.Context, id uuid.UUID) (*entities.DispositionRoute, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.DispositionRoute
	if err := db.WithContext(ctx).
		Preload("FromDepartment").
		Preload("ToDepartment").
		First(&e, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &e, nil
}
func (r *DispositionRouteRepository) ListByFromDept(ctx context.Context, fromDept uuid.UUID) ([]entities.DispositionRoute, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.DispositionRoute
	if err := db.WithContext(ctx).Where("from_department_id = ? and is_active=true", fromDept).
		Preload("FromDepartment").
		Preload("ToDepartment").
		Order("to_department_id").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *DispositionRouteRepository) IsEligibleForDisposition(ctx context.Context, fromDept uuid.UUID) (bool, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.DispositionRoute
	if err := db.WithContext(ctx).Where("from_department_id = ?", fromDept).Find(&list).Error; err != nil {
		return false, err
	}
	return len(list) > 0, nil
}

func (r *DispositionRouteRepository) SetActive(ctx context.Context, id uuid.UUID, isActive bool) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Model(&entities.DispositionRoute{}).Where("id = ?", id).Update("is_active", isActive).Error
}

// ListAllGrouped returns all disposition routes grouped by from_department_id
func (r *DispositionRouteRepository) ListAllGrouped(ctx context.Context) (map[uuid.UUID][]uuid.UUID, error) {
	db := DBFromContext(ctx, r.db)
	var routes []entities.DispositionRoute

	if err := db.WithContext(ctx).
		Where("is_active = ?", true).
		Order("from_department_id, to_department_id").
		Find(&routes).Error; err != nil {
		return nil, err
	}

	// Group by from_department_id
	grouped := make(map[uuid.UUID][]uuid.UUID)
	for _, route := range routes {
		grouped[route.FromDepartmentID] = append(grouped[route.FromDepartmentID], route.ToDepartmentID)
	}

	return grouped, nil
}

// ListAllGroupedWithDepartments returns all disposition routes grouped by from_department_id with department details
func (r *DispositionRouteRepository) ListAllGroupedWithDepartments(ctx context.Context) ([]entities.DispositionRoute, error) {
	db := DBFromContext(ctx, r.db)
	var routes []entities.DispositionRoute

	if err := db.WithContext(ctx).
		Preload("FromDepartment").
		Preload("ToDepartment").
		Where("is_active = ?", true).
		Order("from_department_id, to_department_id").
		Find(&routes).Error; err != nil {
		return nil, err
	}

	return routes, nil
}

// ListAll returns all disposition routes with department details
func (r *DispositionRouteRepository) ListAll(ctx context.Context) ([]entities.DispositionRoute, error) {
	db := DBFromContext(ctx, r.db)
	var routes []entities.DispositionRoute

	if err := db.WithContext(ctx).
		Preload("FromDepartment").
		Preload("ToDepartment").
		Where("is_active = ?", true).
		Order("from_department_id, to_department_id").
		Find(&routes).Error; err != nil {
		return nil, err
	}

	return routes, nil
}
