package cn.ppdxzz.controller;

import cn.ppdxzz.domain.Visitor;
import cn.ppdxzz.service.VisitorService;
import com.github.pagehelper.PageInfo;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Description: 该类是访客相关功能的控制器，处理与访客相关的各种请求，例如来访登记、注销、查询、导出等操作。
 *
 * @Date: 2020/2/18 16:26
 * @Author: PeiChen
 */
// 声明这是一个Spring框架中的控制器，表明该类可以处理HTTP请求并返回相应的视图或数据
@Controller
// 映射访问路径为/visitor，意味着所有以/visitor开头的请求都会被该控制器处理
@RequestMapping("/visitor")
public class VisitorController {

    // 自动注入VisitorService服务类，通过依赖注入的方式获取VisitorService的实例，以便调用其中的业务逻辑方法
    private VisitorService visitorService;

    // 通过Autowired注解自动注入VisitorService，这里是设置属性注入的方法，Spring会自动找到VisitorService的实现类并注入进来
    @Autowired
    public void setVisitorService(VisitorService visitorService) {
        this.visitorService = visitorService;
    }

    // 访问路径为/visitor/login的请求处理方法，该方法返回注册页面的视图名称，通常Spring会根据这个名称去查找对应的视图文件进行渲染展示
    @RequestMapping("/login")
    public String register() {
        return "regist_visitor";
    } // 返回regist_visitor视图，此视图应该是用于展示访客注册相关页面内容的，具体的页面解析和渲染由Spring的视图解析器根据配置来完成

    /**
     * 来访登记实现（C操作）
     * 该方法用于处理访客的来访登记功能，接收一个Visitor对象作为参数，将访客信息保存到数据库中，并根据操作结果返回相应的视图。
     *
     * @param visitor 访客对象，包含了访客的各种信息，如姓名、学号、电话、访问地点等，这些信息将被保存到数据库中。
     * @return ModelAndView 视图模型对象，用于携带数据并指定要返回的视图名称，以便Spring进行视图渲染和数据展示。
     * @throws Exception 异常，方法可能会抛出各种异常，比如数据库操作异常等，由调用者进行处理。
     */
    @RequestMapping("/addLogin")
    public ModelAndView addVisitor(Visitor visitor) throws Exception {
        ModelAndView mv = new ModelAndView();
        // 参数校验，如果访客信息不完整（访客对象为空或者关键属性如姓名、学号、电话、访问地点等为空），则返回错误信息页面
        if (visitor == null || visitor.getName() == null || visitor.getSno() == null || visitor.getPhone() == null || visitor.getPlace() == null) {
            mv.addObject("error_msg", "来访登记失败，请重新登记！");
            mv.setViewName("regist_visitor");
            return mv;
        }
        // 生成访客ID，使用UUID（通用唯一识别码）来生成一个唯一的标识符作为访客的ID，如果原访客对象中的ID为空或者是空白字符串，则进行生成操作
        if (visitor.getId() == null || "".trim().equals(visitor.getId())) {
            String uuid = UUID.randomUUID().toString().replace("-", "");
            visitor.setId(uuid);
        }
        // 设置来访时间为当前系统时间，通过SimpleDateFormat格式化当前的日期和时间（格式为"yyyy-MM-dd HH:mm:ss"），并设置到访客对象的来访时间属性中
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        visitor.setBegin_date(date);//设置来访时间为提交来访登记时间
        // 先设置离开时间为空串，后续注销时再修改为注销时系统时间，初始化访客的离开时间为空字符串，表示访客尚未离开
        if (visitor.getEnd_date() == null || "".trim().equals(visitor.getEnd_date())) {
            visitor.setEnd_date("");
        }
        // 添加访客信息到数据库，调用VisitorService中的add方法将完整的访客信息保存到数据库中
        visitorService.add(visitor);
        // 添加访客ID到模型，用于页面显示，将生成的访客ID添加到ModelAndView对象中，以便在后续的视图中可以获取并展示给用户
        mv.addObject("id", visitor.getId());
        mv.setViewName("visitor-success"); // 设置成功页面视图，指定成功登记后要展示的视图名称，通常会显示登记成功的相关提示信息
        return mv;
    }

    /**
     * 访客记录注销
     * 该方法用于处理访客记录的注销功能，接收一个HttpServletRequest对象，从请求中获取访客ID，然后调用业务逻辑进行注销操作，并根据操作结果返回相应的视图。
     *
     * @param request HttpServletRequest对象，用于获取请求中的参数，如访客ID等信息。
     * @return ModelAndView 视图模型对象，携带注销操作的相关提示信息并指定要返回的视图名称。
     * @throws Exception 异常，可能会抛出如数据库操作异常等各种异常情况，由调用者进行处理。
     */
    @RequestMapping("/login_out")
    public ModelAndView logout(HttpServletRequest request) throws Exception {
        ModelAndView mv = new ModelAndView();
        // 获取访客ID，从HttpServletRequest对象中获取名为"id"的参数值，该参数值代表要注销的访客的唯一标识
        String id = request.getParameter("id");
        // 如果ID为空，则返回错误信息页面，提示系统繁忙，让用户稍后再试
        if (id == null || "".trim().equals(id)) {
            mv.addObject("logout_msg", "系统繁忙，请稍后再试！");
            mv.setViewName("regist_visitor");
        }
        // 获取当前系统时间，通过SimpleDateFormat格式化当前的日期和时间（格式为"yyyy-MM-dd HH:mm:ss"），用于更新访客记录的注销时间
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        // 注销访客记录，调用VisitorService中的logout方法，传入访客ID和当前时间，完成在数据库中对访客记录的注销操作（可能是更新离开时间等相关操作）
        visitorService.logout(id, date);
        mv.addObject("logout_msg", "注销成功");
        mv.setViewName("regist_visitor");
        return mv;
    }

    /**
     * 管理员手动注销来访状态
     * 该方法用于管理员手动注销来访状态，接收HttpServletRequest和HttpServletResponse对象，从请求中获取访客ID，进行注销操作，并向响应中写入操作结果（true或false）。
     *
     * @param request HttpServletRequest对象，用于获取请求中的访客ID等参数信息。
     * @param response HttpServletResponse对象，用于设置响应的字符编码、向客户端发送操作结果等信息。
     * @throws Exception 异常，可能出现如数据库操作异常、编码设置异常等情况，由调用者进行处理。
     */
    @RequestMapping("/updateStatus")
    public void updateStatus(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("utf-8");
        // 设置请求的字符编码为UTF-8，确保能正确接收和处理包含中文等特殊字符的请求参数
        response.setCharacterEncoding("utf-8");
        // 设置响应的字符编码为UTF-8，保证返回给客户端的数据能正确显示，避免出现乱码问题
        PrintWriter writer = response.getWriter();
        // 获取响应的输出流的PrintWriter对象，用于向客户端发送文本数据（这里用于写入操作结果）
        // 获取访客ID，从HttpServletRequest对象中获取名为"id"的参数值，该参数值是要注销的访客记录的标识
        String id = request.getParameter("id");
        // 如果ID为空，则返回错误信息，向客户端写入"false"表示注销操作失败
        if (id == null || "".trim().equals(id)) {
            writer.write("false");
            return;
        }
        // 获取当前系统时间，通过SimpleDateFormat格式化当前的日期和时间（格式为"yyyy-MM-dd HH:mm:ss"），用于更新访客记录的注销时间
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        // 注销访客记录，调用VisitorService中的logout方法，传入访客ID和当前时间，在数据库中执行注销操作（比如更新离开时间等相关字段）
        visitorService.logout(id, date);
        writer.write("true");
        // 向客户端写入"true"表示注销操作成功，客户端可以根据接收到的这个结果进行相应的提示或后续处理
    }

    /**
     * 查询所有访客记录
     * 该方法用于查询所有访客记录，支持分页查询以及根据关键词搜索查询，接收当前页码、每页显示数量以及HttpServletRequest和HttpServletResponse对象，返回包含查询结果和分页信息的视图模型对象。
     *
     * @param page 当前页码，用于指定要获取第几页的数据，从请求参数中获取，默认值为1，如果请求中没有传递该参数则使用默认值。
     * @param size 每页显示数量，用于指定每页展示多少条访客记录，从请求参数中获取，默认值为5，如果请求中没有传递该参数则使用默认值。
     * @param request HttpServletRequest对象，用于获取请求中的参数，如关键词等信息，以及设置请求的字符编码。
     * @param response HttpServletResponse对象，用于设置响应的字符编码等操作。
     * @return ModelAndView 视图模型对象，携带查询到的访客记录列表以及分页信息，并指定要返回的视图名称，方便Spring进行视图渲染和数据展示。
     * @throws Exception 异常，可能出现数据库查询异常、编码设置异常等情况，由调用者进行处理。
     */
    @RequestMapping("/findAll")
    public ModelAndView findAll(@RequestParam(name = "page", required = true, defaultValue = "1") int page,
                                @RequestParam(name = "page", required = true, defaultValue = "5") int size,
                                HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 设置请求的字符编码为UTF-8，确保能正确处理包含中文等特殊字符的请求参数
        request.setCharacterEncoding("utf-8");
        // 设置响应的字符编码为UTF-8，保证返回给客户端的数据能正确显示，避免出现乱码情况
        response.setCharacterEncoding("utf-8");

        // 创建ModelAndView对象用于返回视图和数据，该对象可以携带数据并指定要展示的视图名称，方便Spring进行后续的视图渲染操作
        ModelAndView mv = new ModelAndView();
        List<Visitor> visitors = null;

        // 获取请求中的keyword参数，该参数用于根据关键词进行访客记录的搜索，如果没有该参数则查询所有记录
        String keyword = request.getParameter("keyword");

        // 如果keyword为空或为空字符串，或者长度为0（进一步确保为空的情况），则查询所有访客记录，调用VisitorService的findAll方法获取所有访客信息（分页查询）
        if (keyword == null || "".trim().equals(keyword) || keyword.length() == 0) {
            visitors = visitorService.findAll(page, size);
        } else {
            // 否则根据keyword进行搜索，调用VisitorService的search方法，传入当前页码、每页显示数量以及关键词，进行带关键词的分页搜索操作
            visitors = visitorService.search(page, size, keyword);
        }

        // 创建分页信息对象，通过PageInfo对查询到的访客列表进行分页相关信息的封装，如总页数、总记录数等信息都会被自动计算并封装在该对象中
        PageInfo pageInfo = new PageInfo(visitors);
        // 将分页信息添加到ModelAndView对象中，这样在视图中就可以获取并展示分页相关的信息，如页码导航、总记录数等内容
        mv.addObject("pageInfo", pageInfo);
        // 设置视图名称为"visitor-list"，指定要返回的视图名称，Spring会根据配置找到对应的视图文件进行渲染展示，该视图可能用于展示访客记录列表信息
        mv.setViewName("visitor-list");
        // 返回ModelAndView对象，将携带数据的视图模型返回给Spring框架，由框架进行后续的视图渲染和响应返回操作
        return mv;
    }

    /**
     * 访客日志
     * 该方法用于获取访客日志信息，支持分页查询，返回包含访客日志列表和分页信息的视图模型对象。
     *
     * @return ModelAndView 视图模型对象，包含访客日志列表和分页信息，用于指定要返回的视图以及携带相应的数据供视图展示。
     * @throws Exception 抛出可能的异常，如数据库查询异常等情况，由调用者进行处理。
     */
    @RequestMapping("/log")
    public ModelAndView log(@RequestParam(name = "page", required = true, defaultValue = "1") int page,
                            @RequestParam(name = "size", required = true, defaultValue = "10") int size) throws Exception {
        // 创建ModelAndView对象用于返回视图和数据，该对象可以携带数据并指定要展示的视图名称，方便Spring进行后续的视图渲染操作
        ModelAndView mv = new ModelAndView();
        // 获取访客日志列表，调用VisitorService的log方法，传入当前页码和每页显示数量，获取相应分页的访客日志信息
        List<Visitor> logs = visitorService.log(page, size);
        // 创建分页信息对象，通过PageInfo对获取到的访客日志列表进行分页相关信息的封装，方便在视图中展示分页相关的内容
        PageInfo pageInfo = new PageInfo(logs);
        // 将分页信息添加到ModelAndView对象中，使得视图可以获取并展示如总页数、当前页码等分页相关信息
        mv.addObject("pageInfo", pageInfo);
        // 设置视图名称为"visitor-log"，指定要返回的视图名称，Spring会根据配置找到对应的视图文件进行渲染展示，该视图用于展示访客日志相关信息
        mv.setViewName("visitor-log");
        // 返回ModelAndView对象，将携带访客日志数据和分页信息的视图模型返回给Spring框架，由框架进行后续的视图渲染和响应返回操作
        return mv;
    }

    /**
     * 导出访客信息
     * 该方法用于将访客信息导出为Excel文件格式，接收一个HttpServletResponse对象，用于设置响应头信息以及将Excel文件内容通过响应流发送给客户端。
     *
     * @param response HttpServletResponse对象，用于发送Excel文件作为响应，设置响应的相关属性（如内容类型、文件头信息等）以及输出文件内容。
     * @throws Exception 抛出可能的异常，如文件读取异常、流操作异常等情况，由调用者进行处理。
     */
    @RequestMapping("/visitorInfo")
    public void export(HttpServletResponse response) throws Exception {
        // 获取Excel文件的输入流，调用VisitorService的getInputStream方法获取访客信息对应的Excel文件的输入流，以便后续将文件内容发送给客户端
        InputStream is = visitorService.getInputStream();

        // 设置响应的内容类型为Excel文件，指定响应的内容类型为"application/vnd.ms-excel"，告诉客户端返回的数据是Excel格式的文件，以便客户端进行正确的处理（如下载或打开操作）
        response.setContentType("application/vnd.ms-excel");
        // 设置响应头，指示浏览器以附件形式下载文件，并设置文件名为visitorInfo.xls，通过设置"contentDisposition"头信息，让浏览器以附件形式下载文件，并指定文件名，方便用户保存文件
        response.setHeader("contentDisposition", "attachment;filename=visitorInfo.xls");
        // 获取响应的输出流，用于将Excel文件的内容写入到响应中，发送给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        // 使用IOUtils工具类将输入流的内容复制到输出流，借助Apache Commons IO库提供的IOUtils工具类，方便地将Excel文件的输入流内容复制到响应的输出流中，实现文件的发送
        IOUtils.copy(is, outputStream);

    }
}