package me.qwq.doghouse.controller.blog;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;

import lombok.extern.slf4j.Slf4j;
import me.qwq.doghouse.component.PostTypeComponent;
import me.qwq.doghouse.controller.common.BaseController;
import me.qwq.doghouse.entity.*;
import me.qwq.doghouse.entity.config.CommentConfig;
import me.qwq.doghouse.entity.config.SiteConfig;
import me.qwq.doghouse.entity.Post;
import me.qwq.doghouse.enums.PostStatusEnum;
import me.qwq.doghouse.exception.PageNotFoundException;
import me.qwq.doghouse.exception.RException;
import me.qwq.doghouse.interfaces.IPostTypeConfig;
import me.qwq.doghouse.pojo.bo.PostPassJar;
import me.qwq.doghouse.pojo.dto.R;
import me.qwq.doghouse.service.*;
import me.qwq.doghouse.service.post.AbstractPostService;
import me.qwq.doghouse.service.post.PostService;
import me.qwq.doghouse.util.Cryptos;
import me.qwq.doghouse.util.IPUtils;
import me.qwq.doghouse.util.MarkDownUtils;
import me.qwq.doghouse.util.SessionUtils;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Objects;
import java.util.Optional;


/**
 * 前端控制器
 * @author Doghole
 */
@Controller
@Slf4j
public class PostFrontendController extends BaseController {
    
    @Autowired
    PostTypeComponent postTypeComponent;

    @Autowired
    PostService postService;

    @Autowired
    CommentService commentService;

    @Autowired
    MarkdownService markdownService;
    
    @Autowired
    MailService mailService;
    
    @Autowired
    SiteConfig siteConfig;
    
    @Autowired
    CommentConfig commentConfig;
    
    @Autowired
    IpService ipService;
    
    @Autowired
    ThemeService themeService;
    
    /**
     * 文章详情
     */
    @GetMapping({
        "/p/{postId}", "/p/{postId}/{page}", 
        "/n/{postName}", "/n/{postName}/{page}", 
        "/{postName}.html", "/{postName}.html/{page}"})
    public String detail(
            @PathVariable(value="postId", required=false) Long postId,
            @PathVariable(value="postName", required=false) String postName,
            @PathVariable(value="page", required=false) Long commentPage) {
        
        Boolean isLogin = isLogin();
        // 获得文章info
        Post post = postService.getByIdOrName(postId, postName, isLogin);
        
        if (post == null) {
            throw new PageNotFoundException();
        }
        
        IPostTypeConfig<?> postTypeConfig = postTypeComponent.getPostTypeConfig(post.getType());
        AbstractPostService<?, ?> postTypeService = postTypeComponent.getPostTypeService(post.getType());
        
        if (!postTypeConfig.getEnabled()) {
            throw new PageNotFoundException();
        }
        
        // <link rel="canonical" href="https://example.com/dresses/green-dresses" />
        // 检查规范网页
        // TODO 使用 UriComponentsBuilder
        String canonical = post.getLink();
        String requestUri = request.getRequestURI();
        if (!requestUri.startsWith(canonical)) {
            
            String[] split = requestUri.split("/");
            
            // 获取除却 2 级 uri 后的参数并做重定向
            requestUri = requestUri.replace(String.format("/%s/%s", split[1], split[2]), canonical);
            return "redirect:" + requestUri;
        }
        
        request.setAttribute("canonical", siteConfig.getAddress() + canonical);
        
        // 文章校验、清除
        Optional<String> detailCheck = postTypeService.detailCheckAndJump(post);
        if (detailCheck.isPresent()) {
            return detailCheck.get();
        }

        request.setAttribute("title", 
                new StringBuilder()
                .append(postTypeService.getFrontEndPostTitle(post))
                .append(" - ")
                .append(postTypeConfig.getName())
                .toString());
        
        request.setAttribute("post", post);
        
        // 评论
        IPage<Comment> page = commentService.getPostCommentPage(
                commentPage, postTypeConfig.getCommentPageSize(), 
                post.getPostId());
        request.setAttribute("commentPaginationPrefix", post.getLink() + "/");
        request.setAttribute("commentPage", page);
        if (commentPage != null && commentPage != 1L && page.getRecords().size() == 0) {
            // 页数不为首页且评论数为 0，一般是属于本来有那么多页但是最终删除了
            String uri = request.getRequestURI();
            return "redirect:" + request.getRequestURI().substring(0, uri.lastIndexOf('/'));
        }
        

        String targetTemplet = postTypeComponent.getTemplatePath(post.getType(), "detail");
        
        if (!StringUtils.isEmpty(post.getTemplate())) {
            targetTemplet = themeService
                    .getCurrentTheme()
                    .getTemplateString("template", post.getTemplate());
        }
        
        return targetTemplet;
    }
    
    /**
     * 校验密码
     * @param postPageName
     * @param postId
     * @param password
     * @return
     */
    @PostMapping({
        "/p/{postId}/password",
        "/n/{postPageName}/password"})
    @ResponseBody
    public R<?> checkPassword(
            @PathVariable(value="postPageName", required=false) String postPageName,
            @PathVariable(value="postId", required=false) Long postId,
            String password) {
        boolean postIdGiven = Objects.nonNull(postId);
        boolean postPageNameGiven = StringUtils.isNoneBlank(postPageName);
        if (!postIdGiven && !postPageNameGiven) {
            throw new RException(HttpStatus.BAD_REQUEST, "非法请求");
        }
        
        Boolean isLogin = isLogin();
        Post post = postService.getOne(new LambdaQueryWrapper<Post>()
                .eq(!isLogin, Post::getPostStatus, PostStatusEnum.RELEASED)
                .eq(Post::getIsDeleted, false)
                .eq(postIdGiven, Post::getPostId, postId)
                .eq(postPageNameGiven, Post::getPostPageName, postPageName));
        if (post == null) {
            throw new RException(HttpStatus.BAD_REQUEST, "非法操作");
        } 
        if (isLogin || StringUtils.isBlank(post.getPassword())) { //
            return R.ok();
        }

        if (!postTypeComponent.checkModeEnable(post.getType())) {
            throw new RException(HttpStatus.BAD_REQUEST, "非法操作");
        }
        
        if (Cryptos.sha3(post.getPassword(), 224).equals(password)) {
            // 有密码
            PostPassJar.addToSession(session, post, password);
            return R.ok();
        }
        throw new RException(HttpStatus.FORBIDDEN, "密码输入错误");
    }
    
    
    @GetMapping({
        "/p/{postId}/lock",
        "/n/{postPageName}/lock"})
    public String lock(
            @PathVariable(value="postPageName", required=false) String postPageName,
            @PathVariable(value="postId", required=false) Long postId,
            String password) {
        
        Boolean isLogin = isLogin();
        Post post = postService.getByIdOrName(postId, postPageName, isLogin);
        if (post == null) {
            throw new PageNotFoundException();
        }
        PostPassJar.removeFromSession(session, post);
        return "redirect:" + post.getLink();
    }
    
    
    /**
     * 评论页元素
     */
    @GetMapping({
        "/{postName}.html/comments/{page}",
        "/p/{postId}/comments/{page}",
        "/n/{postName}/comments/{page}"
        })
    public String commentPage(
            @PathVariable(value="postId", required=false) Long postId,
            @PathVariable(value="postName", required=false) String postName,
            @PathVariable(value="page") Long commentPage) {


        Boolean isLogin = isLogin();
        
        Post post = postService.getOne(
                new LambdaQueryWrapper<Post>()
                .eq(!isLogin, Post::getPostStatus, PostStatusEnum.RELEASED)
                .eq(Post::getIsDeleted, false)
                // 是哪种页面，如何定位
                .and(!Objects.isNull(postId), i -> i.eq(Post::getPostId, postId))
                .and(!StringUtils.isEmpty(postName), i -> i.eq(Post::getPostPageName, postName))
                .last("LIMIT 1")
                );

        if (post == null) {
            // 如果 POST 不存在
            throw new PageNotFoundException();
        }
        
        IPostTypeConfig<?> postTypeConfig = postTypeComponent.getPostTypeConfig(post.getType());
        
        if (!postTypeConfig.getEnabled()) {
            throw new PageNotFoundException();
        }
        
        String targetTemplet = postTypeComponent.getTemplatePath(post.getType(), "comment-only");
        
        // 文章加密状态校验、清除
        if (!PostPassJar.validFromSession(session, post)) {
            return targetTemplet;
        }
        
        // 评论
        IPage<Comment> page = commentService.getPostCommentPage(
            commentPage, postTypeConfig.getCommentPageSize(), 
            post.getPostId());
        response.addHeader("Post-Id", post.getPostId().toString());
        request.setAttribute("post", post);
        request.setAttribute("enableComment", post.getEnableComment());
        request.setAttribute("commentPaginationPrefix", post.getLink() + "/");
        request.setAttribute("commentPage", page);
        if (commentPage != null && commentPage != 1L && page.getRecords().size() == 0) {
            // 页数不为首页且评论数为 0，一般是属于本来有那么多页但是最终删除了
            String uri = request.getRequestURI();
            return "redirect:" + request.getRequestURI().substring(0, uri.lastIndexOf('/'));
        }
        
        return targetTemplet;
    }
    
    /**
     * 评论 
     */
    @PostMapping({
        "/{postPageName}.html/reply",
        "/p/{postId}/reply",
        "/n/{postPageName}/reply"
        })
    @ResponseBody
    public R<?> reply(@Validated Comment comment, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            throw new RException(HttpStatus.BAD_REQUEST, bindingResult.getAllErrors().get(0).getDefaultMessage())
            .setLogRequest(true);
        }
        if (commentService.saveOrUpdate(comment)) {
            mailService.sendAdminNotification(comment);
            mailService.addOrUpdateSendQueue(comment);
            
            comment = commentService.getById(comment.getId())
                .setIsDeleted(null)
                .setIp(null)
                .setAgent(null)
                .setEditable(true);
            
            SessionUtils.addCommentId(request, comment.getId());
            
            if (commentConfig.isMarkdownOn() && comment.isMarkdown()) {
                comment.setContent(MarkDownUtils.commentToHtml(comment.getContent()));
            }
            return R.ok(comment);
        }
        throw RException.internalServerError();
    }
    

    /**
     * 假评论
     */
    @PostMapping({
        "/{postPageName}.html/honeyReply",
        "/p/{postId}/honeyReply",
        "/n/{postPageName}/honeyReply"
        })
    @ResponseBody
    public R<?> honeyReply(
            @Validated Comment comment, BindingResult bindingResult) {
        
        String ip = ipService.getClientIp(request);
        Boolean isLocalhost = IPUtils.isLocalhost(ip);
        
        // 添加入 ip 黑名单
        if (!isLocalhost) {
            commentConfig.addBlockIp(ip).saveOrUpdate();
        }
        
        if (bindingResult.hasErrors()) {
            //log.info("触发测试评论蜜罐, ip: {}, 但表单验证不通过", IPUtils.getClientIP(request));
            throw new RException(HttpStatus.BAD_REQUEST, bindingResult.getAllErrors().get(0).getDefaultMessage())
            .setLogRequest(false);
        }
        
        log.debug("触发测试评论蜜罐, ip: {}, {}", ip, comment.toString());
        return R.ok();
    }

    @GetMapping("/comments/go/{commentId}")
    public String commentsGo(@PathVariable("commentId") Long id) {
        return "redirect:" + commentService.getJumpToUrl(id);
    }
    
    @GetMapping("/posts/gotoPage/{postId}")
    public String postsGo(@PathVariable("postId") Long id) {
        return "redirect:" + postService.getJumpToPagesUrl(id);
    }
    
}
