package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"netidhub-saas-be/app/database/entity"
	stepRepo "netidhub-saas-be/app/module/approval_workflow_steps/repository"
	"netidhub-saas-be/app/module/approval_workflows/repository"
	"netidhub-saas-be/app/module/approval_workflows/request"
	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 approvalWorkflowsService struct {
	ApprovalWorkflowsRepository     repository.ApprovalWorkflowsRepository
	ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
	UsersRepository                 usersRepo.UsersRepository
	Log                             zerolog.Logger
}

// ApprovalWorkflowsService define interface of IApprovalWorkflowsService
type ApprovalWorkflowsService interface {
	// Basic CRUD
	GetAll(authToken string, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error)
	FindOne(authToken string, id uint) (workflow *entity.ApprovalWorkflows, err error)
	Create(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
	Update(authToken string, id uint, workflow *entity.ApprovalWorkflows) (err error)
	Delete(authToken string, id uint) (err error)

	// Workflow management
	GetDefault(authToken string) (workflow *entity.ApprovalWorkflows, err error)
	SetDefault(authToken string, id uint) (err error)
	ActivateWorkflow(authToken string, id uint) (err error)
	DeactivateWorkflow(authToken string, id uint) (err error)

	// Workflow with steps
	GetWorkflowWithSteps(authToken string, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error)
	CreateWorkflowWithSteps(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
	UpdateWorkflowWithSteps(authToken string, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error)

	// Validation
	ValidateWorkflow(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error)
	CanDeleteWorkflow(authToken string, id uint) (canDelete bool, reason string, err error)
}

func NewApprovalWorkflowsService(
	approvalWorkflowsRepository repository.ApprovalWorkflowsRepository,
	approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
	usersRepository usersRepo.UsersRepository,
	log zerolog.Logger,
) ApprovalWorkflowsService {
	return &approvalWorkflowsService{
		ApprovalWorkflowsRepository:     approvalWorkflowsRepository,
		ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
		UsersRepository:                 usersRepository,
		Log:                             log,
	}
}

// Basic CRUD implementations
func (_i *approvalWorkflowsService) GetAll(authToken string, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, 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.ApprovalWorkflowsRepository.GetAll(clientId, req)
}

func (_i *approvalWorkflowsService) FindOne(authToken string, id uint) (workflow *entity.ApprovalWorkflows, 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.ApprovalWorkflowsRepository.FindOne(clientId, id)
}

func (_i *approvalWorkflowsService) Create(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, 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 and steps
	isValid, validationErrors, err := _i.ValidateWorkflow(authToken, workflow, steps)
	if err != nil {
		return nil, err
	}

	if !isValid {
		return nil, errors.New(fmt.Sprintf("Validation failed: %v", validationErrors))
	}

	// Create workflow
	workflowReturn, err = _i.ApprovalWorkflowsRepository.Create(clientId, workflow)
	if err != nil {
		return nil, err
	}

	// Create steps
	for i, step := range steps {
		step.WorkflowId = workflowReturn.ID
		step.StepOrder = i + 1
		_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
		if err != nil {
			// Rollback workflow creation if step creation fails
			_i.ApprovalWorkflowsRepository.Delete(clientId, workflowReturn.ID)
			return nil, err
		}
	}

	return workflowReturn, nil
}

func (_i *approvalWorkflowsService) Update(authToken string, id uint, workflow *entity.ApprovalWorkflows) (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 workflow exists
	existingWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}

	if existingWorkflow == nil {
		return errors.New("workflow not found")
	}

	return _i.ApprovalWorkflowsRepository.Update(clientId, id, workflow)
}

func (_i *approvalWorkflowsService) 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 workflow can be deleted
	canDelete, reason, err := _i.CanDeleteWorkflow(authToken, id)
	if err != nil {
		return err
	}

	if !canDelete {
		return errors.New(reason)
	}

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

// Workflow management
func (_i *approvalWorkflowsService) GetDefault(authToken string) (workflow *entity.ApprovalWorkflows, 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.ApprovalWorkflowsRepository.FindDefault(clientId)
}

func (_i *approvalWorkflowsService) SetDefault(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 workflow exists and is active
	workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}

	if workflow == nil {
		return errors.New("workflow not found")
	}

	if workflow.IsActive == nil || !*workflow.IsActive {
		return errors.New("cannot set inactive workflow as default")
	}

	return _i.ApprovalWorkflowsRepository.SetDefault(clientId, id)
}

func (_i *approvalWorkflowsService) ActivateWorkflow(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")
	}

	// Validate workflow before activation
	workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}

	if workflow == nil {
		return errors.New("workflow not found")
	}

	// Get workflow steps and validate
	steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
	if err != nil {
		return err
	}

	isValid, validationErrors, err := _i.ValidateWorkflow(authToken, workflow, steps)
	if err != nil {
		return err
	}

	if !isValid {
		return errors.New(fmt.Sprintf("Cannot activate invalid workflow: %v", validationErrors))
	}

	// Activate workflow
	isActive := true
	updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
	return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}

func (_i *approvalWorkflowsService) DeactivateWorkflow(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 this is the default workflow
	defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
	if err != nil {
		return err
	}

	if defaultWorkflow != nil && defaultWorkflow.ID == id {
		return errors.New("cannot deactivate default workflow")
	}

	// Check if workflow is being used in active approval flows
	canDelete, reason, err := _i.CanDeleteWorkflow(authToken, id)
	if err != nil {
		return err
	}

	if !canDelete {
		return errors.New(fmt.Sprintf("Cannot deactivate workflow: %s", reason))
	}

	// Deactivate workflow
	isActive := false
	updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
	return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}

// Workflow with steps
func (_i *approvalWorkflowsService) GetWorkflowWithSteps(authToken string, id uint) (workflow *entity.ApprovalWorkflows, 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, nil, errors.New("clientId not found in auth token")
	}

	workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
	if err != nil {
		return nil, nil, err
	}

	steps, err = _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
	if err != nil {
		return nil, nil, err
	}

	return workflow, steps, nil
}

func (_i *approvalWorkflowsService) CreateWorkflowWithSteps(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, 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.Create(authToken, workflow, steps)
}

func (_i *approvalWorkflowsService) UpdateWorkflowWithSteps(authToken string, id uint, workflow *entity.ApprovalWorkflows, 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 errors.New("clientId not found in auth token")
	}

	// Update workflow
	err = _i.Update(authToken, id, workflow)
	if err != nil {
		return err
	}

	// Get existing steps
	existingSteps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
	if err != nil {
		return err
	}

	// Delete existing steps (simplified approach - in production, you might want to update/merge)
	for _, existingStep := range existingSteps {
		err = _i.ApprovalWorkflowStepsRepository.Delete(clientId, existingStep.ID)
		if err != nil {
			return err
		}
	}

	// Create new steps
	for i, step := range steps {
		step.WorkflowId = id
		step.StepOrder = i + 1
		_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
		if err != nil {
			return err
		}
	}

	return nil
}

// Validation
func (_i *approvalWorkflowsService) ValidateWorkflow(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) {
	errors = make([]string, 0)

	// Validate workflow
	if workflow.Name == "" {
		errors = append(errors, "Workflow name is required")
	}

	// Validate steps
	if len(steps) == 0 {
		errors = append(errors, "Workflow must have at least one step")
	} else {
		// For multi-branch workflow, we need different validation logic
		_i.validateMultiBranchSteps(steps, &errors)
	}

	isValid = len(errors) == 0
	return isValid, errors, nil
}

// validateMultiBranchSteps validates steps for multi-branch workflow
func (_i *approvalWorkflowsService) validateMultiBranchSteps(steps []*entity.ApprovalWorkflowSteps, errors *[]string) {
	// Group steps by step order to handle parallel branches
	stepOrderGroups := make(map[int][]*entity.ApprovalWorkflowSteps)

	for i, step := range steps {
		// Basic validation for each step
		if step.StepName == "" {
			*errors = append(*errors, fmt.Sprintf("Step %d name is required", i+1))
		}

		if step.RequiredUserLevelId == 0 {
			*errors = append(*errors, fmt.Sprintf("Step %d must have a required user level", i+1))
		}

		if step.StepOrder <= 0 {
			*errors = append(*errors, fmt.Sprintf("Step %d must have a valid step order (greater than 0)", i+1))
		}

		// Validate condition type and value
		if step.ConditionType != nil {
			validConditionTypes := []string{"user_level", "user_level_hierarchy", "always", "custom"}
			isValidConditionType := false
			for _, validType := range validConditionTypes {
				if *step.ConditionType == validType {
					isValidConditionType = true
					break
				}
			}
			if !isValidConditionType {
				*errors = append(*errors, fmt.Sprintf("Step %d has invalid condition type: %s", i+1, *step.ConditionType))
			}

			// Validate condition value format for specific condition types
			if step.ConditionValue != nil && *step.ConditionValue != "" {
				if *step.ConditionType == "user_level_hierarchy" || *step.ConditionType == "user_level" {
					// Try to parse as JSON to validate format
					var conditionData interface{}
					if err := json.Unmarshal([]byte(*step.ConditionValue), &conditionData); err != nil {
						*errors = append(*errors, fmt.Sprintf("Step %d has invalid condition value format: %s", i+1, *step.ConditionValue))
					}
				}
			}
		}

		// Group steps by step order
		stepOrderGroups[step.StepOrder] = append(stepOrderGroups[step.StepOrder], step)
	}

	// Validate step order groups
	maxStepOrder := 0
	for stepOrder, groupSteps := range stepOrderGroups {
		if stepOrder > maxStepOrder {
			maxStepOrder = stepOrder
		}

		// Check for duplicate branch names within the same step order
		branchNames := make(map[string]bool)
		for _, step := range groupSteps {
			if step.BranchName != nil && *step.BranchName != "" {
				if branchNames[*step.BranchName] {
					*errors = append(*errors, fmt.Sprintf("Duplicate branch name '%s' found in step order %d", *step.BranchName, stepOrder))
				}
				branchNames[*step.BranchName] = true
			}
		}

		// Validate branch order within the same step order
		branchOrders := make(map[int]bool)
		for _, step := range groupSteps {
			if step.BranchOrder != nil {
				if branchOrders[*step.BranchOrder] {
					*errors = append(*errors, fmt.Sprintf("Duplicate branch order %d found in step order %d", *step.BranchOrder, stepOrder))
				}
				branchOrders[*step.BranchOrder] = true
			}
		}
	}

	// Validate that we have at least one step with step order 1
	if len(stepOrderGroups[1]) == 0 {
		*errors = append(*errors, "Workflow must have at least one step with step order 1")
	}

	// Validate that step orders are sequential (no gaps)
	for i := 1; i <= maxStepOrder; i++ {
		if len(stepOrderGroups[i]) == 0 {
			*errors = append(*errors, fmt.Sprintf("Missing step order %d - step orders must be sequential", i))
		}
	}
}

func (_i *approvalWorkflowsService) CanDeleteWorkflow(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 workflow is default
	defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
	if err != nil {
		return false, "", err
	}

	if defaultWorkflow != nil && defaultWorkflow.ID == id {
		return false, "Cannot delete default workflow", nil
	}

	// Check if workflow is being used in active approval flows
	// This would require a method in ArticleApprovalFlowsRepository
	// For now, we'll assume it can be deleted
	// TODO: Implement check for active approval flows

	return true, "", nil
}
