package de.avatic.lcc.service.access;

import de.avatic.lcc.dto.configuration.nodes.update.NodeUpdateDTO;
import de.avatic.lcc.dto.configuration.nodes.view.NodeDetailDTO;
import de.avatic.lcc.dto.generic.NodeDTO;
import de.avatic.lcc.dto.generic.NodeType;
import de.avatic.lcc.repositories.NodeRepository;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.repositories.users.UserNodeRepository;
import de.avatic.lcc.service.transformer.generic.NodeTransformer;
import de.avatic.lcc.service.transformer.nodes.NodeDetailTransformer;
import de.avatic.lcc.service.transformer.nodes.NodeUpdateDTOTransformer;
import de.avatic.lcc.service.users.AuthorizationService;
import de.avatic.lcc.util.exception.badrequest.NodeNotFoundException;
import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Service class for managing lifecycle operations for nodes and related entities.
 * Provides methods to list, retrieve, update, delete, and create nodes in the system.
 */
@Service
public class NodeService {

    private final NodeRepository nodeRepository;
    private final NodeTransformer nodeTransformer;
    private final NodeDetailTransformer nodeDetailTransformer;
    private final NodeUpdateDTOTransformer nodeUpdateDTOTransformer;
    private final UserNodeRepository userNodeRepository;
    private final AuthorizationService authorizationService;

    public NodeService(NodeRepository nodeRepository, NodeTransformer nodeTransformer, NodeDetailTransformer nodeDetailTransformer, NodeUpdateDTOTransformer nodeUpdateDTOTransformer, UserNodeRepository userNodeRepository, AuthorizationService authorizationService) {
        this.nodeRepository = nodeRepository;
        this.nodeTransformer = nodeTransformer;
        this.nodeDetailTransformer = nodeDetailTransformer;
        this.nodeUpdateDTOTransformer = nodeUpdateDTOTransformer;
        this.userNodeRepository = userNodeRepository;
        this.authorizationService = authorizationService;
    }

    /**
     * Retrieves a paginated list of nodes filtered by the provided keyword.
     *
     * @param filter the filter keyword to search nodes.
     * @param page   the page number to retrieve.
     * @param limit  the number of nodes per page.
     * @return a {@link SearchQueryResult} containing the paginated list of nodes.
     */
    public SearchQueryResult<NodeDTO> listNodes(String filter, int page, int limit) {
        return SearchQueryResult.map(nodeRepository.listNodes(filter, true, new SearchQueryPagination(page, limit)), nodeTransformer::toNodeDTO);
    }

    /**
     * Retrieves a paginated detailed view of nodes based on the provided filter.
     *
     * @param filter the filter keyword to search nodes.
     * @param page   the page number to retrieve.
     * @param limit  the number of nodes per page.
     * @return a {@link SearchQueryResult} containing detailed information of nodes.
     */
    public SearchQueryResult<NodeDetailDTO> listNodesView(String filter, int page, int limit) {
        return SearchQueryResult.map(nodeRepository.listNodes(filter, true, new SearchQueryPagination(page, limit)), nodeDetailTransformer::toNodeDetailDTO);
    }

    /**
     * Retrieves the detailed view of a specific node by its ID.
     *
     * @param id the ID of the node to retrieve.
     * @return a {@link NodeDetailDTO} containing detailed information of the node.
     * @throws NodeNotFoundException if no node is found with the specified ID.
     */
    public NodeDetailDTO getNode(Integer id) {
        return nodeDetailTransformer.toNodeDetailDTO(nodeRepository.getById(id).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.NODE, "id", id.toString())));
    }

    /**
     * Marks a node as deprecated by its ID.
     *
     * @param id the ID of the node to delete.
     * @return the ID of the deprecated node.
     * @throws NodeNotFoundException if no node is found with the specified ID.
     */
    public Integer deleteNode(Integer id) {
        return nodeRepository.setDeprecatedById(id).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.NODE, "id", id.toString()));
    }

    /**
     * Updates an existing node with the provided data.
     *
     * @param dto the {@link NodeUpdateDTO} containing the update data for the node.
     * @return the ID of the updated node.
     * @throws NodeNotFoundException if no node is found for the update.
     */
    public Integer updateNode(NodeUpdateDTO dto) {
        return nodeRepository.update(nodeUpdateDTOTransformer.fromNodeUpdateDTO(dto)).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.NODE, "id", dto.getId().toString()));
    }

    /**
     * Searches for nodes by filter and type with an option to include user-specific nodes.
     *
     * @param filter          the filter keyword to search nodes.
     * @param limit           the maximum number of nodes to retrieve.
     * @param nodeType        the type of node to restrict the search to.
     * @param includeUserNode if true, includes user-specific nodes in the search results.
     * @return a list of {@link NodeDTO} objects representing the search results.
     */
    public List<NodeDTO> searchNode(String filter, int limit, NodeType nodeType, boolean includeUserNode) {
        List<NodeDTO> nodes = new ArrayList<>();
        int userId = authorizationService.getUserId();

        if (includeUserNode && NodeType.SOURCE.equals(nodeType)) {
            nodes.addAll(userNodeRepository.searchNode(filter, limit, userId, true).stream().map(nodeTransformer::toUserNodeDTO).toList());
            nodes.forEach(node -> node.setUserNode(true));
            limit -= nodes.size();
        }

        if (limit > 0) {
            nodes.addAll(nodeRepository.searchNode(filter, limit, nodeType, true).stream().map(nodeTransformer::toNodeDTO).toList());
        }

        return nodes;
    }


}
