package com.example.backend.books.api;

import java.text.ParseException;
import java.util.Date;

import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.example.backend.core.api.PageAttributesMapper;
import com.example.backend.core.configuration.Constants;
import com.example.backend.core.utils.Formatter;
import com.example.backend.core.utils.ToBase64;
import com.example.backend.books.model.BookEntity;
import com.example.backend.books.service.BookService;
import com.example.backend.categories.api.CategoryDto;
import com.example.backend.categories.model.CategoryEntity;
import com.example.backend.categories.service.CategoryService;

import jakarta.validation.Valid;

@Controller
@RequestMapping(BookController.URL)
public class BookController {
    public static final String URL = Constants.ADMIN_PREFIX + "/book";

    private static final String BOOK_VIEW = "book";
    private static final String BOOK_EDIT_VIEW = "book-edit";

    private static final String PAGE_ATTRIBUTE = "page";
    private static final String CATEGORYID_ATTRIBUTE = "filter";
    private static final String CATEGORIES_ATTRIBUTE = "categories";
    private static final String BOOK_ATTRIBUTE = "book";

    private final BookService bookService;
    private final CategoryService categoryService;
    private final ModelMapper modelMapper;

    public BookController(BookService bookService, CategoryService categoryService, ModelMapper modelMapper) {
        this.bookService = bookService;
        this.categoryService = categoryService;
        this.modelMapper = modelMapper;
    }

    private BookDto toDto(BookEntity entity) {
        final BookDto dto = modelMapper.map(entity, BookDto.class);
        dto.setCategoryName(entity.getCategory().getName());
        dto.setDate(Formatter.format(entity.getDate()));
        return dto;
    }

    private BookEntity toEntity(BookDto dto) throws ParseException {
        final BookEntity entity = modelMapper.map(dto, BookEntity.class);
        entity.setCategory(categoryService.get(dto.getCategoryId()));
        entity.setDate(Formatter.parse(dto.getDate()));
        return entity;
    }

    private CategoryDto toCategoryDto(CategoryEntity entity) {
        return modelMapper.map(entity, CategoryDto.class);
    }

    @GetMapping
    public String getAll(
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @RequestParam(name = CATEGORYID_ATTRIBUTE, defaultValue = "0") int filter,
            Model model) {
        model.addAttribute(PAGE_ATTRIBUTE, page);
        model.addAttribute(CATEGORYID_ATTRIBUTE, filter);
        model.addAllAttributes(PageAttributesMapper.toAttributes(
                bookService.getAll(filter, page, Constants.DEFAULT_PAGE_SIZE),
                this::toDto));
        model.addAttribute(
                CATEGORIES_ATTRIBUTE,
                categoryService.getAll().stream()
                        .map(this::toCategoryDto)
                        .toList());
        return BOOK_VIEW;
    }

    @GetMapping("/edit/")
    public String create(
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @RequestParam(name = CATEGORYID_ATTRIBUTE, defaultValue = "0") int filter,
            Model model) {
        model.addAttribute(PAGE_ATTRIBUTE, page);
        model.addAttribute(CATEGORYID_ATTRIBUTE, filter);
        model.addAttribute(
                CATEGORIES_ATTRIBUTE,
                categoryService.getAll().stream()
                        .map(this::toCategoryDto)
                        .toList());
        model.addAttribute(BOOK_ATTRIBUTE, new BookDto());
        return BOOK_EDIT_VIEW;
    }

    @PostMapping("/edit/")
    public String create(
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @ModelAttribute(name = BOOK_ATTRIBUTE) @Valid BookDto book,
            @RequestParam("cover") MultipartFile cover,
            BindingResult bindingResult,
            Model model,
            RedirectAttributes redirectAttributes) throws ParseException {
        if (bindingResult.hasErrors()) {
            model.addAttribute(PAGE_ATTRIBUTE, page);
            model.addAttribute(
                    CATEGORIES_ATTRIBUTE,
                    categoryService.getAll().stream()
                            .map(this::toCategoryDto)
                            .toList());
            return BOOK_EDIT_VIEW;
        }

        String base64Image = ToBase64.getBase64FromFile(cover);

        book.setImage(base64Image);

        redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
        book.setDate(Formatter.format(new Date()));
        bookService.create(toEntity(book));
        return Constants.REDIRECT_VIEW + URL;
    }

    @GetMapping("/edit/{id}")
    public String update(
            @PathVariable(name = "id") Long id,
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @RequestParam(name = CATEGORYID_ATTRIBUTE, defaultValue = "0") int filter,
            Model model) {
        if (id <= 0) {
            throw new IllegalArgumentException();
        }
        model.addAttribute(PAGE_ATTRIBUTE, page);
        model.addAttribute(CATEGORYID_ATTRIBUTE, filter);
        model.addAttribute(
                CATEGORIES_ATTRIBUTE,
                categoryService.getAll().stream()
                        .map(this::toCategoryDto)
                        .toList());
        model.addAttribute(BOOK_ATTRIBUTE, toDto(bookService.get(id)));
        return BOOK_EDIT_VIEW;
    }

    @PostMapping("/edit/{id}")
    public String update(
            @PathVariable(name = "id") Long id,
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @ModelAttribute(name = BOOK_ATTRIBUTE) @Valid BookDto book,
            @RequestParam("cover") MultipartFile cover,
            BindingResult bindingResult,
            Model model,
            RedirectAttributes redirectAttributes) throws ParseException {
        if (bindingResult.hasErrors()) {
            model.addAttribute(PAGE_ATTRIBUTE, page);
            model.addAttribute(
                    CATEGORIES_ATTRIBUTE,
                    categoryService.getAll().stream()
                            .map(this::toCategoryDto)
                            .toList());
            return BOOK_EDIT_VIEW;
        }
        if (id <= 0) {
            throw new IllegalArgumentException();
        }

        String base64Image = ToBase64.getBase64FromFile(cover);
        book.setImage(base64Image);

        redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
        bookService.update(id, toEntity(book));
        return Constants.REDIRECT_VIEW + URL;
    }

    @PostMapping("/delete/{id}")
    public String delete(
            @PathVariable(name = "id") Long id,
            @RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
            @RequestParam(name = CATEGORYID_ATTRIBUTE, defaultValue = "0") int filter,
            RedirectAttributes redirectAttributes) {
        redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
        redirectAttributes.addAttribute(CATEGORYID_ATTRIBUTE, filter);
        bookService.delete(id);
        return Constants.REDIRECT_VIEW + URL;
    }
}
