package service

import (
	"context"

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

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type ApprovalFlowService interface {
	CreateApprovalFlow(ctx context.Context, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
	GetApprovalFlow(ctx context.Context, id uuid.UUID) (*contract.ApprovalFlowResponse, error)
	GetApprovalFlowByDepartment(ctx context.Context, departmentID uuid.UUID) (*contract.ApprovalFlowResponse, error)
	UpdateApprovalFlow(ctx context.Context, id uuid.UUID, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
	DeleteApprovalFlow(ctx context.Context, id uuid.UUID) error
	ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error)
}

type ApprovalFlowServiceImpl struct {
	db               *gorm.DB
	flowRepo         *repository.ApprovalFlowRepository
	stepRepo         *repository.ApprovalFlowStepRepository
	txManager        *repository.TxManager
}

func NewApprovalFlowService(
	db *gorm.DB,
	flowRepo *repository.ApprovalFlowRepository,
	stepRepo *repository.ApprovalFlowStepRepository,
	txManager *repository.TxManager,
) *ApprovalFlowServiceImpl {
	return &ApprovalFlowServiceImpl{
		db:        db,
		flowRepo:  flowRepo,
		stepRepo:  stepRepo,
		txManager: txManager,
	}
}

func (s *ApprovalFlowServiceImpl) CreateApprovalFlow(ctx context.Context, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error) {
	flow := &entities.ApprovalFlow{
		DepartmentID: req.DepartmentID,
		Name:         req.Name,
		Description:  req.Description,
		IsActive:     req.IsActive,
	}
	
	err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.flowRepo.Create(txCtx, flow); err != nil {
			return err
		}
		
		if len(req.Steps) > 0 {
			steps := make([]entities.ApprovalFlowStep, len(req.Steps))
			for i, stepReq := range req.Steps {
				if stepReq.ApproverRoleID == nil && stepReq.ApproverUserID == nil {
					return gorm.ErrInvalidData
				}
				
				steps[i] = entities.ApprovalFlowStep{
					FlowID:         flow.ID,
					StepOrder:      stepReq.StepOrder,
					ParallelGroup:  stepReq.ParallelGroup,
					ApproverRoleID: stepReq.ApproverRoleID,
					ApproverUserID: stepReq.ApproverUserID,
					Required:       stepReq.Required,
				}
			}
			
			if err := s.stepRepo.CreateBulk(txCtx, steps); err != nil {
				return err
			}
		}
		
		return nil
	})
	
	if err != nil {
		return nil, err
	}
	
	result, err := s.flowRepo.Get(ctx, flow.ID)
	if err != nil {
		return nil, err
	}
	
	return transformApprovalFlowToResponse(result), nil
}

func (s *ApprovalFlowServiceImpl) GetApprovalFlow(ctx context.Context, id uuid.UUID) (*contract.ApprovalFlowResponse, error) {
	flow, err := s.flowRepo.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	
	return transformApprovalFlowToResponse(flow), nil
}

func (s *ApprovalFlowServiceImpl) GetApprovalFlowByDepartment(ctx context.Context, departmentID uuid.UUID) (*contract.ApprovalFlowResponse, error) {
	flow, err := s.flowRepo.GetByDepartment(ctx, departmentID)
	if err != nil {
		return nil, err
	}
	
	return transformApprovalFlowToResponse(flow), nil
}

func (s *ApprovalFlowServiceImpl) UpdateApprovalFlow(ctx context.Context, id uuid.UUID, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error) {
	flow, err := s.flowRepo.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	
	flow.DepartmentID = req.DepartmentID
	flow.Name = req.Name
	flow.Description = req.Description
	flow.IsActive = req.IsActive
	
	err = s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.flowRepo.Update(txCtx, flow); err != nil {
			return err
		}
		
		if err := s.stepRepo.DeleteByFlow(txCtx, id); err != nil {
			return err
		}
		
		if len(req.Steps) > 0 {
			steps := make([]entities.ApprovalFlowStep, len(req.Steps))
			for i, stepReq := range req.Steps {
				if stepReq.ApproverRoleID == nil && stepReq.ApproverUserID == nil {
					return gorm.ErrInvalidData
				}
				
				steps[i] = entities.ApprovalFlowStep{
					FlowID:         flow.ID,
					StepOrder:      stepReq.StepOrder,
					ParallelGroup:  stepReq.ParallelGroup,
					ApproverRoleID: stepReq.ApproverRoleID,
					ApproverUserID: stepReq.ApproverUserID,
					Required:       stepReq.Required,
				}
			}
			
			if err := s.stepRepo.CreateBulk(txCtx, steps); err != nil {
				return err
			}
		}
		
		return nil
	})
	
	if err != nil {
		return nil, err
	}
	
	result, err := s.flowRepo.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	
	return transformApprovalFlowToResponse(result), nil
}

func (s *ApprovalFlowServiceImpl) DeleteApprovalFlow(ctx context.Context, id uuid.UUID) error {
	return s.flowRepo.Delete(ctx, id)
}

func (s *ApprovalFlowServiceImpl) ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error) {
	filter := repository.ListApprovalFlowsFilter{
		DepartmentID: req.DepartmentID,
		IsActive:     req.IsActive,
	}
	
	flows, total, err := s.flowRepo.List(ctx, filter, req.Limit, req.Offset)
	if err != nil {
		return nil, err
	}
	
	items := make([]*contract.ApprovalFlowResponse, len(flows))
	for i, flow := range flows {
		items[i] = transformApprovalFlowToResponse(&flow)
	}
	
	return &contract.ListApprovalFlowsResponse{
		Items: items,
		Total: total,
	}, nil
}

func transformApprovalFlowToResponse(flow *entities.ApprovalFlow) *contract.ApprovalFlowResponse {
	resp := &contract.ApprovalFlowResponse{
		ID:           flow.ID,
		DepartmentID: flow.DepartmentID,
		Name:         flow.Name,
		Description:  flow.Description,
		IsActive:     flow.IsActive,
		CreatedAt:    flow.CreatedAt,
		UpdatedAt:    flow.UpdatedAt,
	}
	
	if flow.Department != nil {
		resp.Department = &contract.DepartmentResponse{
			ID:   flow.Department.ID,
			Name: flow.Department.Name,
		}
	}
	
	if len(flow.Steps) > 0 {
		resp.Steps = make([]contract.ApprovalFlowStepResponse, len(flow.Steps))
		for i, step := range flow.Steps {
			stepResp := contract.ApprovalFlowStepResponse{
				ID:             step.ID,
				StepOrder:      step.StepOrder,
				ParallelGroup:  step.ParallelGroup,
				ApproverRoleID: step.ApproverRoleID,
				ApproverUserID: step.ApproverUserID,
				Required:       step.Required,
				CreatedAt:      step.CreatedAt,
				UpdatedAt:      step.UpdatedAt,
			}
			
			if step.ApproverRole != nil {
				stepResp.ApproverRole = &contract.RoleResponse{
					ID:   step.ApproverRole.ID,
					Name: step.ApproverRole.Name,
				}
			}
			
			if step.ApproverUser != nil {
				stepResp.ApproverUser = &contract.UserResponse{
					ID:    step.ApproverUser.ID,
					Name:  step.ApproverUser.Name,
					Email: step.ApproverUser.Email,
				}
			}
			
			resp.Steps[i] = stepResp
		}
	}
	
	return resp
}