package com.example.demo.news.api;

import java.util.List;
import java.util.Map;

import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.demo.core.api.PageAttributesMapper;
import com.example.demo.core.configuration.Constants;
import com.example.demo.department.api.DepartmentDto;
import com.example.demo.department.service.DepartmentService;
import com.example.demo.news.model.NewsEntity;
import com.example.demo.news.service.NewsService;

import jakarta.validation.Valid;

@Controller
@RequestMapping(NewsController.URL)
public class NewsController {
    public static final String URL = "/news";
    private static final String NEWS_VIEW = "news";
    private static final String NEWS_EDIT_VIEW = "new-edit";
    private static final String NEWS_ATTRIBUTE = "news";
    private static final String PAGE_ATTRIBUTE = "page";
    private static final String DEPARTMENT_ATTRIBUTE = "departments";
    private static final String DEPARTMENT_ITEM_ATTRIBUTE = "department";
    private static final String DESCRIPTION_ATTRIBUTE = "description";
    private final NewsService newsService;
    private final ModelMapper modelMapper;
    private final DepartmentService departmentService;

    public NewsController(NewsService newsService, ModelMapper modelMapper, DepartmentService departmentService) {
        this.newsService = newsService;
        this.modelMapper = modelMapper;
        this.departmentService = departmentService;
    }

    private NewsDto toDto(NewsEntity entity) {
        return modelMapper.map(entity, NewsDto.class);
    }

    private NewsEntity toEntity(NewsDto dto) {
        final NewsEntity entity = modelMapper.map(dto, NewsEntity.class);
        entity.setDepartment(departmentService.get(dto.getDepartmentId()));
        return entity;
    }

    private List<DepartmentDto> getDepartments(){
       return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
    }

    private DepartmentDto getDepartment(Long departmentId){
       return modelMapper.map(departmentService.get(departmentId), new TypeToken<DepartmentDto>(){}.getType());
    }

    @GetMapping
    public String getAll(
        @RequestParam(name=PAGE_ATTRIBUTE, defaultValue = "0") int page,
        @RequestParam(name="departmentId", required = false) Long departmentId,
        @RequestParam(name=DESCRIPTION_ATTRIBUTE, required = false) String description,
        @RequestParam(name="reset", required = false, defaultValue = "false") String reset,
        Model model) {
            if (reset.contains("true")){
                description = "";
                departmentId = null;
            }
        final Map<String, Object> attributes = PageAttributesMapper.
            toAttributes(newsService.getAll(page, Constants.DEFAULT_PAGE_SIZE_FOR_NEWS, departmentId, description), this::toDto);
        model.addAllAttributes(attributes);
        model.addAttribute(PAGE_ATTRIBUTE, page);
        model.addAttribute(DESCRIPTION_ATTRIBUTE, description);
        if(departmentId != null){
            model.addAttribute("departmentId", departmentId);
            model.addAttribute(DEPARTMENT_ITEM_ATTRIBUTE, departmentService.get(departmentId));
        }
        model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
        return NEWS_VIEW;
    }

    @GetMapping("/edit/")
    public String create(Model model) {
        model.addAttribute(NEWS_ATTRIBUTE, new NewsDto());
        model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
        return NEWS_EDIT_VIEW;
    }

    @PostMapping("/edit/")
    public String create(
            @ModelAttribute(name = NEWS_ATTRIBUTE) @Valid NewsDto newItem,
            BindingResult bindingResult,
            Model model) {
        if (bindingResult.hasErrors()) {
            return NEWS_EDIT_VIEW;
        }
        newsService.create(toEntity(newItem));
        return Constants.REDIRECT_VIEW + URL;
    }

    @PostMapping("/delete/{id}")
    public String delete(
            @PathVariable(name = "id") Long id) {
        newsService.delete(id);
        return Constants.REDIRECT_VIEW + URL;
    }

    @GetMapping("/edit/{id}")
    public String update(
            @PathVariable(name = "id") Long id,
            Model model) {
        if (id <= 0) {
            throw new IllegalArgumentException();
        }
        model.addAttribute(NEWS_ATTRIBUTE, toDto(newsService.get(id)));
        model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
        return NEWS_EDIT_VIEW;
    }

    @PostMapping("/edit/{id}")
    public String update(
            @PathVariable(name = "id") Long id,
            @ModelAttribute(name = NEWS_ATTRIBUTE) @Valid NewsDto newItem,
            BindingResult bindingResult,
            Model model) {
        if (bindingResult.hasErrors()) {
            return NEWS_EDIT_VIEW;
        }
        if (id <= 0) {
            throw new IllegalArgumentException();
        }
        newsService.update(id, toEntity(newItem));
        return Constants.REDIRECT_VIEW + URL;
    }
}
