package service

import (
	"errors"
	"fmt"
	"time"
	"web-qudo-be/app/database/entity"
	stepRepo "web-qudo-be/app/module/approval_workflow_steps/repository"
	flowRepo "web-qudo-be/app/module/article_approval_flows/repository"
	"web-qudo-be/app/module/article_approval_step_logs/repository"
	"web-qudo-be/app/module/article_approval_step_logs/request"
	"web-qudo-be/utils/paginator"

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

type articleApprovalStepLogsService struct {
	ArticleApprovalStepLogsRepository repository.ArticleApprovalStepLogsRepository
	ArticleApprovalFlowsRepository    flowRepo.ArticleApprovalFlowsRepository
	ApprovalWorkflowStepsRepository   stepRepo.ApprovalWorkflowStepsRepository
	Log                               zerolog.Logger
}

// ArticleApprovalStepLogsService define interface of IArticleApprovalStepLogsService
type ArticleApprovalStepLogsService interface {
	// Basic CRUD
	GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
	FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error)
	Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error)
	Update(clientId *uuid.UUID, id uint, log *entity.ArticleApprovalStepLogs) (err error)
	Delete(clientId *uuid.UUID, id uint) (err error)

	// Approval process management
	GetByApprovalFlowID(clientId *uuid.UUID, flowID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
	GetByWorkflowStepID(clientId *uuid.UUID, stepID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
	GetByApproverUserID(clientId *uuid.UUID, userID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
	GetPendingApprovals(clientId *uuid.UUID, userID *uint, roleID *uint) (logs []*entity.ArticleApprovalStepLogs, err error)
	GetOverdueApprovals(clientId *uuid.UUID, userID *uint) (logs []*entity.ArticleApprovalStepLogs, err error)

	// Approval actions
	ProcessApproval(clientId *uuid.UUID, logID uint, userID uint, statusID uint, comments *string) (err error)
	BulkProcessApproval(clientId *uuid.UUID, logIDs []uint, userID uint, statusID uint, comments *string) (err error)
	AutoApprove(clientId *uuid.UUID, logID uint, reason string) (err error)

	// History and analytics
	GetApprovalHistory(clientId *uuid.UUID, articleID *uint, userID *uint, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, err error)
	GetApprovalStats(clientId *uuid.UUID, filters map[string]interface{}) (stats map[string]interface{}, err error)
	GetUserWorkload(clientId *uuid.UUID, userID uint, includeStats bool) (workload map[string]interface{}, err error)

	// Validation
	ValidateStepLog(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (isValid bool, errors []string, err error)
	CanProcessApproval(clientId *uuid.UUID, logID uint, userID uint) (canProcess bool, reason string, err error)
}

func NewArticleApprovalStepLogsService(
	articleApprovalStepLogsRepository repository.ArticleApprovalStepLogsRepository,
	articleApprovalFlowsRepository flowRepo.ArticleApprovalFlowsRepository,
	approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
	log zerolog.Logger,
) ArticleApprovalStepLogsService {
	return &articleApprovalStepLogsService{
		ArticleApprovalStepLogsRepository: articleApprovalStepLogsRepository,
		ArticleApprovalFlowsRepository:    articleApprovalFlowsRepository,
		ApprovalWorkflowStepsRepository:   approvalWorkflowStepsRepository,
		Log:                               log,
	}
}

func (_i *articleApprovalStepLogsService) GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
	return _i.ArticleApprovalStepLogsRepository.GetAll(clientId, req)
}

func (_i *articleApprovalStepLogsService) FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error) {
	return _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
}

func (_i *articleApprovalStepLogsService) Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error) {
	// Validate business rules
	if log.ApprovalFlowId == 0 {
		return nil, errors.New("approval flow ID is required")
	}

	// Validate approval flow exists
	flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, log.ApprovalFlowId)
	if err != nil {
		return nil, fmt.Errorf("approval flow not found: %w", err)
	}
	if flow == nil {
		return nil, errors.New("approval flow not found")
	}

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

	return _i.ArticleApprovalStepLogsRepository.Create(clientId, log)
}

func (_i *articleApprovalStepLogsService) Update(clientId *uuid.UUID, id uint, log *entity.ArticleApprovalStepLogs) (err error) {
	// Check if log exists
	existingLog, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}
	if existingLog == nil {
		return errors.New("step log not found")
	}

	return _i.ArticleApprovalStepLogsRepository.Update(id, log)
}

func (_i *articleApprovalStepLogsService) Delete(clientId *uuid.UUID, id uint) (err error) {
	// Check if log exists
	existingLog, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
	if err != nil {
		return err
	}
	if existingLog == nil {
		return errors.New("step log not found")
	}

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

func (_i *articleApprovalStepLogsService) GetByApprovalFlowID(clientId *uuid.UUID, flowID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
	return _i.ArticleApprovalStepLogsRepository.GetByApprovalFlowId(clientId, flowID)
}

func (_i *articleApprovalStepLogsService) GetByWorkflowStepID(clientId *uuid.UUID, stepID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
	// This method is not implemented in repository, return empty slice for now
	return []*entity.ArticleApprovalStepLogs{}, nil
}

func (_i *articleApprovalStepLogsService) GetByApproverUserID(clientId *uuid.UUID, userID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
	// This method is not implemented in repository, return empty slice for now
	return []*entity.ArticleApprovalStepLogs{}, nil
}

func (_i *articleApprovalStepLogsService) GetPendingApprovals(clientId *uuid.UUID, userID *uint, roleID *uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
	// This method is not implemented in repository, return empty slice for now
	return []*entity.ArticleApprovalStepLogs{}, nil
}

func (_i *articleApprovalStepLogsService) GetOverdueApprovals(clientId *uuid.UUID, userID *uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
	// This method is not implemented in repository, return empty slice for now
	return []*entity.ArticleApprovalStepLogs{}, nil
}

func (_i *articleApprovalStepLogsService) ProcessApproval(clientId *uuid.UUID, logID uint, userID uint, statusID uint, comments *string) (err error) {
	// Check if user can process this approval
	canProcess, reason, err := _i.CanProcessApproval(clientId, logID, userID)
	if err != nil {
		return err
	}
	if !canProcess {
		return fmt.Errorf("cannot process approval: %s", reason)
	}

	// Update the step log
	now := time.Now()
	updateLog := &entity.ArticleApprovalStepLogs{
		ApprovedById: &userID,
		Action:       "approve", // This should be determined based on statusID
		Message:      comments,
		ProcessedAt:  now,
	}

	return _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
}

func (_i *articleApprovalStepLogsService) BulkProcessApproval(clientId *uuid.UUID, logIDs []uint, userID uint, statusID uint, comments *string) (err error) {
	// Validate all logs can be processed by this user
	for _, logID := range logIDs {
		canProcess, reason, err := _i.CanProcessApproval(clientId, logID, userID)
		if err != nil {
			return err
		}
		if !canProcess {
			return fmt.Errorf("cannot process approval for log %d: %s", logID, reason)
		}
	}

	// Note: BulkUpdate method is not available in repository
	// This functionality would need to be implemented when repository is updated
	// For now, we'll process each log individually
	now := time.Now()
	for _, logID := range logIDs {
		updateLog := &entity.ArticleApprovalStepLogs{
			ApprovedById: &userID,
			Action:       "approve",
			Message:      comments,
			ProcessedAt:  now,
		}
		err := _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
		if err != nil {
			return err
		}
	}
	return nil
}

func (_i *articleApprovalStepLogsService) AutoApprove(clientId *uuid.UUID, logID uint, reason string) (err error) {
	// Get the step log
	log, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, logID)
	if err != nil {
		return err
	}
	if log == nil {
		return errors.New("step log not found")
	}

	// Note: WorkflowStepID and AutoApprove fields are not available in current entity structure
	// This functionality would need to be implemented when entity is updated

	// Auto approve with current entity structure
	now := time.Now()
	updateLog := &entity.ArticleApprovalStepLogs{
		Action:      "approve",
		Message:     &reason,
		ProcessedAt: now,
	}

	return _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
}

func (_i *articleApprovalStepLogsService) GetApprovalHistory(clientId *uuid.UUID, articleID *uint, userID *uint, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, err error) {
	// This method signature doesn't match repository, return empty slice for now
	return []*entity.ArticleApprovalStepLogs{}, nil
}

func (_i *articleApprovalStepLogsService) GetApprovalStats(clientId *uuid.UUID, filters map[string]interface{}) (stats map[string]interface{}, err error) {
	// This method is not implemented in repository, return empty map for now
	return make(map[string]interface{}), nil
}

func (_i *articleApprovalStepLogsService) GetUserWorkload(clientId *uuid.UUID, userID uint, includeStats bool) (workload map[string]interface{}, err error) {
	// This method is not implemented in repository, return empty map for now
	return make(map[string]interface{}), nil
}

func (_i *articleApprovalStepLogsService) ValidateStepLog(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (isValid bool, errors []string, err error) {
	var validationErrors []string

	// Validate required fields
	if log.ApprovalFlowId == 0 {
		validationErrors = append(validationErrors, "approval flow ID is required")
	}

	// Note: WorkflowStepID field is not available in current entity structure
	// This validation would need to be implemented when entity is updated

	// Note: ApprovalStatusID field is not available in current entity structure
	// This validation would need to be implemented when entity is updated

	// Validate message length if provided
	if log.Message != nil && len(*log.Message) > 1000 {
		validationErrors = append(validationErrors, "message must not exceed 1000 characters")
	}

	// Note: DueDate field is not available in current entity structure
	// This validation would need to be implemented when entity is updated

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

func (_i *articleApprovalStepLogsService) CanProcessApproval(clientId *uuid.UUID, logID uint, userID uint) (canProcess bool, reason string, err error) {
	// Get the step log
	log, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, logID)
	if err != nil {
		return false, "", err
	}
	if log == nil {
		return false, "step log not found", nil
	}

	// Check if already processed (using ProcessedAt field)
	if !log.ProcessedAt.IsZero() {
		return false, "approval already processed", nil
	}

	// Check if user has permission to approve this step
	// This would require checking user roles against step approver role
	// For now, we'll allow any user to process
	// TODO: Implement proper role-based authorization

	// Note: DueDate field is not available in current entity structure
	// This check would need to be implemented when entity is updated

	return true, "", nil
}
