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

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
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.CreateImportErrorReportReq;
import com.ffii.lioner.modules.lioner.req.UpdateExtRefReq;
import com.ffii.lioner.modules.lioner.service.AwardService;
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.lioner.modules.master.service.CategoryService;
import com.ffii.lioner.modules.master.service.PromotionChannelService;
import com.ffii.lioner.modules.master.service.SubDivisionService;
import com.ffii.lioner.modules.master.service.TagService;
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("/award")
public class AwardController{

    private final Log logger = LogFactory.getLog(getClass());
	private AwardService awardService;
    private FileService fileService;
    private FileRefService fileRefService;
    private ExcelReportService excelReportService;
    private PromotionChannelService promotionChannelService;
    private TagService tagService;
    private CategoryService categoryService;
    private SubDivisionService subDivisionService;

    public AwardController(AwardService awardService, FileService fileService,
     FileRefService fileRefService, ExcelReportService excelReportService,
     PromotionChannelService promotionChannelService, TagService tagService,
     CategoryService categoryService, SubDivisionService subDivisionService
     ) {
        this.awardService = awardService;
        this.fileService = fileService;
        this.fileRefService = fileRefService;
        this.excelReportService = excelReportService;
        this.promotionChannelService = promotionChannelService;
        this.tagService = tagService;
        this.categoryService = categoryService;
        this.subDivisionService = subDivisionService;
	}

    @GetMapping("/{id}")
	public Map<String, Object> get(@PathVariable Long id) {
		return Map.of(
				Params.DATA, awardService.getAwardDetail(Map.of(Params.ID, id)).orElseThrow(NotFoundException::new),
                "subDivisionList", awardService.getAwardSubDivision(Map.of(Params.ID, id))
            );
	}

    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
    @PostMapping(value = "/massImportAward", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Map<String, Object> massImport(
        @RequestParam(value = "files", required = false) MultipartFile[] files) throws Exception {
			Map<String,Object> resultMap = awardService.massImport(files);
			return Map.of(Params.DATA, resultMap);
	}

    @PostMapping("/errorExport")
	public ResponseEntity<Resource> downloadAppreciationsImportErrorReport(@RequestBody @Valid CreateImportErrorReportReq req) throws Exception {

		byte[] reportResult = awardService.generateImportErrorReport(req.getErrorRecord());
		
		final DateTimeFormatter fileDateFormat = DateTimeFormatter.ofPattern("YYYYMMdd");

        return ResponseEntity.ok()
                    .header("filename", "Award_Import_Error_Report_" + LocalDate.now().format(fileDateFormat) + ".xlsx")
                    .body(new ByteArrayResource(reportResult));
	}

    @GetMapping
	public RecordsRes<Map<String, Object>> list(HttpServletRequest request) throws ServletRequestBindingException {
		return new RecordsRes<>(awardService.list(
				CriteriaArgsBuilder.withRequest(request)
                .addStringLike("eventName")
                .addStringLike("applicationName")
                .addDate("fromDate")
                .addDateTo("toDate")
                .addDate("awardFromDate")
                .addDateTo("awardToDate")
                .addStringLike("awardName")
                .addIntegerList("categoryIds")
                .addIntegerList("tagIds")
                .addIntegerList("divisionIdList")
                .addInteger("userSubDivisionId")
                .addIntegerList("subDivisionIdList")
                .addStringLike("storageLocation")
                .addStringLike("physicalAward")
                .addString("promotionChannel")
                .addStringLike("responsibleOfficer")
                .addStringLike("keyword")
						.build()));
	}
    
    @GetMapping("/getExportTemplate")
	public ResponseEntity<Resource> getAwardImportTemplate() throws ServletRequestBindingException, IOException {
		Map<String,List<Map<String,Object>>> masterData  = new HashMap<>();
        masterData.put("Promotion Channel", promotionChannelService.getNameList());
        masterData.put("Tag", tagService.getNameList());
        masterData.put("Category", categoryService.getNameList());
        masterData.put("Sub-Division", subDivisionService.getNameList());

        byte[] reportResult = excelReportService.exportAwardImportTemplate(masterData);
		return ResponseEntity.ok()
				.header("filename", "Award_Record_Import_Template_v1.0.xlsx")
				.body(new ByteArrayResource(reportResult));
    }

    @GetMapping("/export")
	public ResponseEntity<Resource> getAwardExport(HttpServletRequest request) throws ServletRequestBindingException, IOException {
        List<Map<String,Object>> record = awardService.listReport(CriteriaArgsBuilder.withRequest(request)
                .addStringLike("eventName")
                .addStringLike("applicationName")
                .addDate("fromDate")
                .addDateTo("toDate")
                .addDate("awardFromDate")
                .addDateTo("awardToDate")
                .addStringLike("awardName")
                .addIntegerList("categoryIds")
                .addIntegerList("tagIds")
                .addIntegerList("divisionIdList")
                .addInteger("userSubDivisionId")
                .addIntegerList("subDivisionIdList")
                .addStringLike("storageLocation")
                .addStringLike("physicalAward")
                .addString("promotionChannel")
                .addStringLike("responsibleOfficer")
                .addStringLike("keyword")
					.build());
		
		byte[] reportResult = excelReportService.generateAwardExcelReport(record);
		return ResponseEntity.ok()
				.header("filename", "award_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 applicationId,
        @RequestParam String name ,
        @RequestParam String nameCht ,
        @RequestParam String remarks ,
        @RequestParam Long categoryId ,
        @RequestParam(value = "receiveDate", required = false) LocalDate receiveDate ,
        @RequestParam(value = "storageLocation", required = false)  String storageLocation,
        @RequestParam(value = "physicalAward", required = false)  String physicalAward ,
        @RequestParam String responsibleOfficer ,
        @RequestParam(value = "promotionChannel", required = false)  String promotionChannel ,
        @RequestParam List<Long> subDivisionIdList,
        @RequestParam List<Long> subDivisionRemoveIdList,
        @RequestParam(value = "files", required = false) MultipartFile[] files) throws Exception {

            Map<String,Object> resultMap = awardService.saveOrUpdate(
            id, applicationId, name, nameCht, remarks, categoryId,receiveDate,
            storageLocation,physicalAward,responsibleOfficer,
            promotionChannel,subDivisionIdList,subDivisionRemoveIdList,
            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", "award"
                )
        ));
	}

    @GetMapping("/storedSize/{id}")
	public Map<String, Object> getFileStoredSize(@PathVariable Long id) throws ServletRequestBindingException {
		BigDecimal size = fileRefService.getFileStoredSize(
            Map.of(
                "refId",id,
                "refType", "award"
            ));
        
        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", "award"
            ));
        
        return Map.of("count", count);
	}

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

    @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, "award"));
	}

    @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) {
        awardService.markDeleteWithAuditLog(awardService.find(id).get());

	}

    @GetMapping("/checkDuplicate")
    public Map<String, Boolean> checkDuplicate(@RequestParam String name, @RequestParam Long id, @RequestParam Long applicationId) {
        boolean isNameTaken = awardService.isNameTaken(name,id,applicationId);
        return Map.of(
            "isTaken", isNameTaken
        );
    }

}
