package com.mzh.library.controller;

import com.mzh.library.dao.impl.JdbcBorrowRecordDao;
import com.mzh.library.entity.AuthenticatedUser;
import com.mzh.library.entity.BorrowRecord;
import com.mzh.library.entity.BorrowRecordSearchCriteria;
import com.mzh.library.entity.BorrowRecordStatus;
import com.mzh.library.service.ServiceResult;
import com.mzh.library.service.impl.BorrowingServiceImpl;
import com.mzh.library.util.SessionAttributes;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class BorrowingManagementServlet extends HttpServlet {
    private static final String MANAGE_JSP = "/WEB-INF/jsp/borrowing/manage.jsp";
    private static final String FORM_JSP = "/WEB-INF/jsp/borrowing/form.jsp";
    private static final String UNAUTHORIZED_JSP = "/WEB-INF/jsp/auth/unauthorized.jsp";
    private static final String FLASH_SUCCESS = "flashSuccess";
    private static final String FLASH_ERROR = "flashError";

    private BorrowingServiceImpl borrowingService;

    @Override
    public void init() {
        this.borrowingService = new BorrowingServiceImpl(new JdbcBorrowRecordDao());
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getServletPath();
        if ("/borrowing/new".equals(path)) {
            renderForm(request, response, Collections.emptyMap(), Collections.emptyMap(), null);
            return;
        }
        if (!"/borrowing".equals(path)) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        showManagementList(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getServletPath();
        if ("/borrowing/create".equals(path)) {
            createBorrowRecord(request, response);
            return;
        }
        if ("/borrowing/return".equals(path)) {
            returnBook(request, response);
            return;
        }
        if ("/borrowing/renew".equals(path)) {
            renewLoan(request, response);
            return;
        }

        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }

    private void showManagementList(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        BorrowRecordSearchCriteria criteria = searchCriteria(request);
        request.setAttribute("criteria", criteria);
        request.setAttribute("statuses", BorrowRecordStatus.values());
        request.setAttribute("overdueStatus", BorrowRecordSearchCriteria.OVERDUE_STATUS);
        request.setAttribute("maxRenewals", borrowingService.getMaxRenewals());
        applyFlash(request);

        ServiceResult<List<BorrowRecord>> result = borrowingService.searchRecords(currentUser(request), criteria);
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }

        request.setAttribute("borrowRecords", result.isSuccessful() ? result.getData() : Collections.emptyList());
        if (!result.isSuccessful()) {
            request.setAttribute("errorMessage", result.getMessage());
            request.setAttribute("errors", result.getErrors());
        }

        request.getRequestDispatcher(MANAGE_JSP).forward(request, response);
    }

    private void createBorrowRecord(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        Map<String, String> values = formValues(request);
        ServiceResult<Long> result = borrowingService.borrowBook(currentUser(request),
            values.get("readerIdentifier"), values.get("bookIdentifier"));
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        if (!result.isSuccessful()) {
            renderForm(request, response, values, result.getErrors(), result.getMessage());
            return;
        }

        flashSuccess(request, result.getMessage());
        response.sendRedirect(request.getContextPath() + "/borrowing");
    }

    private void returnBook(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Void> result = id <= 0
            ? ServiceResult.failure("请选择有效的借阅记录。")
            : borrowingService.returnBook(currentUser(request), id);
        redirectWithResult(request, response, result);
    }

    private void renewLoan(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Void> result = id <= 0
            ? ServiceResult.failure("请选择有效的借阅记录。")
            : borrowingService.renewLoan(currentUser(request), id);
        redirectWithResult(request, response, result);
    }

    private void redirectWithResult(HttpServletRequest request, HttpServletResponse response, ServiceResult<?> result)
        throws IOException, ServletException {
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        if (result.isSuccessful()) {
            flashSuccess(request, result.getMessage());
        } else {
            flashError(request, messageFor(result));
        }
        response.sendRedirect(request.getContextPath() + "/borrowing");
    }

    private void renderForm(HttpServletRequest request, HttpServletResponse response, Map<String, String> formValues,
                            Map<String, String> errors, String errorMessage)
        throws ServletException, IOException {
        request.setAttribute("formValues", formValues);
        request.setAttribute("errors", errors);
        if (errorMessage != null && !errorMessage.isEmpty()) {
            request.setAttribute("errorMessage", errorMessage);
        }
        request.getRequestDispatcher(FORM_JSP).forward(request, response);
    }

    private BorrowRecordSearchCriteria searchCriteria(HttpServletRequest request) {
        return new BorrowRecordSearchCriteria(
            request.getParameter("readerIdentifier"),
            request.getParameter("bookIdentifier"),
            request.getParameter("status")
        );
    }

    private Map<String, String> formValues(HttpServletRequest request) {
        Map<String, String> values = new LinkedHashMap<>();
        values.put("readerIdentifier", trim(request.getParameter("readerIdentifier")));
        values.put("bookIdentifier", trim(request.getParameter("bookIdentifier")));
        return values;
    }

    private long requiredLong(String value, long fallback) {
        try {
            long parsed = Long.parseLong(trim(value));
            return parsed > 0 ? parsed : fallback;
        } catch (NumberFormatException ex) {
            return fallback;
        }
    }

    private String messageFor(ServiceResult<?> result) {
        if (result.getMessage() != null && !result.getMessage().isEmpty()) {
            return result.getMessage();
        }
        if (result.hasErrors()) {
            return result.getErrors().values().iterator().next();
        }
        return "借阅操作失败。";
    }

    private boolean isPermissionDenied(ServiceResult<?> result) {
        return !result.isSuccessful()
            && "您无权管理借阅。".equals(result.getMessage());
    }

    private void forwardDenied(HttpServletRequest request, HttpServletResponse response, String message)
        throws ServletException, IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        request.setAttribute("errorMessage", message);
        request.getRequestDispatcher(UNAUTHORIZED_JSP).forward(request, response);
    }

    private AuthenticatedUser currentUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        Object value = session == null ? null : session.getAttribute(SessionAttributes.AUTHENTICATED_USER);
        return value instanceof AuthenticatedUser ? (AuthenticatedUser) value : null;
    }

    private void applyFlash(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return;
        }
        moveFlash(session, request, FLASH_SUCCESS, "successMessage");
        moveFlash(session, request, FLASH_ERROR, "errorMessage");
    }

    private void moveFlash(HttpSession session, HttpServletRequest request, String sessionKey, String requestKey) {
        Object value = session.getAttribute(sessionKey);
        if (value != null) {
            request.setAttribute(requestKey, value);
            session.removeAttribute(sessionKey);
        }
    }

    private void flashSuccess(HttpServletRequest request, String message) {
        request.getSession().setAttribute(FLASH_SUCCESS, message);
    }

    private void flashError(HttpServletRequest request, String message) {
        request.getSession().setAttribute(FLASH_ERROR, message);
    }

    private String trim(String value) {
        return value == null ? "" : value.trim();
    }
}
