package com.codenetworkz.blog.services.impl;

import com.codenetworkz.blog.config.SlugGenerator;
import com.codenetworkz.blog.dtos.CategoryDto;
import com.codenetworkz.blog.dtos.PostDto;
import com.codenetworkz.blog.dtos.PostTagAndCategoryDto;
import com.codenetworkz.blog.dtos.TagDto;
import com.codenetworkz.blog.exceptions.InternalServerException;
import com.codenetworkz.blog.exceptions.ResourceNotAvailable;
import com.codenetworkz.blog.externalcall.ExternalCallFileService;
import com.codenetworkz.blog.mappers.CategoryMapper;
import com.codenetworkz.blog.mappers.PostMapper;
import com.codenetworkz.blog.mappers.TagMapper;
import com.codenetworkz.blog.models.Post;
import com.codenetworkz.blog.models.Status;

import com.codenetworkz.blog.repositories.PostRepository;
import com.codenetworkz.blog.services.*;
import org.springframework.transaction.annotation.Transactional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
public class PostServiceImpl implements PostService {

    private static final Logger log = LogManager.getLogger(PostServiceImpl.class);
    private final PostRepository postRepository;
    private final TagService tagService;
    private final CategoryService categoryService;
    private final SlugGenerator slugGenerator;
    private final ExternalCallFileService externalCallService;
    private final CategoryMapper categoryMapper;
    private final TagMapper tagMapper;
    private final PostMapper postMapper;


    PostServiceImpl(PostRepository postRepository, @Qualifier("userServiceImpl") UserService userService, TagService tagService,
                    CategoryService categoryService, SlugGenerator slugGenerator, ExternalCallFileService externalCallService,
                    CategoryMapper categoryMapper, TagMapper tagMapper, PostMapper postMapper) {
        this.postRepository = postRepository;
        this.categoryService=categoryService;
        this.tagService=tagService;
        this.slugGenerator=slugGenerator;
        this.externalCallService=externalCallService;
        this.categoryMapper=categoryMapper;
        this.tagMapper=tagMapper;
        this.postMapper = postMapper;
    }

    @Override
    @Transactional
    public Optional<PostDto> save(String name, String content,  List<Long> tagIds, List<Long> categoryIds, MultipartFile file, String isPublish, String author) throws InternalServerException, FileUploadException, ResourceNotAvailable {
        Post save;
        Post post = new Post();
        List<TagDto> tags = List.of();
        List<CategoryDto> categories = List.of();
        String slug = SlugGenerator.generateSlug(name);
        post.setSlug(slug);
        try {
            Optional<Post> findPost = findByPostSlug(post.getSlug());
            if (findPost.isPresent()) {
                throw new InternalServerException(name + " is already available");
            }
        }catch (Exception e) {
            throw new InternalServerException("there is a problem with Database.");
        }
        tags = tagService.findByTagIds(tagIds);
        categories = categoryService.findByCategoryIds(categoryIds);
       if (tags == null && categories == null) {
            throw new InternalServerException("No tags and Categories found");
       }
        post.setName(name);
        post.setContent(content);
        assert tags != null;
        post.setTags(tagMapper.tagDtoListToTagList(tags));
        post.setCategories(categoryMapper.categoryListDtoToCategoryList(categories));
        String upload = externalCallService.upload(file);
        post.setFeaturedImageUrl(upload);
        post.setCreatedBy(author);
        if(isPublish.equalsIgnoreCase("YES"))
            post.setIsPublished(Status.YES);
        else
            post.setIsPublished(Status.NO);
        post.setCreatedBy(author);
        try {
            save = postRepository.save(post);
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postToPostDto(Optional.of(save));
    }


    @Override
    public Optional<PostDto> delete(String postSlug) throws InternalServerException, ResourceNotAvailable {
        Post update;
        Optional<Post> findPost;
        try {
            findPost = findByPostSlug(postSlug);
        } catch (Exception e) {
            throw new InternalServerException("there is a problem with Database.");
        }
        if (findPost.isEmpty()) {
            throw new ResourceNotAvailable("Post is Not available ");
        }
        findPost.get().setIsDelete(Status.YES);
        findPost.get().setDeleteAt(LocalDateTime.now());
        findPost.get().setIsActive(Status.NO);
        findPost.get().setIsPublished(Status.NO);
        try {
            update = postRepository.save(findPost.get());
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return  postMapper.postToPostDto(Optional.of(update));
    }

    @Override
    @Transactional
    public Optional<PostDto> update(String postSlug, String name, String content, List<Long> tagIds, List<Long> categoryIds,  MultipartFile file, String isPublish, String author) throws InternalServerException, FileUploadException, ResourceNotAvailable {
        Post update;
        Optional<Post> findPost;
        Post post = new Post();
        List<TagDto> tags;
        List<CategoryDto> categories;
        post.setSlug(SlugGenerator.generateSlug(name));
        try {
            findPost = findByPostSlug(postSlug);

            tags = tagService.findByTagIds(tagIds);
            categories = categoryService.findByCategoryIds(categoryIds);
        } catch (Exception e) {
            throw new InternalServerException("there is a problem with Database.");
        }
       if (tags==null&& categories==null){
           throw new InternalServerException("No tags and Categories found");
       }
        if (findPost.isEmpty()) {
            throw new ResourceNotAvailable("Post is Not available ");
        }
        findPost.get().setName(name);
        findPost.get().setSlug(slugGenerator.generateSlug(name));
        findPost.get().setContent(content);

        findPost.get().setCategories(categoryMapper.categoryListDtoToCategoryList(categories));
        findPost.get().setTags(tagMapper.tagDtoListToTagList(tags));
        String upload = externalCallService.upload(file);
        findPost.get().setFeaturedImageUrl(upload);
        findPost.get().setUpdateAt(LocalDateTime.now());
        findPost.get().setUpdateBy(author);
        if (isPublish.equals(Status.YES)) {
            findPost.get().setIsPublished(Status.YES);
        } else {
            findPost.get().setIsPublished(Status.NO);
        }
        try {
            update = postRepository.save(post);
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postToPostDto(Optional.of(update));
    }

    @Override
    public List<PostDto> findAll() throws InternalServerException, ResourceNotAvailable {
        List<Post> all;
        try {
            all = postRepository.findAllAndIsDelete(Status.NO);
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postListToPostDtoList(all);
    }

    @Override
    public Optional<PostTagAndCategoryDto> getPostRequestData() throws InternalServerException {
        PostTagAndCategoryDto postTagAndCategoryDto = new PostTagAndCategoryDto();
        List<TagDto> tagDtos = tagService.findAll().stream().filter(categoryDto -> categoryDto.getActiveStatus().equals(Status.YES)).toList();
        List<CategoryDto> categoryDtos = categoryService.findAll().stream().filter(categoryDto -> categoryDto.getActiveStatus().equals(Status.YES)).toList();
        postTagAndCategoryDto.setTags(tagDtos);
        postTagAndCategoryDto.setCategories(categoryDtos);
        return Optional.of(postTagAndCategoryDto);
    }

    @Override
    public Optional<Post> findByPostSlug(String postSlug) throws InternalServerException, ResourceNotAvailable {
        Optional<Post> post;
        try {
            post = postRepository.findBySlug(postSlug);
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return post;
    }

    @Override
    public Optional<PostDto> changeStatusPublish(String postSlug) throws InternalServerException, ResourceNotAvailable {
        Optional<Post> saved;
        Optional<Post> findPost = findByPostSlug(postSlug);
        if (findPost.isEmpty()) {
            throw new ResourceNotAvailable("Post is Not available ");
        }
        if(findPost.get().getIsPublished().equals(Status.NO)){
            findPost.get().setIsPublished(Status.YES);
        }else {
            findPost.get().setIsPublished(Status.YES);
        }
        try {
            saved = Optional.of(postRepository.save(findPost.get()));
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postToPostDto(saved);
    }

    @Override
    public Optional<PostDto> changeActivateStatus(String postSlug) throws InternalServerException, ResourceNotAvailable {
        Post saved;
        Optional<Post> post = findByPostSlug(postSlug);
        if (post.isEmpty()) {
            throw new ResourceNotAvailable("Post is Not available ");
        }
        if(post.get().getIsActive().equals(Status.NO)){
            post.get().setIsActive(Status.YES);
        }else {
            post.get().setIsActive(Status.NO);
        }
        try {
            saved =postRepository.save(post.get());
        } catch (Exception e) {
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postToPostDto(Optional.of(saved));
    }

    @Override
    public Optional<PostDto> uploadFeaturedImage(String postSlug,MultipartFile file) throws InternalServerException, ResourceNotAvailable, FileUploadException {
        Optional<Post> findPost;
        Optional<Post> saved;
        try {
            findPost = findByPostSlug(postSlug);
            String upload = externalCallService.upload(file);
            if (findPost.isEmpty()) {
                throw new ResourceNotAvailable("Post is Not available ");
            }
            findPost.get().setFeaturedImageUrl(upload);
            findPost.get().setUpdateAt(LocalDateTime.now());
            saved = Optional.of(postRepository.save(findPost.get()));
        }catch (Exception e){
            throw new InternalServerException(e.getMessage());
        }
        return postMapper.postToPostDto(saved);
    }

    @Override
    public MultipartFile downloadFeaturedImage(String postSlug, String fileCode) throws InternalServerException, ResourceNotAvailable, FileUploadException {
        Optional<Post> findPost;
        MultipartFile download;
        try {
            findPost = findByPostSlug(postSlug);
            if (findPost.isEmpty()) {
                throw new ResourceNotAvailable("Post is Not available ");
            }
            download = externalCallService.download(fileCode);
        }catch (Exception e){
            throw new InternalServerException(e.getMessage());
        }
        return download ;
    }

    @Override
    public Optional<PostDto> deleteFeaturedImage(String postSlug, String fileCode) throws InternalServerException, ResourceNotAvailable, FileUploadException {
        Optional<Post> findPost;
        Optional<Post> saved = Optional.empty();
        try {
            findPost = findByPostSlug(postSlug);
            if (findPost.isEmpty()) {
                throw new ResourceNotAvailable("Post is Not available ");
            }
            String upload = externalCallService.delete(fileCode);
            if(upload.contains("successfully")){
                findPost.get().setFeaturedImageUrl(null);
                findPost.get().setUpdateAt(LocalDateTime.now());
                saved = Optional.of(postRepository.save(findPost.get()));
            }
        }catch (Exception e){
            throw new InternalServerException(e.getMessage());
        }
         return postMapper.postToPostDto(saved);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<PostDto> getPostWithPagination(Integer page, Integer size) throws InternalServerException {
        // Create a Pageable instance with the desired page number and size
        Pageable pageable = PageRequest.of(page, size, Sort.by("id").ascending());

        // Fetch the paginated results
        Page<Post> posts;
        try {
            posts = postRepository.findAllByIsDelete(Status.NO, pageable);
        } catch (Exception e) {
            throw new InternalServerException("There is a problmes with Database Connection");
        }
        List<PostDto> postDtos = posts.stream()
                .map(post -> postMapper.postToPostDto(Optional.of(post)).get())
                .toList();

        long totalElements = posts.getTotalElements();

        return new PageImpl<>(postDtos, pageable, totalElements);
    }


}
