package com.ffii.lioner.modules.lioner.web;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.ffii.lioner.modules.lioner.req.UpdateExtRefReq;
import com.ffii.lioner.modules.lioner.service.ApplicationService;
import com.ffii.lioner.modules.lioner.service.FileRefService;
import com.ffii.lioner.modules.lioner.service.FileService;
import com.ffii.lioner.modules.common.service.ExcelReportService;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.response.IdRes;
import com.ffii.core.response.RecordsRes;
import com.ffii.core.utils.CriteriaArgsBuilder;
import com.ffii.core.utils.Params;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;

@RestController
@RequestMapping("/application")
public class ApplicationController{

    private final Log logger = LogFactory.getLog(getClass());
	private ApplicationService applicationService;
    private FileService fileService;
    private FileRefService fileRefService;
    private ExcelReportService excelReportService;

    public ApplicationController(ApplicationService applicationService, FileService fileService, FileRefService fileRefService, ExcelReportService excelReportService) {
        this.applicationService = applicationService;
        this.fileService = fileService;
        this.fileRefService = fileRefService;
        this.excelReportService = excelReportService;
	}

    @GetMapping("/{id}")
	public Map<String, Object> get(@PathVariable Long id) {
		return Map.of(
				Params.DATA, applicationService.getApplicationDetail(Map.of(Params.ID, id)).orElseThrow(NotFoundException::new),
                "tagList", applicationService.getApplicationTag(Map.of(Params.ID, id)),
                "subDivisionList", applicationService.getApplicationSubDivision(Map.of(Params.ID, id))
            );
	}

    @GetMapping
	public RecordsRes<Map<String, Object>> list(HttpServletRequest request) throws ServletRequestBindingException {
		return new RecordsRes<>(applicationService.list(
				CriteriaArgsBuilder.withRequest(request)
						.addStringLike("eventName")
                        .addStringLike("applicationName")
                        .addStringLike("description")
                        .addStringLike("status")
                        .addStringLike("officer")
                        .addInteger("userSubDivisionId")
                        .addIntegerList("tagList")
                        .addIntegerList("divisionIdList")
                        .addIntegerList("subDivisionIdList")
                        .addStringLike("keyword")
						.build()));
	}
    
    @GetMapping("/checkDuplicate")
    public Map<String, Boolean> checkDuplicate(@RequestParam String name, @RequestParam Long id, @RequestParam Long eventId) {
        boolean isNameTaken = applicationService.isNameTaken(name,id,eventId);
        return Map.of(
            "isTaken", isNameTaken
        );
    }

    @GetMapping("/export")
	public ResponseEntity<Resource> getApplicationExport(HttpServletRequest request) throws ServletRequestBindingException, IOException {
        List<Map<String,Object>> record = applicationService.listReport(CriteriaArgsBuilder.withRequest(request)
                    .addStringLike("eventName")
                    .addStringLike("applicationName")
                    .addStringLike("description")
                    .addStringLike("status")
                    .addStringLike("officer")
                    .addInteger("userSubDivisionId")
                    .addIntegerList("tagList")
                    .addIntegerList("divisionIdList")
                    .addIntegerList("subDivisionIdList")
                    .addStringLike("keyword")
					.build());
		
		byte[] reportResult = excelReportService.generateApplicationExcelReport(record);
		return ResponseEntity.ok()
				.header("filename", "application_export_" + LocalDate.now())
				.body(new ByteArrayResource(reportResult));
    }

    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
    @PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Map<String, Object> saveOrUpdate(
        @RequestParam Long id,
        @RequestParam Long eventId,
        @RequestParam String eventName ,
        @RequestParam String description ,
        @RequestParam String name ,
        @RequestParam String responsibleOfficer,
        @RequestParam String status ,
        @RequestParam List<Long> subDivisionIdList,
        @RequestParam List<Long> subDivisionRemoveIdList,
        @RequestParam List<Long> tagIdList ,
        @RequestParam List<Long> tagRemoveIdList,
        @RequestParam(value = "files", required = false) MultipartFile[] files) throws Exception {

            Map<String,Object> resultMap = applicationService.saveOrUpdate(
            id, eventId, eventName,description,name,
            responsibleOfficer,status,
            subDivisionIdList,subDivisionRemoveIdList,
            tagIdList,tagRemoveIdList,
            files
        );

		return Map.of(Params.DATA, resultMap);
	}
    
    @GetMapping("/fileRef/{id}")
	public RecordsRes<Map<String, Object>> getFileRefList(@PathVariable Long id) throws ServletRequestBindingException {
		return new RecordsRes<>(fileService.listFile(
				Map.of(
                    "refId",id,
                    "refType", "application"
                )
        ));
	}

    @GetMapping("/externalRef/{id}")
	public RecordsRes<Map<String, Object>> getExtRefList(@PathVariable Long id) throws ServletRequestBindingException {
		return new RecordsRes<>(fileRefService.listExternal(
				Map.of(
                    "refId",id,
                    "refType", "application"
                )
        ));
	}

    @GetMapping("/storedSize/{id}")
	public Map<String, Object> getFileStoredSize(@PathVariable Long id) throws ServletRequestBindingException {
		BigDecimal size = fileRefService.getFileStoredSize(
            Map.of(
                "refId",id,
                "refType", "application"
            ));
        
        return Map.of("size", size);
	}

    @GetMapping("/videoExist/{id}")
	public Map<String, Object> getVideoExist(@PathVariable Long id) throws ServletRequestBindingException {
		Integer count = fileRefService.getVideoExist(
            Map.of(
                "refId",id,
                "refType", "application"
            ));
        
        return Map.of("count", count);
	}

    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
    @PostMapping("/externalRef/save")
	public IdRes saveOrUpdate(@RequestBody @Valid UpdateExtRefReq req) {
		return new IdRes(this.fileRefService.saveExternal(req, "application"));
	}

    @DeleteMapping("/externalRef/{id}")
	@ResponseStatus(HttpStatus.NO_CONTENT)
	public void deleteFile(@PathVariable Long id) {
		fileRefService.markDelete(id);
	}

    @DeleteMapping("/{id}")
	@ResponseStatus(HttpStatus.NO_CONTENT)
	public void delete(@PathVariable Long id) {
        applicationService.markDeleteWithAuditLog(applicationService.find(id).get());

	}

    @GetMapping("/award")
	public RecordsRes<Map<String, Object>> listByApplication(HttpServletRequest request) throws ServletRequestBindingException {
		return new RecordsRes<>(applicationService.listByApplication(
                CriteriaArgsBuilder.withRequest(request)
            .addInteger("applicationId")
            .addInteger("userSubDivisionId")
            .build()
        ));
	}

    @GetMapping("/combo")
	public RecordsRes<Map<String, Object>> getApplicationCombo(HttpServletRequest request) throws ServletRequestBindingException {
		System.out.println(request);
		return new RecordsRes<>(applicationService.listApplicationCombo(
				CriteriaArgsBuilder.withRequest(request)
						.addInteger("eventId")
                        .addInteger("userSubDivisionId")
						.build()));
	}

}
