package service

import (
	"context"
	"sort"

	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"
	"eslogad-be/internal/repository"
	"eslogad-be/internal/transformer"

	"github.com/google/uuid"
)

type DispositionRouteServiceImpl struct {
	repo *repository.DispositionRouteRepository
}

func NewDispositionRouteService(repo *repository.DispositionRouteRepository) *DispositionRouteServiceImpl {
	return &DispositionRouteServiceImpl{repo: repo}
}

// CreateOrUpdate handles bulk create or update of disposition routes
func (s *DispositionRouteServiceImpl) CreateOrUpdate(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.BulkCreateDispositionRouteResponse, error) {
	// Set default values
	isActive := true
	if req.IsActive != nil {
		isActive = *req.IsActive
	}

	var allowedActions entities.JSONB
	if req.AllowedActions != nil {
		allowedActions = entities.JSONB(*req.AllowedActions)
	}

	// Perform bulk upsert
	created, updated, err := s.repo.BulkUpsert(ctx, req.FromDepartmentID, req.ToDepartmentIDs, isActive, allowedActions)
	if err != nil {
		return nil, err
	}

	// Fetch all routes for the from_department_id to return
	routes, err := s.repo.ListByFromDept(ctx, req.FromDepartmentID)
	if err != nil {
		return nil, err
	}

	// Transform to response
	routeResponses := transformer.DispositionRoutesToContract(routes)

	return &contract.BulkCreateDispositionRouteResponse{
		Created: created,
		Updated: updated,
		Routes:  routeResponses,
	}, nil
}

// Create maintains backward compatibility for single route creation
func (s *DispositionRouteServiceImpl) Create(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.DispositionRouteResponse, error) {
	// If only one to_department_id is provided, create a single route
	if len(req.ToDepartmentIDs) == 1 {
		entity := &entities.DispositionRoute{
			FromDepartmentID: req.FromDepartmentID,
			ToDepartmentID:   req.ToDepartmentIDs[0],
		}
		if req.IsActive != nil {
			entity.IsActive = *req.IsActive
		} else {
			entity.IsActive = true
		}
		if req.AllowedActions != nil {
			entity.AllowedActions = entities.JSONB(*req.AllowedActions)
		}

		// Use upsert to handle create or update
		if err := s.repo.Upsert(ctx, entity); err != nil {
			return nil, err
		}

		// Fetch the created/updated route
		route, err := s.repo.Get(ctx, entity.ID)
		if err != nil {
			// If we can't get by ID (new creation), try to get by from/to combination
			routes, err := s.repo.ListByFromDept(ctx, req.FromDepartmentID)
			if err != nil {
				return nil, err
			}
			for _, r := range routes {
				if r.ToDepartmentID == req.ToDepartmentIDs[0] {
					resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{r})[0]
					return &resp, nil
				}
			}
		}

		resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*route})[0]
		return &resp, nil
	}

	// For multiple to_department_ids, use bulk create/update
	bulkResp, err := s.CreateOrUpdate(ctx, req)
	if err != nil {
		return nil, err
	}

	// Return the first route as response for backward compatibility
	if len(bulkResp.Routes) > 0 {
		return &bulkResp.Routes[0], nil
	}

	return nil, nil
}
func (s *DispositionRouteServiceImpl) Update(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionRouteRequest) (*contract.DispositionRouteResponse, error) {
	entity, err := s.repo.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	if req.IsActive != nil {
		entity.IsActive = *req.IsActive
	}
	if req.AllowedActions != nil {
		entity.AllowedActions = entities.JSONB(*req.AllowedActions)
	}
	if err := s.repo.Update(ctx, entity); err != nil {
		return nil, err
	}
	resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*entity})[0]
	return &resp, nil
}
func (s *DispositionRouteServiceImpl) Get(ctx context.Context, id uuid.UUID) (*contract.DispositionRouteResponse, error) {
	entity, err := s.repo.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	resp := transformer.DispositionRoutesToContract([]entities.DispositionRoute{*entity})[0]
	return &resp, nil
}
func (s *DispositionRouteServiceImpl) ListByFromDept(ctx context.Context, from uuid.UUID) (*contract.ListDispositionRoutesResponse, error) {
	list, err := s.repo.ListByFromDept(ctx, from)
	if err != nil {
		return nil, err
	}
	return &contract.ListDispositionRoutesResponse{Routes: transformer.DispositionRoutesToContract(list)}, nil
}
func (s *DispositionRouteServiceImpl) SetActive(ctx context.Context, id uuid.UUID, active bool) error {
	return s.repo.SetActive(ctx, id, active)
}

// ListGrouped returns all disposition routes grouped by from_department_id with clean department structure
func (s *DispositionRouteServiceImpl) ListGrouped(ctx context.Context) (*contract.ListDispositionRoutesGroupedResponse, error) {
	// Get routes with department details
	routes, err := s.repo.ListAllGroupedWithDepartments(ctx)
	if err != nil {
		return nil, err
	}

	// Group routes by from_department_id and collect department info
	type groupedData struct {
		fromDept  contract.DepartmentMapping
		toDepts   []contract.DepartmentMapping
		toDeptMap map[uuid.UUID]bool // To avoid duplicates
	}

	grouped := make(map[uuid.UUID]*groupedData)

	for _, route := range routes {
		if _, exists := grouped[route.FromDepartmentID]; !exists {
			grouped[route.FromDepartmentID] = &groupedData{
				fromDept: contract.DepartmentMapping{
					ID:   route.FromDepartmentID,
					Name: route.FromDepartment.Name,
				},
				toDepts:   []contract.DepartmentMapping{},
				toDeptMap: make(map[uuid.UUID]bool),
			}
		}

		// Add to_department if not already added (avoid duplicates)
		if !grouped[route.FromDepartmentID].toDeptMap[route.ToDepartmentID] {
			grouped[route.FromDepartmentID].toDepts = append(
				grouped[route.FromDepartmentID].toDepts,
				contract.DepartmentMapping{
					ID:   route.ToDepartmentID,
					Name: route.ToDepartment.Name,
				},
			)
			grouped[route.FromDepartmentID].toDeptMap[route.ToDepartmentID] = true
		}
	}

	// Convert to response format
	var dispositions []contract.DispositionRouteGroupedItem
	for _, data := range grouped {
		dispositions = append(dispositions, contract.DispositionRouteGroupedItem{
			FromDepartment: data.fromDept,
			ToDepartments:  data.toDepts,
		})
	}

	// Sort by from department name for consistent ordering
	sort.Slice(dispositions, func(i, j int) bool {
		return dispositions[i].FromDepartment.Name < dispositions[j].FromDepartment.Name
	})

	return &contract.ListDispositionRoutesGroupedResponse{
		Dispositions: dispositions,
	}, nil
}

// ListAll returns all disposition routes with department details
func (s *DispositionRouteServiceImpl) ListAll(ctx context.Context) (*contract.ListDispositionRoutesDetailedResponse, error) {
	routes, err := s.repo.ListAll(ctx)
	if err != nil {
		return nil, err
	}

	routeResponses := transformer.DispositionRoutesToContract(routes)

	return &contract.ListDispositionRoutesDetailedResponse{
		Routes: routeResponses,
		Total:  len(routeResponses),
	}, nil
}
