package com.mzh.library.service.impl;

import com.mzh.library.dao.BookDao;
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.Permission;
import com.mzh.library.exception.DaoException;
import com.mzh.library.service.BookService;
import com.mzh.library.service.PermissionPolicy;
import com.mzh.library.service.ServiceResult;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BookServiceImpl implements BookService {
    private static final Logger LOGGER = Logger.getLogger(BookServiceImpl.class.getName());
    private static final String UNAVAILABLE_MESSAGE =
        "图书服务暂时不可用，请稍后重试。";
    private static final String VALIDATION_MESSAGE = "请修正高亮的图书字段。";
    private static final String CATEGORY_VALIDATION_MESSAGE = "请修正高亮的分类字段。";
    private static final String DENIED_MESSAGE = "您无权管理图书。";

    private final BookDao bookDao;
    private final PermissionPolicy permissionPolicy;

    public BookServiceImpl(BookDao bookDao) {
        this(bookDao, new PermissionPolicy());
    }

    public BookServiceImpl(BookDao bookDao, PermissionPolicy permissionPolicy) {
        this.bookDao = bookDao;
        this.permissionPolicy = permissionPolicy;
    }

    @Override
    public ServiceResult<List<BookCategory>> listCategories() {
        try {
            return ServiceResult.success(bookDao.findAllCategories());
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to list book categories", ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Optional<BookCategory>> findCategory(long id) {
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的分类。");
        }

        try {
            return ServiceResult.success(bookDao.findCategoryById(id));
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to load book category id=" + id, ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Long> createCategory(AuthenticatedUser actor, BookCategory category) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(category);
        Map<String, String> errors = validate(category, false);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(CATEGORY_VALIDATION_MESSAGE, errors);
        }

        try {
            if (bookDao.findCategoryByName(category.getName()).isPresent()) {
                errors.put("name", "分类名称已被使用。");
                return ServiceResult.validationFailure(CATEGORY_VALIDATION_MESSAGE, errors);
            }

            long id = bookDao.createCategory(category);
            LOGGER.info("Created book category id=" + id + " actorId=" + actor.getId());
            return ServiceResult.success(id, "分类已创建。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to create book category actorId=" + actor.getId()
                + " name=" + safeCategoryName(category), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> updateCategory(AuthenticatedUser actor, BookCategory category) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(category);
        Map<String, String> errors = validate(category, true);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(CATEGORY_VALIDATION_MESSAGE, errors);
        }

        try {
            Optional<BookCategory> existingWithName = bookDao.findCategoryByName(category.getName());
            if (existingWithName.isPresent() && existingWithName.get().getId() != category.getId()) {
                errors.put("name", "分类名称已被使用。");
                return ServiceResult.validationFailure(CATEGORY_VALIDATION_MESSAGE, errors);
            }

            if (!bookDao.updateCategory(category)) {
                return ServiceResult.failure("未找到分类。");
            }

            LOGGER.info("Updated book category id=" + category.getId() + " actorId=" + actor.getId());
            return ServiceResult.success(null, "分类已更新。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to update book category id=" + category.getId()
                + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> deleteCategory(AuthenticatedUser actor, long id) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的分类。");
        }

        try {
            if (!bookDao.findCategoryById(id).isPresent()) {
                return ServiceResult.failure("未找到分类。");
            }
            if (bookDao.countBooksByCategoryId(id) > 0) {
                Map<String, String> errors = new LinkedHashMap<>();
                errors.put("category", "该分类已被现有图书使用，不能删除。");
                return ServiceResult.validationFailure("该分类已被现有图书使用，不能删除。",
                    errors);
            }
            if (!bookDao.deleteCategory(id)) {
                return ServiceResult.failure("未找到分类。");
            }

            LOGGER.info("Deleted book category id=" + id + " actorId=" + actor.getId());
            return ServiceResult.success(null, "分类已删除。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to delete book category id=" + id + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<List<Book>> searchBooks(BookSearchCriteria criteria) {
        BookSearchCriteria normalized = criteria == null ? new BookSearchCriteria() : criteria;
        if (normalized.getCategoryId() != null && normalized.getCategoryId() <= 0) {
            Map<String, String> errors = new LinkedHashMap<>();
            errors.put("categoryId", "请选择有效的分类。");
            return ServiceResult.validationFailure("请修正馆藏检索筛选条件。", errors);
        }

        try {
            return ServiceResult.success(bookDao.search(normalized));
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to search books", ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Optional<Book>> findBook(long id) {
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的图书。");
        }

        try {
            return ServiceResult.success(bookDao.findById(id));
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to load book id=" + id, ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Long> createBook(AuthenticatedUser actor, Book book) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(book);
        Map<String, String> errors = validate(book, false);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
        }

        try {
            if (bookDao.findByIdentifier(book.getIdentifier()).isPresent()) {
                errors.put("identifier", "图书编号已被使用。");
                return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
            }

            long id = bookDao.create(book);
            LOGGER.info("Created book id=" + id + " actorId=" + actor.getId());
            return ServiceResult.success(id, "图书已创建。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to create book actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> updateBook(AuthenticatedUser actor, Book book) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(book);
        Map<String, String> errors = validate(book, true);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
        }

        try {
            Optional<Book> existingWithIdentifier = bookDao.findByIdentifier(book.getIdentifier());
            if (existingWithIdentifier.isPresent() && existingWithIdentifier.get().getId() != book.getId()) {
                errors.put("identifier", "图书编号已被使用。");
                return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
            }

            if (!bookDao.update(book)) {
                return ServiceResult.failure("未找到图书。");
            }

            LOGGER.info("Updated book id=" + book.getId() + " actorId=" + actor.getId());
            return ServiceResult.success(null, "图书已更新。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to update book id=" + book.getId() + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> deleteBook(AuthenticatedUser actor, long id) {
        if (!canManageBooks(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的图书。");
        }

        try {
            if (!bookDao.delete(id)) {
                return ServiceResult.failure("未找到图书。");
            }

            LOGGER.info("Deleted book id=" + id + " actorId=" + actor.getId());
            return ServiceResult.success(null, "图书已删除。");
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to delete book id=" + id + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    private boolean canManageBooks(AuthenticatedUser actor) {
        return actor != null && permissionPolicy.allows(actor.getRole(), Permission.MANAGE_BOOKS);
    }

    private void normalize(Book book) {
        if (book == null) {
            return;
        }
        book.setIdentifier(trim(book.getIdentifier()));
        book.setTitle(trim(book.getTitle()));
        book.setAuthor(trim(book.getAuthor()));
    }

    private void normalize(BookCategory category) {
        if (category == null) {
            return;
        }
        category.setName(trim(category.getName()));
        category.setDescription(trim(category.getDescription()));
    }

    private Map<String, String> validate(Book book, boolean requireId) {
        Map<String, String> errors = new LinkedHashMap<>();
        if (book == null) {
            errors.put("book", "请填写图书详情。");
            return errors;
        }

        if (requireId && book.getId() <= 0) {
            errors.put("id", "请选择有效的图书。");
        }
        requireLength(errors, "identifier", book.getIdentifier(), "图书编号", 64);
        requireLength(errors, "title", book.getTitle(), "书名", 200);
        requireLength(errors, "author", book.getAuthor(), "作者", 120);
        if (book.getCategoryId() <= 0) {
            errors.put("categoryId", "请选择分类。");
        }
        if (book.getTotalCopies() < 0) {
            errors.put("totalCopies", "馆藏总数不能为负数。");
        }
        if (book.getAvailableCopies() < 0) {
            errors.put("availableCopies", "可借数量不能为负数。");
        }
        if (book.getAvailableCopies() > book.getTotalCopies()) {
            errors.put("availableCopies", "可借数量不能超过馆藏总数。");
        }
        if (book.getStatus() == null) {
            errors.put("status", "请选择状态。");
        }
        return errors;
    }

    private Map<String, String> validate(BookCategory category, boolean requireId) {
        Map<String, String> errors = new LinkedHashMap<>();
        if (category == null) {
            errors.put("category", "请填写分类详情。");
            return errors;
        }

        if (requireId && category.getId() <= 0) {
            errors.put("id", "请选择有效的分类。");
        }
        requireLength(errors, "name", category.getName(), "分类名称", 96);
        if (category.getDescription() != null && category.getDescription().length() > 255) {
            errors.put("description", "说明不能超过 255 个字符。");
        }
        return errors;
    }

    private String safeCategoryName(BookCategory category) {
        return category == null ? "" : category.getName();
    }

    private void requireLength(Map<String, String> errors, String field, String value, String label, int maxLength) {
        if (value == null || value.isEmpty()) {
            errors.put(field, "请填写" + label + "。");
            return;
        }
        if (value.length() > maxLength) {
            errors.put(field, label + "不能超过 " + maxLength + " 个字符。");
        }
    }

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