package service

import (
	"errors"
	"fmt"
	"netidhub-saas-be/app/database/entity"
	"netidhub-saas-be/app/module/approval_workflow_steps/repository"
	"netidhub-saas-be/app/module/approval_workflow_steps/request"
	workflowRepo "netidhub-saas-be/app/module/approval_workflows/repository"
	usersRepo "netidhub-saas-be/app/module/users/repository"
	"netidhub-saas-be/utils/paginator"
	utilSvc "netidhub-saas-be/utils/service"

	"github.com/google/uuid"
	"github.com/rs/zerolog"
)

type approvalWorkflowStepsService struct {
	ApprovalWorkflowStepsRepository repository.ApprovalWorkflowStepsRepository
	ApprovalWorkflowsRepository     workflowRepo.ApprovalWorkflowsRepository
	UsersRepository                 usersRepo.UsersRepository
	Log                             zerolog.Logger
}

// ApprovalWorkflowStepsService define interface of IApprovalWorkflowStepsService
type ApprovalWorkflowStepsService interface {
	// Basic CRUD
	GetAll(authToken string, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error)
	FindOne(authToken string, id uint) (step *entity.ApprovalWorkflowSteps, err error)
	Create(authToken string, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error)
	Update(authToken string, id uint, step *entity.ApprovalWorkflowSteps) (err error)
	Delete(authToken string, id uint) (err error)

	// Workflow steps management
	GetByWorkflowID(authToken string, workflowID uint) (steps []*entity.ApprovalWorkflowSteps, err error)
	// GetByRoleID(authToken string, roleID uint) (steps []*entity.ApprovalWorkflowSteps, err error) // Not implemented yet
	BulkCreate(authToken string, workflowID uint, steps []*entity.ApprovalWorkflowSteps) (stepsReturn []*entity.ApprovalWorkflowSteps, err error)
	ReorderSteps(authToken string, workflowID uint, stepOrders []struct {
		ID        uint
		StepOrder int
	}) (err error)

	// Validation
	ValidateStep(authToken string, step *entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error)
	CanDeleteStep(authToken string, id uint) (canDelete bool, reason string, err error)
	ValidateStepOrder(authToken string, workflowID uint, stepOrder int, excludeID *uint) (isValid bool, err error)
}

func NewApprovalWorkflowStepsService(
	approvalWorkflowStepsRepository repository.ApprovalWorkflowStepsRepository,
	approvalWorkflowsRepository workflowRepo.ApprovalWorkflowsRepository,
	usersRepository usersRepo.UsersRepository,
	log zerolog.Logger,
) ApprovalWorkflowStepsService {
	return &approvalWorkflowStepsService{
		ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
		ApprovalWorkflowsRepository:     approvalWorkflowsRepository,
		UsersRepository:                 usersRepository,
		Log:                             log,
	}
}

func (_i *approvalWorkflowStepsService) GetAll(authToken string, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return nil, paginator.Pagination{}, errors.New("clientId not found in auth token")
	}

	return _i.ApprovalWorkflowStepsRepository.GetAll(clientId, req)
}

func (_i *approvalWorkflowStepsService) FindOne(authToken string, id uint) (step *entity.ApprovalWorkflowSteps, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return nil, errors.New("clientId not found in auth token")
	}

	return _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
}

func (_i *approvalWorkflowStepsService) Create(authToken string, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return nil, errors.New("clientId not found in auth token")
	}

	// Validate workflow exists
	workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, step.WorkflowId)
	if err != nil {
		return nil, fmt.Errorf("workflow not found: %w", err)
	}
	if workflow == nil {
		return nil, errors.New("workflow not found")
	}

	// Validate step order is unique within workflow
	isValid, err := _i.ValidateStepOrder(authToken, step.WorkflowId, step.StepOrder, nil)
	if err != nil {
		return nil, err
	}
	if !isValid {
		return nil, errors.New("step order already exists in this workflow")
	}

	// Validate step data
	isValid, validationErrors, err := _i.ValidateStep(authToken, step)
	if err != nil {
		return nil, err
	}
	if !isValid {
		return nil, fmt.Errorf("validation failed: %v", validationErrors)
	}

	return _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
}

func (_i *approvalWorkflowStepsService) Update(authToken string, id uint, step *entity.ApprovalWorkflowSteps) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return errors.New("clientId not found in auth token")
	}

	// Check if step exists
	existingStep, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}
	if existingStep == nil {
		return errors.New("step not found")
	}

	// If step order is being changed, validate it's unique
	if step.StepOrder != 0 && step.StepOrder != existingStep.StepOrder {
		isValid, err := _i.ValidateStepOrder(authToken, existingStep.WorkflowId, step.StepOrder, &id)
		if err != nil {
			return err
		}
		if !isValid {
			return errors.New("step order already exists in this workflow")
		}
	}

	return _i.ApprovalWorkflowStepsRepository.Update(id, step)
}

func (_i *approvalWorkflowStepsService) Delete(authToken string, id uint) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return errors.New("clientId not found in auth token")
	}

	// Check if step can be deleted
	canDelete, reason, err := _i.CanDeleteStep(authToken, id)
	if err != nil {
		return err
	}
	if !canDelete {
		return fmt.Errorf("cannot delete step: %s", reason)
	}

	return _i.ApprovalWorkflowStepsRepository.Delete(clientId, id)
}

func (_i *approvalWorkflowStepsService) GetByWorkflowID(authToken string, workflowID uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return nil, errors.New("clientId not found in auth token")
	}

	return _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, workflowID)
}

// GetByRoleID method is not implemented in repository yet
// func (_i *approvalWorkflowStepsService) GetByRoleID(authToken string, roleID uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
//	return _i.ApprovalWorkflowStepsRepository.GetByRoleID(clientId, roleID)
// }

func (_i *approvalWorkflowStepsService) BulkCreate(authToken string, workflowID uint, steps []*entity.ApprovalWorkflowSteps) (stepsReturn []*entity.ApprovalWorkflowSteps, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return nil, errors.New("clientId not found in auth token")
	}

	// Validate workflow exists
	workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, workflowID)
	if err != nil {
		return nil, fmt.Errorf("workflow not found: %w", err)
	}
	if workflow == nil {
		return nil, errors.New("workflow not found")
	}

	// Validate all steps
	stepOrders := make(map[int]bool)
	for i, step := range steps {
		step.WorkflowId = workflowID

		// Check for duplicate step orders within the batch
		if stepOrders[step.StepOrder] {
			return nil, fmt.Errorf("duplicate step order %d in batch", step.StepOrder)
		}
		stepOrders[step.StepOrder] = true

		// Validate step order is unique in database
		isValid, err := _i.ValidateStepOrder(authToken, workflowID, step.StepOrder, nil)
		if err != nil {
			return nil, err
		}
		if !isValid {
			return nil, fmt.Errorf("step order %d already exists in workflow", step.StepOrder)
		}

		// Validate step data
		var errors []string
		if step.RequiredUserLevelId == 0 {
			errors = append(errors, fmt.Sprintf("Step %d: RequiredUserLevelId is required", i+1))
		}

		if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours <= 0 {
			errors = append(errors, fmt.Sprintf("Step %d: AutoApproveAfterHours must be positive", i+1))
		}

		if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours > 720 {
			errors = append(errors, fmt.Sprintf("Step %d: AutoApproveAfterHours cannot exceed 720 hours (30 days)", i+1))
		}

		if len(step.StepName) > 500 {
			errors = append(errors, fmt.Sprintf("Step %d: StepName cannot exceed 500 characters", i+1))
		}

		if len(errors) > 0 {
			return nil, fmt.Errorf("validation failed for step %d: %v", step.StepOrder, errors)
		}
	}

	// BulkCreate method is not implemented in repository yet
	// return _i.ApprovalWorkflowStepsRepository.BulkCreate(clientId, steps)
	return nil, fmt.Errorf("BulkCreate method not implemented yet")
}

func (_i *approvalWorkflowStepsService) ReorderSteps(authToken string, workflowID uint, stepOrders []struct {
	ID        uint
	StepOrder int
}) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return errors.New("clientId not found in auth token")
	}

	// Validate workflow exists
	workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, workflowID)
	if err != nil {
		return fmt.Errorf("workflow not found: %w", err)
	}
	if workflow == nil {
		return errors.New("workflow not found")
	}

	// Validate all steps belong to the workflow and orders are unique
	orders := make(map[int]bool)
	for _, stepOrder := range stepOrders {
		// Check step exists and belongs to workflow
		step, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, stepOrder.ID)
		if err != nil {
			return err
		}
		if step == nil {
			return fmt.Errorf("step with ID %d not found", stepOrder.ID)
		}
		if step.WorkflowId != workflowID {
			return fmt.Errorf("step with ID %d does not belong to workflow %d", stepOrder.ID, workflowID)
		}

		// Check for duplicate orders
		if orders[stepOrder.StepOrder] {
			return fmt.Errorf("duplicate step order %d", stepOrder.StepOrder)
		}
		orders[stepOrder.StepOrder] = true
	}

	// Convert stepOrders to the format expected by repository
	stepOrderMaps := make([]map[string]interface{}, len(stepOrders))
	for i, stepOrder := range stepOrders {
		stepOrderMaps[i] = map[string]interface{}{
			"id":         stepOrder.ID,
			"step_order": stepOrder.StepOrder,
		}
	}

	return _i.ApprovalWorkflowStepsRepository.ReorderSteps(clientId, workflowID, stepOrderMaps)
}

func (_i *approvalWorkflowStepsService) ValidateStep(authToken string, step *entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) {
	var validationErrors []string

	// Validate step name
	if step.StepName == "" {
		validationErrors = append(validationErrors, "step name is required")
	}
	if len(step.StepName) < 3 {
		validationErrors = append(validationErrors, "step name must be at least 3 characters")
	}
	if len(step.StepName) > 500 {
		validationErrors = append(validationErrors, "step name must not exceed 500 characters")
	}

	// Validate step order
	if step.StepOrder < 1 {
		validationErrors = append(validationErrors, "step order must be at least 1")
	}

	// Validate required user level ID
	if step.RequiredUserLevelId == 0 {
		validationErrors = append(validationErrors, "required user level ID is required")
	}

	// Validate auto approve after hours if provided
	if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours < 1 {
		validationErrors = append(validationErrors, "auto approve after hours must be at least 1")
	}
	if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours > 720 {
		validationErrors = append(validationErrors, "auto approve after hours must not exceed 720 (30 days)")
	}

	return len(validationErrors) == 0, validationErrors, nil
}

func (_i *approvalWorkflowStepsService) CanDeleteStep(authToken string, id uint) (canDelete bool, reason string, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return false, "clientId not found in auth token", errors.New("clientId not found in auth token")
	}

	// Check if step exists
	step, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
	if err != nil {
		return false, "", err
	}
	if step == nil {
		return false, "step not found", nil
	}

	// Check if there are any active approval flows using this step
	// This would require checking article_approval_step_logs table
	// For now, we'll allow deletion but this should be implemented
	// based on business requirements

	return true, "", nil
}

func (_i *approvalWorkflowStepsService) ValidateStepOrder(authToken string, workflowID uint, stepOrder int, excludeID *uint) (isValid bool, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if clientId == nil {
		return false, errors.New("clientId not found in auth token")
	}

	existingStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, workflowID, stepOrder)
	if err != nil {
		return false, err
	}

	// If no existing step found, order is valid
	if existingStep == nil {
		return true, nil
	}

	// If excludeID is provided and matches existing step, order is valid (updating same step)
	if excludeID != nil && existingStep.ID == *excludeID {
		return true, nil
	}

	// Order already exists for different step
	return false, nil
}
