package service

import (
	"errors"
	"fmt"
	"netidhub-saas-be/app/database/entity"
	"netidhub-saas-be/app/module/clients/mapper"
	"netidhub-saas-be/app/module/clients/repository"
	"netidhub-saas-be/app/module/clients/request"
	"netidhub-saas-be/app/module/clients/response"
	usersRepository "netidhub-saas-be/app/module/users/repository"
	usersRequest "netidhub-saas-be/app/module/users/request"
	usersService "netidhub-saas-be/app/module/users/service"
	"netidhub-saas-be/utils/paginator"

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

	utilSvc "netidhub-saas-be/utils/service"
)

// ClientsService
type clientsService struct {
	Repo      repository.ClientsRepository
	UsersRepo usersRepository.UsersRepository
	UsersSvc  usersService.UsersService
	Log       zerolog.Logger
}

// ClientsService define interface of IClientsService
type ClientsService interface {
	All(authToken string, req request.ClientsQueryRequest) (clients []*response.ClientsResponse, paging paginator.Pagination, err error)
	Show(id uuid.UUID) (clients *response.ClientsResponse, err error)
	Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error)
	Update(id uuid.UUID, req request.ClientsUpdateRequest) (err error)
	Delete(id uuid.UUID) error

	// New hierarchy methods
	CreateSubClient(parentId uuid.UUID, req request.ClientsCreateRequest) (*entity.Clients, error)
	MoveClient(clientId uuid.UUID, req request.MoveClientRequest) error
	GetHierarchy(clientId uuid.UUID) (*response.ClientHierarchyResponse, error)
	GetClientStats(clientId uuid.UUID) (*response.ClientStatsResponse, error)
	BulkCreateSubClients(req request.BulkCreateSubClientsRequest) (*response.BulkOperationResponse, error)

	// Client with user creation
	CreateClientWithUser(req request.ClientWithUserCreateRequest) (*response.ClientWithUserResponse, error)
}

// NewClientsService init ClientsService
func NewClientsService(repo repository.ClientsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository, usersSvc usersService.UsersService) ClientsService {

	return &clientsService{
		Repo:      repo,
		Log:       log,
		UsersRepo: usersRepo,
		UsersSvc:  usersSvc,
	}
}

// All implement interface of ClientsService
func (_i *clientsService) All(authToken string, req request.ClientsQueryRequest) (clientss []*response.ClientsResponse, paging paginator.Pagination, 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")
		}
	}

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

	for _, result := range results {
		clientss = append(clientss, mapper.ClientsResponseMapper(result))
	}

	return
}

func (_i *clientsService) Show(id uuid.UUID) (clients *response.ClientsResponse, err error) {
	result, err := _i.Repo.FindOne(id)
	if err != nil {
		return nil, err
	}

	return mapper.ClientsResponseMapper(result), nil
}

func (_i *clientsService) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) {
	_i.Log.Info().Interface("data", req).Msg("")

	// Convert request to entity
	newReq := &entity.Clients{
		Name:           req.Name,
		Description:    req.Description,
		ClientType:     req.ClientType,
		ParentClientId: req.ParentClientId,
		MaxUsers:       req.MaxUsers,
		MaxStorage:     req.MaxStorage,
		Settings:       req.Settings,
	}

	_i.Log.Info().Interface("token", authToken).Msg("")
	createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	_i.Log.Info().Interface("token", authToken).Msg("")
	newReq.CreatedById = &createdBy.ID

	newReq.ID = uuid.New()

	_i.Log.Info().Interface("new data", newReq).Msg("")

	return _i.Repo.Create(newReq)
}

func (_i *clientsService) Update(id uuid.UUID, req request.ClientsUpdateRequest) (err error) {
	_i.Log.Info().Interface("data", req).Msg("")

	// Convert request to entity
	updateReq := &entity.Clients{
		Name:           *req.Name,
		Description:    req.Description,
		ClientType:     *req.ClientType,
		ParentClientId: req.ParentClientId,
		MaxUsers:       req.MaxUsers,
		MaxStorage:     req.MaxStorage,
		Settings:       req.Settings,
		IsActive:       req.IsActive,
	}

	return _i.Repo.Update(id, updateReq)
}

func (_i *clientsService) Delete(id uuid.UUID) error {
	result, err := _i.Repo.FindOne(id)
	if err != nil {
		return err
	}

	isActive := false
	result.IsActive = &isActive
	return _i.Repo.Update(id, result)
}

// =====================================================================
// NEW HIERARCHY METHODS
// =====================================================================

// CreateSubClient creates a client under a parent
func (_i *clientsService) CreateSubClient(parentId uuid.UUID, req request.ClientsCreateRequest) (*entity.Clients, error) {
	// Validate parent exists
	_, err := _i.Repo.FindOne(parentId)
	if err != nil {
		return nil, errors.New("parent client not found")
	}

	// Set client type and parent
	req.ClientType = "sub_client"
	req.ParentClientId = &parentId

	// Convert to entity
	newReq := &entity.Clients{
		Name:           req.Name,
		Description:    req.Description,
		ClientType:     req.ClientType,
		ParentClientId: req.ParentClientId,
		MaxUsers:       req.MaxUsers,
		MaxStorage:     req.MaxStorage,
		Settings:       req.Settings,
	}

	newReq.ID = uuid.New()

	return _i.Repo.Create(newReq)
}

// MoveClient moves a client to different parent
func (_i *clientsService) MoveClient(clientId uuid.UUID, req request.MoveClientRequest) error {
	client, err := _i.Repo.FindOne(clientId)
	if err != nil {
		return errors.New("client not found")
	}

	// If moving to root (standalone)
	if req.TargetParentId == nil {
		client.ClientType = "standalone"
		client.ParentClientId = nil
		return _i.Repo.Update(clientId, client)
	}

	// Validate target parent exists
	_, err = _i.Repo.FindOne(*req.TargetParentId)
	if err != nil {
		return errors.New("target parent not found")
	}

	// Move
	return _i.Repo.MoveClient(clientId, *req.TargetParentId)
}

// GetHierarchy gets full client tree
func (_i *clientsService) GetHierarchy(clientId uuid.UUID) (*response.ClientHierarchyResponse, error) {
	client, err := _i.Repo.GetWithHierarchy(clientId)
	if err != nil {
		return nil, err
	}

	return _i.buildHierarchyResponse(client, 0, []string{}), nil
}

// buildHierarchyResponse recursively builds hierarchy
func (_i *clientsService) buildHierarchyResponse(client *entity.Clients, level int, path []string) *response.ClientHierarchyResponse {
	currentPath := append(path, client.Name)

	resp := &response.ClientHierarchyResponse{
		ID:             client.ID,
		Name:           client.Name,
		Description:    client.Description,
		ClientType:     client.ClientType,
		Level:          level,
		Path:           currentPath,
		ParentClientId: client.ParentClientId,
		IsActive:       client.IsActive,
	}

	// Count users (simplified - would need proper DB access)
	resp.CurrentUsers = 0 // TODO: implement user count

	// Build sub-clients recursively
	if client.SubClients != nil {
		for _, subClient := range client.SubClients {
			resp.SubClients = append(resp.SubClients,
				*_i.buildHierarchyResponse(&subClient, level+1, currentPath))
		}
	}

	return resp
}

// GetClientStats gets comprehensive statistics
func (_i *clientsService) GetClientStats(clientId uuid.UUID) (*response.ClientStatsResponse, error) {
	client, err := _i.Repo.FindOne(clientId)
	if err != nil {
		return nil, err
	}

	stats, err := _i.Repo.GetClientStats(clientId)
	if err != nil {
		return nil, err
	}

	isParent, _ := _i.Repo.IsParentClient(clientId)

	return &response.ClientStatsResponse{
		ClientId:       client.ID,
		ClientName:     client.Name,
		TotalUsers:     stats["total_users"].(int),
		TotalArticles:  stats["total_articles"].(int),
		SubClientCount: stats["sub_client_count"].(int),
		IsParent:       isParent,
	}, nil
}

// BulkCreateSubClients creates multiple sub-clients
func (_i *clientsService) BulkCreateSubClients(req request.BulkCreateSubClientsRequest) (*response.BulkOperationResponse, error) {
	results := []response.BulkOperationResult{}
	successful := 0
	failed := 0

	for i, subClientReq := range req.SubClients {
		createReq := request.ClientsCreateRequest{
			Name:           subClientReq.Name,
			Description:    subClientReq.Description,
			ClientType:     "sub_client",
			ParentClientId: &req.ParentClientId,
			MaxUsers:       subClientReq.MaxUsers,
			MaxStorage:     subClientReq.MaxStorage,
		}

		client, err := _i.CreateSubClient(req.ParentClientId, createReq)
		if err != nil {
			failed++
			errMsg := err.Error()
			results = append(results, response.BulkOperationResult{
				Index:   i,
				Name:    subClientReq.Name,
				Success: false,
				Error:   &errMsg,
			})
		} else {
			successful++
			results = append(results, response.BulkOperationResult{
				Index:    i,
				ClientId: &client.ID,
				Name:     client.Name,
				Success:  true,
			})
		}
	}

	return &response.BulkOperationResponse{
		TotalRequested: len(req.SubClients),
		Successful:     successful,
		Failed:         failed,
		Results:        results,
	}, nil
}

// CreateClientWithUser creates a client and admin user in one transaction
func (_i *clientsService) CreateClientWithUser(req request.ClientWithUserCreateRequest) (*response.ClientWithUserResponse, error) {
	_i.Log.Info().Interface("data", req).Msg("Creating client with admin user (Public endpoint)")

	// Step 1: Create the client
	clientReq := req.Client
	newClient := &entity.Clients{
		Name:           clientReq.Name,
		Description:    clientReq.Description,
		ClientType:     clientReq.ClientType,
		ParentClientId: clientReq.ParentClientId,
		MaxUsers:       clientReq.MaxUsers,
		MaxStorage:     clientReq.MaxStorage,
		Settings:       clientReq.Settings,
	}

	// Generate new UUID for client
	newClient.ID = uuid.New()

	// For public endpoint, no created by user
	newClient.CreatedById = nil

	// Create client
	createdClient, err := _i.Repo.Create(newClient)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create client")
		return nil, fmt.Errorf("failed to create client: %w", err)
	}

	_i.Log.Info().Interface("clientId", createdClient.ID).Msg("Client created successfully")

	// Step 2: Create admin user for the client
	adminUserReq := req.AdminUser

	// Convert to UsersCreateRequest format
	userCreateReq := usersRequest.UsersCreateRequest{
		Username:            adminUserReq.Username,
		Email:               adminUserReq.Email,
		Fullname:            adminUserReq.Fullname,
		Password:            adminUserReq.Password,
		PhoneNumber:         adminUserReq.PhoneNumber,
		Address:             adminUserReq.Address,
		WorkType:            adminUserReq.WorkType,
		GenderType:          adminUserReq.GenderType,
		IdentityType:        adminUserReq.IdentityType,
		IdentityGroup:       adminUserReq.IdentityGroup,
		IdentityGroupNumber: adminUserReq.IdentityGroupNumber,
		IdentityNumber:      adminUserReq.IdentityNumber,
		DateOfBirth:         adminUserReq.DateOfBirth,
		LastEducation:       adminUserReq.LastEducation,
		ClientId:            &createdClient.ID,
		// Set default admin level and role (you may need to adjust these based on your system)
		UserLevelId: 1, // Assuming level 1 is admin level
		UserRoleId:  1, // Assuming role 1 is admin role
	}

	// Create user with the new client ID
	createdUser, err := _i.UsersSvc.Save("", userCreateReq)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create admin user")
		// Rollback: delete the created client
		_i.Repo.Delete(createdClient.ID)
		return nil, fmt.Errorf("failed to create admin user: %w", err)
	}

	_i.Log.Info().Interface("userId", createdUser.ID).Msg("Admin user created successfully")
	_i.Log.Info().Interface("createdClient", createdClient).Msg("Created Client")

	// Step 3: Prepare response
	clientResponse := mapper.ClientsResponseMapper(createdClient)

	adminUserResponse := response.AdminUserResponse{
		ID:                  createdUser.ID,
		Username:            createdUser.Username,
		Email:               createdUser.Email,
		Fullname:            createdUser.Fullname,
		UserLevelId:         createdUser.UserLevelId,
		UserRoleId:          createdUser.UserRoleId,
		PhoneNumber:         createdUser.PhoneNumber,
		Address:             createdUser.Address,
		WorkType:            createdUser.WorkType,
		GenderType:          createdUser.GenderType,
		IdentityType:        createdUser.IdentityType,
		IdentityGroup:       createdUser.IdentityGroup,
		IdentityGroupNumber: createdUser.IdentityGroupNumber,
		IdentityNumber:      createdUser.IdentityNumber,
		DateOfBirth:         createdUser.DateOfBirth,
		LastEducation:       createdUser.LastEducation,
		KeycloakId:          createdUser.KeycloakId,
		ClientId:            *createdUser.ClientId,
		IsActive:            *createdUser.IsActive,
		CreatedAt:           createdUser.CreatedAt,
		UpdatedAt:           createdUser.UpdatedAt,
	}

	return &response.ClientWithUserResponse{
		Client:    *clientResponse,
		AdminUser: adminUserResponse,
		Message:   fmt.Sprintf("Client '%s' and admin user '%s' created successfully", createdClient.Name, createdUser.Username),
	}, nil
}
