package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"mime"
	"netidhub-saas-be/app/database/entity"
	approvalWorkflowsRepository "netidhub-saas-be/app/module/approval_workflows/repository"
	articleApprovalFlowsRepository "netidhub-saas-be/app/module/article_approval_flows/repository"
	articleApprovalFlowsService "netidhub-saas-be/app/module/article_approval_flows/service"
	articleApprovalsRepository "netidhub-saas-be/app/module/article_approvals/repository"
	articleCategoriesRepository "netidhub-saas-be/app/module/article_categories/repository"
	articleCategoryDetailsRepository "netidhub-saas-be/app/module/article_category_details/repository"
	articleCategoryDetailsReq "netidhub-saas-be/app/module/article_category_details/request"
	articleFilesRepository "netidhub-saas-be/app/module/article_files/repository"
	"netidhub-saas-be/app/module/articles/mapper"
	"netidhub-saas-be/app/module/articles/repository"
	"netidhub-saas-be/app/module/articles/request"
	"netidhub-saas-be/app/module/articles/response"
	usersRepository "netidhub-saas-be/app/module/users/repository"
	config "netidhub-saas-be/config/config"
	minioStorage "netidhub-saas-be/config/config"
	"netidhub-saas-be/utils/paginator"
	utilSvc "netidhub-saas-be/utils/service"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/minio/minio-go/v7"
	"github.com/rs/zerolog"
)

// ArticlesService
type articlesService struct {
	Repo                       repository.ArticlesRepository
	ArticleCategoriesRepo      articleCategoriesRepository.ArticleCategoriesRepository
	ArticleFilesRepo           articleFilesRepository.ArticleFilesRepository
	ArticleApprovalsRepo       articleApprovalsRepository.ArticleApprovalsRepository
	ArticleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository
	Log                        zerolog.Logger
	Cfg                        *config.Config
	UsersRepo                  usersRepository.UsersRepository
	MinioStorage               *minioStorage.MinioStorage

	// Dynamic approval system dependencies
	ArticleApprovalFlowsRepo articleApprovalFlowsRepository.ArticleApprovalFlowsRepository
	ApprovalWorkflowsRepo    approvalWorkflowsRepository.ApprovalWorkflowsRepository
	ArticleApprovalFlowsSvc  articleApprovalFlowsService.ArticleApprovalFlowsService
}

// ArticlesService define interface of IArticlesService
type ArticlesService interface {
	All(authToken string, req request.ArticlesQueryRequest) (articles []*response.ArticlesResponse, paging paginator.Pagination, err error)
	Show(authToken string, id uint) (articles *response.ArticlesResponse, err error)
	ShowByOldId(authToken string, oldId uint) (articles *response.ArticlesResponse, err error)
	Save(authToken string, req request.ArticlesCreateRequest) (articles *entity.Articles, err error)
	SaveThumbnail(authToken string, c *fiber.Ctx) (err error)
	Update(authToken string, id uint, req request.ArticlesUpdateRequest) (err error)
	Delete(authToken string, id uint) error
	UpdateActivityCount(authToken string, id uint, activityTypeId int) (err error)
	UpdateApproval(authToken string, id uint, statusId int, userLevelId int, userLevelNumber int, userParentLevelId int) (err error)
	UpdateBanner(authToken string, id uint, isBanner bool) error
	Viewer(authToken string, c *fiber.Ctx) error
	SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error)
	ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error)
	ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error)
	PublishScheduling(authToken string, id uint, publishSchedule string) error
	ExecuteScheduling() error

	// Dynamic approval system methods
	SubmitForApproval(authToken string, articleId uint, workflowId *uint) error
	GetApprovalStatus(authToken string, articleId uint) (*response.ArticleApprovalStatusResponse, error)
	GetArticlesWaitingForApproval(authToken string, page, limit int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error)
	GetPendingApprovals(authToken string, page, limit int, typeId *int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) // Updated with typeId filter

	// No-approval system methods
	CheckApprovalRequired(authToken string, articleId uint, userId uint, userLevelId uint) (bool, error)
	AutoApproveArticle(authToken string, articleId uint, reason string) error
	GetClientApprovalSettings(authToken string) (*response.ClientApprovalSettingsResponse, error)
	SetArticleApprovalExempt(authToken string, articleId uint, exempt bool, reason string) error
}

// NewArticlesService init ArticlesService
func NewArticlesService(
	repo repository.ArticlesRepository,
	articleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository,
	articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository,
	articleFilesRepo articleFilesRepository.ArticleFilesRepository,
	articleApprovalsRepo articleApprovalsRepository.ArticleApprovalsRepository,
	articleApprovalFlowsRepo articleApprovalFlowsRepository.ArticleApprovalFlowsRepository,
	approvalWorkflowsRepo approvalWorkflowsRepository.ApprovalWorkflowsRepository,
	articleApprovalFlowsSvc articleApprovalFlowsService.ArticleApprovalFlowsService,
	log zerolog.Logger,
	cfg *config.Config,
	usersRepo usersRepository.UsersRepository,
	minioStorage *minioStorage.MinioStorage,
) ArticlesService {

	return &articlesService{
		Repo:                       repo,
		ArticleCategoriesRepo:      articleCategoriesRepo,
		ArticleCategoryDetailsRepo: articleCategoryDetailsRepo,
		ArticleFilesRepo:           articleFilesRepo,
		ArticleApprovalsRepo:       articleApprovalsRepo,
		ArticleApprovalFlowsRepo:   articleApprovalFlowsRepo,
		ApprovalWorkflowsRepo:      approvalWorkflowsRepo,
		ArticleApprovalFlowsSvc:    articleApprovalFlowsSvc,
		Log:                        log,
		UsersRepo:                  usersRepo,
		MinioStorage:               minioStorage,
		Cfg:                        cfg,
	}
}

// All implement interface of ArticlesService
func (_i *articlesService) All(authToken string, req request.ArticlesQueryRequest) (articless []*response.ArticlesResponse, paging paginator.Pagination, err error) {
	// Extract clientId and userLevelId from authToken
	var clientId *uuid.UUID
	var userLevelId *uint
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil {
			if user.ClientId != nil {
				clientId = user.ClientId
				_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
			}
			userLevelId = &user.UserLevelId
			_i.Log.Info().Interface("userLevelId", userLevelId).Msg("Extracted userLevelId from auth token")
		}
	}

	if req.Category != nil {
		findCategory, err := _i.ArticleCategoriesRepo.FindOneBySlug(clientId, *req.Category)
		if err != nil {
			return nil, paging, err
		}
		req.CategoryId = &findCategory.ID
	}

	results, paging, err := _i.Repo.GetAll(clientId, userLevelId, req)
	if err != nil {
		return
	}

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:articlesService", "Methods:All").
		Interface("results", results).Msg("")

	host := _i.Cfg.App.Domain

	for _, result := range results {
		articleRes := mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo)
		articless = append(articless, articleRes)
	}

	return
}

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

	result, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		return nil, err
	}

	host := _i.Cfg.App.Domain

	return mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
}

func (_i *articlesService) ShowByOldId(authToken string, oldId uint) (articles *response.ArticlesResponse, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	result, err := _i.Repo.FindByOldId(clientId, oldId)
	if err != nil {
		return nil, err
	}

	host := _i.Cfg.App.Domain

	return mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
}

func (_i *articlesService) Save(authToken string, req request.ArticlesCreateRequest) (articles *entity.Articles, err error) {
	_i.Log.Info().Interface("data", req).Msg("")
	newReq := req.ToEntity()

	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	var userLevelNumber int
	var approvalLevelId int
	if req.CreatedById != nil {
		createdBy, err := _i.UsersRepo.FindOne(clientId, *req.CreatedById)
		if err != nil {
			return nil, fmt.Errorf("User not found")
		}
		newReq.CreatedById = &createdBy.ID
		userLevelNumber = createdBy.UserLevel.LevelNumber

		// Find the next higher level for approval (level_number should be smaller)
		approvalLevelId = _i.findNextApprovalLevel(clientId, userLevelNumber)
	} else {
		createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		newReq.CreatedById = &createdBy.ID
		userLevelNumber = createdBy.UserLevel.LevelNumber

		// Find the next higher level for approval (level_number should be smaller)
		approvalLevelId = _i.findNextApprovalLevel(clientId, userLevelNumber)
	}

	isDraft := true
	if req.IsDraft == &isDraft {
		draftedAt := time.Now()
		newReq.IsDraft = &isDraft
		newReq.DraftedAt = &draftedAt
		isPublishFalse := false
		newReq.IsPublish = &isPublishFalse
		newReq.PublishedAt = nil
	}

	isPublish := true
	if req.IsPublish == &isPublish {
		publishedAt := time.Now()
		newReq.IsPublish = &isPublish
		newReq.PublishedAt = &publishedAt
		isDraftFalse := false
		newReq.IsDraft = &isDraftFalse
		newReq.DraftedAt = nil
	}

	if req.CreatedAt != nil {
		layout := "2006-01-02 15:04:05"
		parsedTime, err := time.Parse(layout, *req.CreatedAt)
		if err != nil {
			return nil, fmt.Errorf("Error parsing time:", err)
		}
		newReq.CreatedAt = parsedTime
	}

	// Dynamic Approval Workflow System
	statusIdOne := 1
	statusIdTwo := 2
	isPublishFalse := false

	// Get user info for approval logic
	createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)

	// Check if user level requires approval
	if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == false {
		_i.Log.Info().
			Uint("userId", createdBy.ID).
			Uint("userLevelId", createdBy.UserLevel.ID).
			Str("userLevelName", createdBy.UserLevel.Name).
			Bool("isApprovalActive", *createdBy.UserLevel.IsApprovalActive).
			Msg("User level does not require approval - auto publishing")

		// User level doesn't require approval - auto publish
		newReq.NeedApprovalFrom = nil
		newReq.StatusId = &statusIdTwo
		newReq.IsPublish = &isPublishFalse
		newReq.PublishedAt = nil
		newReq.BypassApproval = &[]bool{true}[0]
	} else {
		_i.Log.Info().
			Uint("userId", createdBy.ID).
			Uint("userLevelId", createdBy.UserLevel.ID).
			Str("userLevelName", createdBy.UserLevel.Name).
			Bool("isApprovalActive", func() bool {
				if createdBy != nil && createdBy.UserLevel.IsApprovalActive != nil {
					return *createdBy.UserLevel.IsApprovalActive
				}
				return false
			}()).
			Msg("User level requires approval - setting to pending")

		// User level requires approval - set to pending
		newReq.NeedApprovalFrom = &approvalLevelId
		newReq.StatusId = &statusIdOne
		newReq.IsPublish = &isPublishFalse
		newReq.PublishedAt = nil
		newReq.BypassApproval = &[]bool{false}[0]
	}

	saveArticleRes, err := _i.Repo.Create(clientId, newReq)
	if err != nil {
		return nil, err
	}

	// Dynamic Approval Workflow Assignment with Multi-Branch Support
	if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == true {
		_i.Log.Info().
			Uint("userId", createdBy.ID).
			Uint("userLevelId", createdBy.UserLevel.ID).
			Str("userLevelName", createdBy.UserLevel.Name).
			Bool("isApprovalActive", *createdBy.UserLevel.IsApprovalActive).
			Msg("User level requires approval - proceeding with workflow assignment")

		// Get default workflow for the client
		defaultWorkflow, err := _i.ApprovalWorkflowsRepo.GetDefault(clientId)
		if err != nil {
			_i.Log.Error().Err(err).Msg("Failed to get default workflow")
		} else if defaultWorkflow == nil {
			_i.Log.Warn().Msg("No default workflow found for client")
		} else {
			_i.Log.Info().
				Uint("workflowId", defaultWorkflow.ID).
				Str("workflowName", defaultWorkflow.Name).
				Bool("isActive", *defaultWorkflow.IsActive).
				Bool("isDefault", *defaultWorkflow.IsDefault).
				Msg("Found default workflow")

			// Assign workflow to article
			saveArticleRes.WorkflowId = &defaultWorkflow.ID
			saveArticleRes.CurrentApprovalStep = &[]int{1}[0] // Start at step 1

			// Update article with workflow info
			err = _i.Repo.Update(clientId, saveArticleRes.ID, saveArticleRes)
			if err != nil {
				_i.Log.Error().Err(err).Msg("Failed to update article with workflow")
			} else {
				_i.Log.Info().
					Uint("articleId", saveArticleRes.ID).
					Uint("workflowId", defaultWorkflow.ID).
					Msg("Article updated with workflow successfully")
			}

			// Create approval flow with multi-branch support
			approvalFlow := &entity.ArticleApprovalFlows{
				ArticleId:     saveArticleRes.ID,
				WorkflowId:    defaultWorkflow.ID,
				CurrentStep:   1,
				StatusId:      1, // In Progress
				SubmittedById: *newReq.CreatedById,
				SubmittedAt:   time.Now(),
				ClientId:      clientId,
				// Multi-branch fields will be set by the service
				CurrentBranch:  nil,               // Will be determined by first applicable step
				BranchPath:     nil,               // Will be populated as flow progresses
				IsParallelFlow: &[]bool{false}[0], // Default to sequential
			}

			createdFlow, err := _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
			if err != nil {
				_i.Log.Error().Err(err).Msg("Failed to create approval flow")
			} else {
				_i.Log.Info().
					Uint("flowId", createdFlow.ID).
					Uint("articleId", saveArticleRes.ID).
					Uint("workflowId", defaultWorkflow.ID).
					Msg("Approval flow created successfully")

				// Initialize the multi-branch flow by determining the first applicable step
				err = _i.initializeMultiBranchFlow(authToken, createdFlow.ID, createdBy.UserLevel.ID)
				if err != nil {
					_i.Log.Error().Err(err).Msg("Failed to initialize multi-branch flow")
				} else {
					_i.Log.Info().
						Uint("flowId", createdFlow.ID).
						Uint("userLevelId", createdBy.UserLevel.ID).
						Msg("Multi-branch flow initialized successfully")
				}
			}
		}

		// Create legacy approval record for backward compatibility
		articleApproval := &entity.ArticleApprovals{
			ArticleId:       saveArticleRes.ID,
			ApprovalBy:      *newReq.CreatedById,
			StatusId:        statusIdOne,
			Message:         "Need Approval",
			ApprovalAtLevel: &approvalLevelId,
		}
		_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
		if err != nil {
			_i.Log.Error().Err(err).Msg("Failed to create legacy approval record")
		}
	} else {
		// Auto-publish for users who don't require approval
		articleApproval := &entity.ArticleApprovals{
			ArticleId:       saveArticleRes.ID,
			ApprovalBy:      *newReq.CreatedById,
			StatusId:        statusIdTwo,
			Message:         "Publish Otomatis",
			ApprovalAtLevel: nil,
		}
		_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
		if err != nil {
			_i.Log.Error().Err(err).Msg("Failed to create auto-approval record")
		}
	}

	var categoryIds []string
	if req.CategoryIds != "" {
		categoryIds = strings.Split(req.CategoryIds, ",")
	}

	_i.Log.Info().Interface("categoryIds", categoryIds).Msg("")

	for _, categoryId := range categoryIds {
		categoryIdInt, _ := strconv.Atoi(categoryId)

		_i.Log.Info().Interface("categoryIdUint", uint(categoryIdInt)).Msg("")

		findCategory, err := _i.ArticleCategoriesRepo.FindOne(clientId, uint(categoryIdInt))

		_i.Log.Info().Interface("findCategory", findCategory).Msg("")

		if err != nil {
			return nil, err
		}

		if findCategory == nil {
			return nil, errors.New("category not found")
		}

		categoryReq := articleCategoryDetailsReq.ArticleCategoryDetailsCreateRequest{
			ArticleId:  saveArticleRes.ID,
			CategoryId: categoryIdInt,
			IsActive:   true,
		}
		newCategoryReq := categoryReq.ToEntity()

		err = _i.ArticleCategoryDetailsRepo.Create(newCategoryReq)
		if err != nil {
			return nil, err
		}
	}

	return saveArticleRes, nil
}

func (_i *articlesService) SaveThumbnail(authToken string, c *fiber.Ctx) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	id, err := strconv.ParseUint(c.Params("id"), 10, 0)
	if err != nil {
		return err
	}

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:articlesService", "Methods:SaveThumbnail").
		Interface("id", id).Msg("")

	bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName

	form, err := c.MultipartForm()
	if err != nil {
		return err
	}
	files := form.File["files"]

	// Create minio connection.
	minioClient, err := _i.MinioStorage.ConnectMinio()
	if err != nil {
		// Return status 500 and minio connection error.
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": true,
			"msg":   err.Error(),
		})
	}

	// Iterasi semua file yang diunggah
	for _, file := range files {

		_i.Log.Info().Str("timestamp", time.Now().
			Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop1").
			Interface("data", file).Msg("")

		src, err := file.Open()
		if err != nil {
			return err
		}
		defer src.Close()

		filename := filepath.Base(file.Filename)
		filename = strings.ReplaceAll(filename, " ", "")
		filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
		extension := filepath.Ext(file.Filename)[1:]

		now := time.Now()
		rand.New(rand.NewSource(now.UnixNano()))
		randUniqueId := rand.Intn(1000000)

		newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId)
		newFilename := newFilenameWithoutExt + "." + extension
		objectName := fmt.Sprintf("articles/thumbnail/%d/%d/%s", now.Year(), now.Month(), newFilename)

		findCategory, err := _i.Repo.FindOne(clientId, uint(id))
		findCategory.ThumbnailName = &newFilename
		findCategory.ThumbnailPath = &objectName
		err = _i.Repo.Update(clientId, uint(id), findCategory)
		if err != nil {
			return err
		}

		// Upload file ke MinIO
		_, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, file.Size, minio.PutObjectOptions{})
		if err != nil {
			return err
		}
	}

	return
}

func (_i *articlesService) Update(authToken string, id uint, req request.ArticlesUpdateRequest) (err error) {
	_i.Log.Info().Interface("data", req).Msg("")
	newReq := req.ToEntity()

	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	if req.CreatedAt != nil {
		layout := "2006-01-02 15:04:05"
		parsedTime, err := time.Parse(layout, *req.CreatedAt)
		if err != nil {
			return fmt.Errorf("Error parsing time:", err)
		}
		newReq.CreatedAt = parsedTime
	}

	return _i.Repo.UpdateSkipNull(clientId, id, newReq)
}

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

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

func (_i *articlesService) Viewer(authToken string, c *fiber.Ctx) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	thumbnailName := c.Params("thumbnailName")

	emptyImage := "empty-image.jpg"
	searchThumbnail := emptyImage
	if thumbnailName != emptyImage {
		result, err := _i.Repo.FindByFilename(clientId, thumbnailName)
		if err != nil {
			return err
		}
		_i.Log.Info().Str("timestamp", time.Now().
			Format(time.RFC3339)).Str("Service:Resource", "articlesService:Viewer").
			Interface("resultThumbnail", result.ThumbnailPath).Msg("")

		if result.ThumbnailPath != nil {
			searchThumbnail = *result.ThumbnailPath
		} else {
			searchThumbnail = "articles/thumbnail/" + emptyImage
		}
	} else {
		searchThumbnail = "articles/thumbnail/" + emptyImage
	}

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:Resource", "articlesService:Viewer").
		Interface("searchThumbnail", searchThumbnail).Msg("")

	ctx := context.Background()
	bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
	objectName := searchThumbnail

	// Create minio connection.
	minioClient, err := _i.MinioStorage.ConnectMinio()
	if err != nil {
		// Return status 500 and minio connection error.
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": true,
			"msg":   err.Error(),
		})
	}

	fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
	if err != nil {
		log.Fatalln(err)
	}
	defer fileContent.Close()

	contentType := mime.TypeByExtension("." + getFileExtension(objectName))
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	c.Set("Content-Type", contentType)

	if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil {
		return err
	}

	return
}

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

	result, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		return err
	}

	viewCount := 0
	if result.ViewCount != nil {
		viewCount = *result.ViewCount
	}
	shareCount := 0
	if result.ShareCount != nil {
		shareCount = *result.ShareCount
	}
	commentCount := 0
	if result.CommentCount != nil {
		commentCount = *result.CommentCount
	}

	if activityTypeId == 2 {
		viewCount++
	} else if activityTypeId == 3 {
		shareCount++
	} else if activityTypeId == 4 {
		commentCount++
	}
	result.ViewCount = &viewCount
	result.ShareCount = &shareCount
	result.CommentCount = &commentCount
	return _i.Repo.Update(clientId, id, result)
}

func (_i *articlesService) SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)

	result, err := _i.Repo.SummaryStats(clientId, user.ID)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (_i *articlesService) ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats").
		Interface("startDate", startDate).Msg("")
	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats").
		Interface("endDate", endDate).Msg("")

	var userLevelId *uint
	var userLevelNumber *int

	if user != nil {
		userLevelId = &user.UserLevelId
		userLevelNumber = &user.UserLevel.LevelNumber
	}

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats").
		Interface("userLevelId", userLevelId).Msg("")
	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats").
		Interface("userLevelNumber", userLevelNumber).Msg("")

	result, err := _i.Repo.ArticlePerUserLevelStats(clientId, userLevelId, userLevelNumber, nil, nil)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (_i *articlesService) ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)

	var userLevelId *uint
	var userLevelNumber *int

	if user != nil {
		userLevelId = &user.UserLevelId
		userLevelNumber = &user.UserLevel.LevelNumber
	}

	result, err := _i.Repo.ArticleMonthlyStats(clientId, userLevelId, userLevelNumber, *year)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (_i *articlesService) UpdateApproval(authToken string, id uint, statusId int, userLevelId int, userLevelNumber int, userParentLevelId int) (err error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	result, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		return err
	}

	_i.Log.Info().Interface("statusId", statusId).Msg("")

	statusIdOne := 1
	statusIdTwo := 2
	statusIdThree := 3
	isPublish := true
	isDraftFalse := false

	if statusId == 2 {
		if userLevelNumber == 2 || userLevelNumber == 3 {
			result.NeedApprovalFrom = &userParentLevelId
			result.StatusId = &statusIdOne
		} else {
			result.NeedApprovalFrom = nil
			result.StatusId = &statusIdTwo

			result.IsPublish = &isPublish
			publishedAt := time.Now()
			result.PublishedAt = &publishedAt

			result.IsDraft = &isDraftFalse
			result.DraftedAt = nil
		}

		userLevelIdStr := strconv.Itoa(userLevelId)
		if result.HasApprovedBy == nil {
			result.HasApprovedBy = &userLevelIdStr
		} else {
			hasApprovedBySlice := strings.Split(*result.HasApprovedBy, ",")
			hasApprovedBySlice = append(hasApprovedBySlice, userLevelIdStr)
			hasApprovedByJoin := strings.Join(hasApprovedBySlice, ",")
			result.HasApprovedBy = &hasApprovedByJoin
		}
	} else if statusId == 3 {
		result.StatusId = &statusIdThree
		result.NeedApprovalFrom = nil
		result.HasApprovedBy = nil
	}

	err = _i.Repo.Update(clientId, id, result)
	if err != nil {
		return err
	}

	return
}

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

	result, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		return err
	}
	result.PublishSchedule = &publishSchedule
	return _i.Repo.Update(clientId, id, result)
}

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

	result, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		return err
	}
	result.IsBanner = &isBanner
	return _i.Repo.Update(clientId, id, result)
}

func (_i *articlesService) ExecuteScheduling() error {
	// For background jobs, we don't have context, so pass nil for clientId
	articles, err := _i.Repo.GetAllPublishSchedule(nil)
	if err != nil {
		return err
	}

	layout := "2006-01-02"
	now := time.Now()
	today := now.Truncate(24 * time.Hour)

	for _, article := range articles { // Looping setiap artikel
		if article.PublishSchedule == nil {
			continue
		}

		parsedDate, err := time.Parse(layout, *article.PublishSchedule)
		if err != nil {
			continue
		}

		if parsedDate.Equal(today) {
			isPublish := true
			statusIdTwo := 2

			article.PublishSchedule = nil
			article.IsPublish = &isPublish
			article.PublishedAt = &now
			article.StatusId = &statusIdTwo

			// For background jobs, we don't have context, so pass nil for clientId
			if err := _i.Repo.UpdateSkipNull(nil, article.ID, article); err != nil {
				_i.Log.Info().Str("timestamp", time.Now().
					Format(time.RFC3339)).Str("Service:articlesService", "Methods:ExecuteScheduling").
					Interface("Failed to publish Article ID : ", article.ID).Msg("")
			}
		}
	}

	return err
}

func getFileExtension(filename string) string {
	// split file name
	parts := strings.Split(filename, ".")

	// jika tidak ada ekstensi, kembalikan string kosong
	if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") {
		return ""
	}

	// ambil ekstensi terakhir
	return parts[len(parts)-1]
}

// SubmitForApproval submits an article for approval using the dynamic workflow system
func (_i *articlesService) SubmitForApproval(authToken string, articleId uint, workflowId *uint) error {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		return errors.New("user not found from auth token")
	}

	// Check if article exists
	article, err := _i.Repo.FindOne(clientId, articleId)
	if err != nil {
		return err
	}

	// If no workflow specified, get the default workflow
	if workflowId == nil {
		defaultWorkflow, err := _i.ApprovalWorkflowsRepo.GetDefault(clientId)
		if err != nil {
			return err
		}
		workflowId = &defaultWorkflow.ID
	}

	// Validate workflow exists and is active
	_, err = _i.ApprovalWorkflowsRepo.FindOne(clientId, *workflowId)
	if err != nil {
		return err
	}

	// Create approval flow
	approvalFlow := &entity.ArticleApprovalFlows{
		ArticleId:     articleId,
		WorkflowId:    *workflowId,
		CurrentStep:   1,
		StatusId:      1, // 1 = In Progress
		ClientId:      clientId,
		SubmittedById: user.ID,
	}

	_, err = _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
	if err != nil {
		return err
	}

	// Update article status to pending approval
	statusId := 1 // Pending approval
	article.StatusId = &statusId
	article.WorkflowId = workflowId

	err = _i.Repo.Update(clientId, articleId, article)
	if err != nil {
		return err
	}

	_i.Log.Info().Str("timestamp", time.Now().
		Format(time.RFC3339)).Str("Service:articlesService", "Methods:SubmitForApproval").
		Interface("Article submitted for approval", articleId).Msg("")

	return nil
}

// GetApprovalStatus gets the current approval status of an article
func (_i *articlesService) GetApprovalStatus(authToken string, articleId uint) (*response.ArticleApprovalStatusResponse, error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// Check if article exists
	_, err := _i.Repo.FindOne(clientId, articleId)
	if err != nil {
		return nil, err
	}

	// Get approval flow
	approvalFlow, err := _i.ArticleApprovalFlowsRepo.FindByArticleId(clientId, articleId)
	if err != nil {
		// Article might not be in approval process
		return &response.ArticleApprovalStatusResponse{
			ArticleId:   articleId,
			Status:      "not_submitted",
			CurrentStep: 0,
			TotalSteps:  0,
			Progress:    0,
		}, nil
	}

	// Get workflow details
	workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, approvalFlow.WorkflowId)
	if err != nil {
		return nil, err
	}

	// Get workflow steps
	workflowSteps, err := _i.ApprovalWorkflowsRepo.GetWorkflowSteps(clientId, approvalFlow.WorkflowId)
	if err != nil {
		return nil, err
	}

	totalSteps := len(workflowSteps)
	progress := 0.0
	if totalSteps > 0 {
		progress = float64(approvalFlow.CurrentStep-1) / float64(totalSteps) * 100
	}

	// Determine status
	status := "in_progress"
	if approvalFlow.StatusId == 2 {
		status = "approved"
	} else if approvalFlow.StatusId == 3 {
		status = "rejected"
	} else if approvalFlow.StatusId == 4 {
		status = "revision_requested"
	}

	// Get current approver info
	var currentApprover *string
	var nextStep *string
	if approvalFlow.CurrentStep <= totalSteps && approvalFlow.StatusId == 1 {
		if approvalFlow.CurrentStep < totalSteps {
			// Array indexing starts from 0, so subtract 1 from CurrentStep
			nextStepIndex := approvalFlow.CurrentStep - 1
			if nextStepIndex >= 0 && nextStepIndex < len(workflowSteps) {
				nextStepInfo := workflowSteps[nextStepIndex]
				nextStep = &nextStepInfo.RequiredUserLevel.Name
			}
		}
	}

	return &response.ArticleApprovalStatusResponse{
		ArticleId:       articleId,
		WorkflowId:      &workflow.ID,
		WorkflowName:    &workflow.Name,
		CurrentStep:     approvalFlow.CurrentStep,
		TotalSteps:      totalSteps,
		Status:          status,
		CurrentApprover: currentApprover,
		SubmittedAt:     &approvalFlow.CreatedAt,
		LastActionAt:    &approvalFlow.UpdatedAt,
		Progress:        progress,
		CanApprove:      false, // TODO: Implement based on user permissions
		NextStep:        nextStep,
	}, nil
}

// GetPendingApprovals gets articles pending approval for a specific user level
func (_i *articlesService) GetPendingApprovals(authToken string, page, limit int, typeId *int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		return nil, paginator.Pagination{}, errors.New("user not found from auth token")
	}

	// Prepare filters
	filters := make(map[string]interface{})
	if typeId != nil {
		filters["type_id"] = *typeId
	}

	// Get pending approvals for the user level
	approvalFlows, paging, err := _i.ArticleApprovalFlowsRepo.GetPendingApprovals(clientId, user.UserLevelId, page, limit, filters)
	if err != nil {
		return nil, paging, err
	}

	var responses []*response.ArticleApprovalQueueResponse
	for _, flow := range approvalFlows {
		// Get article details
		article, err := _i.Repo.FindOne(clientId, flow.ArticleId)
		if err != nil {
			continue
		}

		// Get workflow details
		workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, flow.WorkflowId)
		if err != nil {
			continue
		}

		// Get workflow steps
		workflowSteps, err := _i.ApprovalWorkflowsRepo.GetWorkflowSteps(clientId, flow.WorkflowId)
		if err != nil {
			continue
		}

		// Calculate days in queue
		daysInQueue := int(time.Since(flow.CreatedAt).Hours() / 24)

		// Determine priority based on days in queue
		priority := "low"
		if daysInQueue > 7 {
			priority = "urgent"
		} else if daysInQueue > 3 {
			priority = "high"
		} else if daysInQueue > 1 {
			priority = "medium"
		}

		// Get author name
		var authorName string
		if article.CreatedById != nil {
			user, err := _i.UsersRepo.FindOne(clientId, *article.CreatedById)
			if err == nil && user != nil {
				authorName = user.Fullname
			}
		}

		// Get category name
		var categoryName string
		if article.CategoryId != 0 {
			category, err := _i.ArticleCategoriesRepo.FindOne(clientId, uint(article.CategoryId))
			if err == nil && category != nil {
				categoryName = category.Title
			}
		}

		response := &response.ArticleApprovalQueueResponse{
			ID:            article.ID,
			Title:         article.Title,
			Slug:          article.Slug,
			Description:   article.Description,
			CategoryName:  categoryName,
			AuthorName:    authorName,
			SubmittedAt:   flow.CreatedAt,
			CurrentStep:   flow.CurrentStep,
			TotalSteps:    len(workflowSteps),
			Priority:      priority,
			DaysInQueue:   daysInQueue,
			WorkflowName:  workflow.Name,
			CanApprove:    true,       // TODO: Implement based on user permissions
			EstimatedTime: "2-3 days", // TODO: Calculate based on historical data
		}

		responses = append(responses, response)
	}

	return responses, paging, nil
}

// GetArticlesWaitingForApproval gets articles that are waiting for approval by a specific user level
func (_i *articlesService) GetArticlesWaitingForApproval(authToken string, page, limit int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		return nil, paginator.Pagination{}, errors.New("user not found from auth token")
	}

	// Use the existing repository method with proper filtering
	pagination := paginator.Pagination{
		Page:  page,
		Limit: limit,
	}
	req := request.ArticlesQueryRequest{
		Pagination: &pagination,
	}

	articles, paging, err := _i.Repo.GetAll(clientId, &user.UserLevelId, req)
	if err != nil {
		return nil, paging, err
	}

	// Build response
	var responses []*response.ArticleApprovalQueueResponse
	for _, article := range articles {
		response := &response.ArticleApprovalQueueResponse{
			ID:          article.ID,
			Title:       article.Title,
			Slug:        article.Slug,
			Description: article.Description,
			SubmittedAt: article.CreatedAt,
			CurrentStep: 1, // Will be updated with actual step
			CanApprove:  true,
		}
		responses = append(responses, response)
	}

	return responses, paging, nil
}

// CheckApprovalRequired checks if an article requires approval based on client settings
func (_i *articlesService) CheckApprovalRequired(authToken string, articleId uint, userId uint, userLevelId uint) (bool, error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// Get article to check category and other properties
	article, err := _i.Repo.FindOne(clientId, articleId)
	if err != nil {
		return true, err // Default to requiring approval on error
	}

	// Check if article is already exempt
	if article.ApprovalExempt != nil && *article.ApprovalExempt {
		return false, nil
	}

	// Check if article should bypass approval
	if article.BypassApproval != nil && *article.BypassApproval {
		return false, nil
	}

	// Check client-level settings (this would require the client approval settings service)
	// For now, we'll use a simple check
	// TODO: Integrate with ClientApprovalSettingsService

	// Check if workflow is set to no approval
	if article.WorkflowId != nil {
		workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, *article.WorkflowId)
		if err == nil && workflow != nil {
			if workflow.RequiresApproval != nil && !*workflow.RequiresApproval {
				return false, nil
			}
			if workflow.AutoPublish != nil && *workflow.AutoPublish {
				return false, nil
			}
		}
	}

	// Default to requiring approval
	return true, nil
}

// AutoApproveArticle automatically approves an article (for no-approval scenarios)
func (_i *articlesService) AutoApproveArticle(authToken string, articleId uint, reason string) error {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	article, err := _i.Repo.FindOne(clientId, articleId)
	if err != nil {
		return err
	}

	// Update article status to approved
	updates := map[string]interface{}{
		"status_id":             2, // Assuming 2 = approved
		"is_publish":            true,
		"published_at":          time.Now(),
		"current_approval_step": 0, // Reset approval step
	}

	// Convert updates map to article entity
	articleUpdate := &entity.Articles{}
	if isPublish, ok := updates["is_publish"].(bool); ok {
		articleUpdate.IsPublish = &isPublish
	}
	if publishedAt, ok := updates["published_at"].(time.Time); ok {
		articleUpdate.PublishedAt = &publishedAt
	}
	if currentApprovalStep, ok := updates["current_approval_step"].(int); ok {
		articleUpdate.CurrentApprovalStep = &currentApprovalStep
	}

	err = _i.Repo.Update(clientId, articleId, articleUpdate)
	if err != nil {
		return err
	}

	// Create approval flow record for audit trail
	approvalFlow := &entity.ArticleApprovalFlows{
		ArticleId:     articleId,
		WorkflowId:    *article.WorkflowId,
		CurrentStep:   0,
		StatusId:      2, // approved
		SubmittedById: *article.CreatedById,
		SubmittedAt:   time.Now(),
		CompletedAt:   &[]time.Time{time.Now()}[0],
		ClientId:      clientId,
	}

	_, err = _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create approval flow for auto-approved article")
		// Don't return error as article was already updated
	}

	_i.Log.Info().
		Str("article_id", fmt.Sprintf("%d", articleId)).
		Str("client_id", clientId.String()).
		Str("reason", reason).
		Msg("Article auto-approved")

	return nil
}

// GetClientApprovalSettings gets the approval settings for a client
func (_i *articlesService) GetClientApprovalSettings(authToken string) (*response.ClientApprovalSettingsResponse, error) {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	// This would require the ClientApprovalSettingsService
	// For now, return default settings
	return &response.ClientApprovalSettingsResponse{
		ClientId:            clientId.String(),
		RequiresApproval:    true, // Default to requiring approval
		AutoPublishArticles: false,
		IsActive:            true,
	}, nil
}

// SetArticleApprovalExempt sets whether an article is exempt from approval
func (_i *articlesService) SetArticleApprovalExempt(authToken string, articleId uint, exempt bool, reason string) error {
	// Extract clientId from authToken
	var clientId *uuid.UUID
	if authToken != "" {
		user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
		if user != nil && user.ClientId != nil {
			clientId = user.ClientId
			_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
		}
	}

	updates := map[string]interface{}{
		"approval_exempt": &exempt,
	}

	if exempt {
		// If exempt, also set bypass approval
		bypass := true
		updates["bypass_approval"] = &bypass
		updates["current_approval_step"] = 0
	}

	// Convert updates map to article entity
	articleUpdate := &entity.Articles{}
	if approvalExempt, ok := updates["approval_exempt"].(*bool); ok {
		articleUpdate.ApprovalExempt = approvalExempt
	}
	if bypassApproval, ok := updates["bypass_approval"].(*bool); ok {
		articleUpdate.BypassApproval = bypassApproval
	}
	if currentApprovalStep, ok := updates["current_approval_step"].(int); ok {
		articleUpdate.CurrentApprovalStep = &currentApprovalStep
	}

	err := _i.Repo.Update(clientId, articleId, articleUpdate)
	if err != nil {
		return err
	}

	_i.Log.Info().
		Str("article_id", fmt.Sprintf("%d", articleId)).
		Str("client_id", clientId.String()).
		Bool("exempt", exempt).
		Str("reason", reason).
		Msg("Article approval exemption updated")

	return nil
}

// findNextApprovalLevel finds the next higher level for approval
func (_i *articlesService) findNextApprovalLevel(clientId *uuid.UUID, currentLevelNumber int) int {
	// For now, we'll use a simple logic based on level numbers
	// Level 3 (POLRES) -> Level 2 (POLDAS) -> Level 1 (POLDAS)

	switch currentLevelNumber {
	case 3: // POLRES
		return 2 // Should be approved by POLDAS (Level 2)
	case 2: // POLDAS
		return 1 // Should be approved by Level 1
	case 1: // Highest level
		return 0 // No approval needed, can publish directly
	default:
		_i.Log.Warn().Int("currentLevel", currentLevelNumber).Msg("Unknown level, no approval needed")
		return 0
	}
}

// initializeMultiBranchFlow initializes the multi-branch flow by determining the first applicable step
func (_i *articlesService) initializeMultiBranchFlow(authToken string, flowId uint, submitterLevelId uint) error {
	// Get the approval flow
	flow, err := _i.ArticleApprovalFlowsRepo.FindOne(nil, flowId)
	if err != nil {
		return fmt.Errorf("failed to find approval flow: %w", err)
	}

	// Find the first applicable step based on submitter's user level
	nextSteps, err := _i.ArticleApprovalFlowsSvc.FindNextStepsForBranch(authToken, flow.WorkflowId, 0, submitterLevelId)
	if err != nil {
		return fmt.Errorf("failed to find next steps: %w", err)
	}

	if len(nextSteps) == 0 {
		// No applicable steps found - this shouldn't happen with proper workflow configuration
		_i.Log.Warn().Uint("flowId", flowId).Uint("submitterLevelId", submitterLevelId).Msg("No applicable steps found for multi-branch flow")
		return nil
	}

	// Update the flow with the first applicable step and branch information
	firstStep := nextSteps[0]
	flow.CurrentStep = firstStep.StepOrder

	// Set branch information if available
	if firstStep.BranchName != nil {
		flow.CurrentBranch = firstStep.BranchName

		// Initialize branch path
		branchPath := []string{*firstStep.BranchName}
		branchPathJSON, err := json.Marshal(branchPath)
		if err == nil {
			branchPathStr := string(branchPathJSON)
			flow.BranchPath = &branchPathStr
		}
	}

	// Update the flow
	err = _i.ArticleApprovalFlowsRepo.Update(flowId, flow)
	if err != nil {
		return fmt.Errorf("failed to update approval flow: %w", err)
	}

	_i.Log.Info().
		Uint("flowId", flowId).
		Uint("submitterLevelId", submitterLevelId).
		Int("currentStep", flow.CurrentStep).
		Str("currentBranch", func() string {
			if flow.CurrentBranch != nil {
				return *flow.CurrentBranch
			}
			return "none"
		}()).
		Msg("Multi-branch flow initialized successfully")

	return nil
}
