package com.mzh.library.controller;

import com.mzh.library.dao.impl.JdbcBookDao;
import com.mzh.library.entity.AuthenticatedUser;
import com.mzh.library.entity.Book;
import com.mzh.library.entity.BookCategory;
import com.mzh.library.entity.BookSearchCriteria;
import com.mzh.library.entity.BookStatus;
import com.mzh.library.service.BookService;
import com.mzh.library.service.ServiceResult;
import com.mzh.library.service.impl.BookServiceImpl;
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 java.util.Optional;

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 BookManagementServlet extends HttpServlet {
    private static final String MANAGE_JSP = "/WEB-INF/jsp/books/manage.jsp";
    private static final String FORM_JSP = "/WEB-INF/jsp/books/form.jsp";
    private static final String CATEGORY_MANAGE_JSP = "/WEB-INF/jsp/books/categories.jsp";
    private static final String CATEGORY_FORM_JSP = "/WEB-INF/jsp/books/category-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 BookService bookService;

    @Override
    public void init() {
        this.bookService = new BookServiceImpl(new JdbcBookDao());
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getServletPath();
        if ("/books/new".equals(path)) {
            renderForm(request, response, "创建图书", "/books", new Book(), Collections.emptyMap(),
                Collections.emptyMap(), null);
            return;
        }
        if ("/books/edit".equals(path)) {
            showEditForm(request, response);
            return;
        }
        if ("/book-categories".equals(path)) {
            showCategoryList(request, response);
            return;
        }
        if ("/book-categories/new".equals(path)) {
            renderCategoryForm(request, response, "创建分类", "/book-categories", new BookCategory(),
                Collections.emptyMap(), Collections.emptyMap(), null);
            return;
        }
        if ("/book-categories/edit".equals(path)) {
            showEditCategoryForm(request, response);
            return;
        }
        if (!"/books".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 ("/books".equals(path)) {
            createBook(request, response);
            return;
        }
        if ("/books/update".equals(path)) {
            updateBook(request, response);
            return;
        }
        if ("/books/delete".equals(path)) {
            deleteBook(request, response);
            return;
        }
        if ("/book-categories".equals(path)) {
            createCategory(request, response);
            return;
        }
        if ("/book-categories/update".equals(path)) {
            updateCategory(request, response);
            return;
        }
        if ("/book-categories/delete".equals(path)) {
            deleteCategory(request, response);
            return;
        }

        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }

    private void showManagementList(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        BookSearchCriteria criteria = searchCriteria(request);
        request.setAttribute("criteria", criteria);
        applyFlash(request);

        ServiceResult<List<BookCategory>> categoryResult = bookService.listCategories();
        request.setAttribute("categories", categoryResult.isSuccessful()
            ? categoryResult.getData()
            : Collections.emptyList());
        if (!categoryResult.isSuccessful()) {
            request.setAttribute("errorMessage", categoryResult.getMessage());
        }

        ServiceResult<List<Book>> searchResult = bookService.searchBooks(criteria);
        request.setAttribute("books", searchResult.isSuccessful()
            ? searchResult.getData()
            : Collections.emptyList());
        if (!searchResult.isSuccessful()) {
            request.setAttribute("errorMessage", searchResult.getMessage());
            request.setAttribute("errors", searchResult.getErrors());
        }

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

    private void showEditForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Optional<Book>> result = bookService.findBook(id);
        if (!result.isSuccessful() || !result.getData().isPresent()) {
            flashError(request, result.isSuccessful() ? "未找到图书。" : result.getMessage());
            response.sendRedirect(request.getContextPath() + "/books");
            return;
        }

        renderForm(request, response, "编辑图书", "/books/update", result.getData().get(),
            Collections.emptyMap(), Collections.emptyMap(), null);
    }

    private void showCategoryList(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        applyFlash(request);

        ServiceResult<List<BookCategory>> result = bookService.listCategories();
        request.setAttribute("categories", result.isSuccessful() ? result.getData() : Collections.emptyList());
        if (!result.isSuccessful()) {
            request.setAttribute("errorMessage", result.getMessage());
        }
        request.getRequestDispatcher(CATEGORY_MANAGE_JSP).forward(request, response);
    }

    private void showEditCategoryForm(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Optional<BookCategory>> result = bookService.findCategory(id);
        if (!result.isSuccessful() || !result.getData().isPresent()) {
            flashError(request, result.isSuccessful() ? "未找到分类。" : result.getMessage());
            response.sendRedirect(request.getContextPath() + "/book-categories");
            return;
        }

        renderCategoryForm(request, response, "编辑分类", "/book-categories/update", result.getData().get(),
            Collections.emptyMap(), Collections.emptyMap(), null);
    }

    private void createBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookForm form = readBookForm(request, false);
        if (!form.getErrors().isEmpty()) {
            renderForm(request, response, "创建图书", "/books", form.getBook(), form.getValues(),
                form.getErrors(), "请修正高亮的图书字段。");
            return;
        }

        ServiceResult<Long> result = bookService.createBook(currentUser(request), form.getBook());
        if (!result.isSuccessful()) {
            handleFormFailure(request, response, "创建图书", "/books", form, result);
            return;
        }

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

    private void updateBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookForm form = readBookForm(request, true);
        if (!form.getErrors().isEmpty()) {
            renderForm(request, response, "编辑图书", "/books/update", form.getBook(), form.getValues(),
                form.getErrors(), "请修正高亮的图书字段。");
            return;
        }

        ServiceResult<Void> result = bookService.updateBook(currentUser(request), form.getBook());
        if (!result.isSuccessful()) {
            handleFormFailure(request, response, "编辑图书", "/books/update", form, result);
            return;
        }

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

    private void deleteBook(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Void> result = bookService.deleteBook(currentUser(request), id);
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        if (result.isSuccessful()) {
            flashSuccess(request, result.getMessage());
        } else {
            flashError(request, result.getMessage());
        }
        response.sendRedirect(request.getContextPath() + "/books");
    }

    private void createCategory(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        CategoryForm form = readCategoryForm(request, false);
        if (!form.getErrors().isEmpty()) {
            renderCategoryForm(request, response, "创建分类", "/book-categories", form.getCategory(),
                form.getValues(), form.getErrors(), "请修正高亮的分类字段。");
            return;
        }

        ServiceResult<Long> result = bookService.createCategory(currentUser(request), form.getCategory());
        if (!result.isSuccessful()) {
            handleCategoryFormFailure(request, response, "创建分类", "/book-categories", form, result);
            return;
        }

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

    private void updateCategory(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        CategoryForm form = readCategoryForm(request, true);
        if (!form.getErrors().isEmpty()) {
            renderCategoryForm(request, response, "编辑分类", "/book-categories/update", form.getCategory(),
                form.getValues(), form.getErrors(), "请修正高亮的分类字段。");
            return;
        }

        ServiceResult<Void> result = bookService.updateCategory(currentUser(request), form.getCategory());
        if (!result.isSuccessful()) {
            handleCategoryFormFailure(request, response, "编辑分类", "/book-categories/update", form, result);
            return;
        }

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

    private void deleteCategory(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
        long id = requiredLong(request.getParameter("id"), -1L);
        ServiceResult<Void> result = bookService.deleteCategory(currentUser(request), id);
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        if (result.isSuccessful()) {
            flashSuccess(request, result.getMessage());
        } else {
            flashError(request, result.getMessage());
        }
        response.sendRedirect(request.getContextPath() + "/book-categories");
    }

    private void handleFormFailure(HttpServletRequest request, HttpServletResponse response, String title,
                                   String action, BookForm form, ServiceResult<?> result)
        throws ServletException, IOException {
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        renderForm(request, response, title, action, form.getBook(), form.getValues(), result.getErrors(),
            result.getMessage());
    }

    private void handleCategoryFormFailure(HttpServletRequest request, HttpServletResponse response, String title,
                                           String action, CategoryForm form, ServiceResult<?> result)
        throws ServletException, IOException {
        if (isPermissionDenied(result)) {
            forwardDenied(request, response, result.getMessage());
            return;
        }
        renderCategoryForm(request, response, title, action, form.getCategory(), form.getValues(),
            result.getErrors(), result.getMessage());
    }

    private void renderForm(HttpServletRequest request, HttpServletResponse response, String title, String action,
                            Book book, Map<String, String> formValues, Map<String, String> errors, String errorMessage)
        throws ServletException, IOException {
        ServiceResult<List<BookCategory>> categoryResult = bookService.listCategories();
        request.setAttribute("categories", categoryResult.isSuccessful()
            ? categoryResult.getData()
            : Collections.emptyList());
        request.setAttribute("statuses", BookStatus.values());
        request.setAttribute("formTitle", title);
        request.setAttribute("formAction", action);
        request.setAttribute("book", book);
        request.setAttribute("formValues", formValues);
        request.setAttribute("errors", errors);
        if (errorMessage != null && !errorMessage.isEmpty()) {
            request.setAttribute("errorMessage", errorMessage);
        } else if (!categoryResult.isSuccessful()) {
            request.setAttribute("errorMessage", categoryResult.getMessage());
        }
        request.getRequestDispatcher(FORM_JSP).forward(request, response);
    }

    private void renderCategoryForm(HttpServletRequest request, HttpServletResponse response, String title,
                                    String action, BookCategory category, Map<String, String> formValues,
                                    Map<String, String> errors, String errorMessage)
        throws ServletException, IOException {
        request.setAttribute("formTitle", title);
        request.setAttribute("formAction", action);
        request.setAttribute("category", category);
        request.setAttribute("formValues", formValues);
        request.setAttribute("errors", errors);
        if (errorMessage != null && !errorMessage.isEmpty()) {
            request.setAttribute("errorMessage", errorMessage);
        }
        request.getRequestDispatcher(CATEGORY_FORM_JSP).forward(request, response);
    }

    private BookForm readBookForm(HttpServletRequest request, boolean requireId) {
        Map<String, String> values = formValues(request);
        Map<String, String> errors = new LinkedHashMap<>();
        Book book = new Book();

        if (requireId) {
            book.setId(parseLong(values.get("id"), "id", "请选择有效的图书。", errors));
        }
        book.setIdentifier(values.get("identifier"));
        book.setTitle(values.get("title"));
        book.setAuthor(values.get("author"));
        book.setCategoryId(parseLong(values.get("categoryId"), "categoryId", "请选择分类。", errors));
        book.setTotalCopies(parseInt(values.get("totalCopies"), "totalCopies", "请输入有效的馆藏总数。", errors));
        book.setAvailableCopies(parseInt(values.get("availableCopies"), "availableCopies",
            "请输入有效的可借数量。", errors));

        try {
            book.setStatus(BookStatus.fromCode(values.get("status")));
        } catch (IllegalArgumentException ex) {
            errors.put("status", "请选择状态。");
        }

        return new BookForm(book, values, errors);
    }

    private Map<String, String> formValues(HttpServletRequest request) {
        Map<String, String> values = new LinkedHashMap<>();
        values.put("id", trim(request.getParameter("id")));
        values.put("identifier", trim(request.getParameter("identifier")));
        values.put("title", trim(request.getParameter("title")));
        values.put("author", trim(request.getParameter("author")));
        values.put("categoryId", trim(request.getParameter("categoryId")));
        values.put("totalCopies", trim(request.getParameter("totalCopies")));
        values.put("availableCopies", trim(request.getParameter("availableCopies")));
        values.put("status", trim(request.getParameter("status")));
        return values;
    }

    private CategoryForm readCategoryForm(HttpServletRequest request, boolean requireId) {
        Map<String, String> values = categoryFormValues(request);
        Map<String, String> errors = new LinkedHashMap<>();
        BookCategory category = new BookCategory();

        if (requireId) {
            category.setId(parseLong(values.get("id"), "id", "请选择有效的分类。", errors));
        }
        category.setName(values.get("name"));
        category.setDescription(values.get("description"));
        return new CategoryForm(category, values, errors);
    }

    private Map<String, String> categoryFormValues(HttpServletRequest request) {
        Map<String, String> values = new LinkedHashMap<>();
        values.put("id", trim(request.getParameter("id")));
        values.put("name", trim(request.getParameter("name")));
        values.put("description", trim(request.getParameter("description")));
        return values;
    }

    private BookSearchCriteria searchCriteria(HttpServletRequest request) {
        return new BookSearchCriteria(
            request.getParameter("identifier"),
            request.getParameter("title"),
            request.getParameter("author"),
            optionalLong(request.getParameter("categoryId"))
        );
    }

    private Long optionalLong(String value) {
        String trimmed = trim(value);
        if (trimmed.isEmpty()) {
            return null;
        }
        try {
            return Long.valueOf(trimmed);
        } catch (NumberFormatException ex) {
            return -1L;
        }
    }

    private long parseLong(String value, String field, String message, Map<String, String> errors) {
        String trimmed = trim(value);
        if (trimmed.isEmpty()) {
            errors.put(field, message);
            return 0L;
        }
        try {
            long parsed = Long.parseLong(trimmed);
            if (parsed <= 0) {
                errors.put(field, message);
            }
            return parsed;
        } catch (NumberFormatException ex) {
            errors.put(field, message);
            return 0L;
        }
    }

    private int parseInt(String value, String field, String message, Map<String, String> errors) {
        String trimmed = trim(value);
        if (trimmed.isEmpty()) {
            errors.put(field, message);
            return -1;
        }
        try {
            return Integer.parseInt(trimmed);
        } catch (NumberFormatException ex) {
            errors.put(field, message);
            return -1;
        }
    }

    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 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();
    }

    private static final class BookForm {
        private final Book book;
        private final Map<String, String> values;
        private final Map<String, String> errors;

        private BookForm(Book book, Map<String, String> values, Map<String, String> errors) {
            this.book = book;
            this.values = values;
            this.errors = errors;
        }

        private Book getBook() {
            return book;
        }

        private Map<String, String> getValues() {
            return values;
        }

        private Map<String, String> getErrors() {
            return errors;
        }
    }

    private static final class CategoryForm {
        private final BookCategory category;
        private final Map<String, String> values;
        private final Map<String, String> errors;

        private CategoryForm(BookCategory category, Map<String, String> values, Map<String, String> errors) {
            this.category = category;
            this.values = values;
            this.errors = errors;
        }

        private BookCategory getCategory() {
            return category;
        }

        private Map<String, String> getValues() {
            return values;
        }

        private Map<String, String> getErrors() {
            return errors;
        }
    }
}
