package pwc.taxtech.atms.controller;

import org.apache.commons.io.FileUtils;
import org.joda.time.DateTime;
import org.nutz.lang.Lang;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import pwc.taxtech.atms.common.CommonUtils;
import pwc.taxtech.atms.common.message.ErrorMessage;
import pwc.taxtech.atms.constant.EnterpriseAccountConstant;
import pwc.taxtech.atms.dpo.EnterpriseAccountDto;
import pwc.taxtech.atms.dpo.EnterpriseAccountSetOrgDto;
import pwc.taxtech.atms.dto.OperationResultDto;
import pwc.taxtech.atms.dto.epaccount.AccountMapDto;
import pwc.taxtech.atms.dto.epaccount.AccountMappingDto;
import pwc.taxtech.atms.dto.epaccount.EnterpriseAccountAndValidateInfo;
import pwc.taxtech.atms.dto.epaccount.EnterpriseAccountSetDto;
import pwc.taxtech.atms.dto.epaccount.EnterpriseAccountUploadDto;
import pwc.taxtech.atms.exception.ApplicationException;
import pwc.taxtech.atms.service.impl.EnterpriseAccountServiceImpl;
import pwc.taxtech.atms.service.impl.EnterpriseAccountSetServiceImpl;
import pwc.taxtech.atms.service.impl.FileService;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@RestController
@RequestMapping("/api/v1/enterpriseAccountManage")
public class EnterpriseAccountManagerController {

    private static final Logger logger = LoggerFactory.getLogger(EnterpriseAccountManagerController.class);

    @Autowired
    private EnterpriseAccountSetServiceImpl enterpriseAccountSetService;
    @Autowired
    private EnterpriseAccountServiceImpl enterpriseAccountService;
    @Autowired
    private FileService fileService;

//    @ApiOperation(value = "Get the enterprise account set list")
    @RequestMapping(value = {"/getEnterpriseAccountSetList"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    List<EnterpriseAccountSetDto> getEnterpriseAccountSetList() {
        return enterpriseAccountSetService.getEnterpriseAccountSetList();
    }

//    @ApiOperation(value = "Gets the enterprise account set")
    @RequestMapping(value = {"/getEnterpriseAccountSet"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    EnterpriseAccountSetDto getEnterpriseAccountSet(@RequestParam String id) {
        return enterpriseAccountSetService.getEnterpriseAccountSet(id);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Updates the enterprise account set")
    @RequestMapping(value = {"/updateEnterpriseAccountSet"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto updateEnterpriseAccountSet(@RequestBody EnterpriseAccountSetDto enterpriseAccountSetDto) {
        return enterpriseAccountSetService.updateEnterpriseAccountSet(enterpriseAccountSetDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Enterprises the account set name validate")
    @RequestMapping(value = {"/enterpriseAccountSetNameValidate"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto enterpriseAccountSetNameValidate(@RequestParam String id, @RequestParam String name) {
        EnterpriseAccountSetDto enterpriseAccountSetDto = new EnterpriseAccountSetDto();
        enterpriseAccountSetDto.setId(id);
        enterpriseAccountSetDto.setName(name);
        return enterpriseAccountSetService.enterpriseAccountSetNameValidate(enterpriseAccountSetDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Enterprises the account set code validate")
    @RequestMapping(value = {"/enterpriseAccountSetCodeValidate"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto enterpriseAccountSetCodeValidate(@RequestParam String id, @RequestParam String code) {
        EnterpriseAccountSetDto enterpriseAccountSetDto = new EnterpriseAccountSetDto();
        enterpriseAccountSetDto.setId(id);
        enterpriseAccountSetDto.setCode(code);
        return enterpriseAccountSetService.enterpriseAccountSetCodeValidate(enterpriseAccountSetDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Enterprises the account set org validate")
    @RequestMapping(value = {"/enterpriseAccountSetOrgValidate"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto enterpriseAccountSetOrgValidate(@RequestBody EnterpriseAccountSetDto enterpriseAccountSetDto) {
        return enterpriseAccountSetService.enterpriseAccountSetOrgValidate(enterpriseAccountSetDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Add the oganization link to an enterprises account set")
    @RequestMapping(value = {"/addEnterpriseAccountSetOrg"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto addEnterpriseAccountSetOrg(@RequestBody EnterpriseAccountSetDto enterpriseAccountSetDto) {
        return enterpriseAccountSetService.addEnterpriseAccountSetOrg(enterpriseAccountSetDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Update the oganization link to an enterprises account set")
    @RequestMapping(value = {"/updateEnterpriseAccountSetOrg"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto updateEnterpriseAccountSetOrg(@RequestBody EnterpriseAccountSetOrgDto enterpriseAccountSetOrgDto) {
        return enterpriseAccountSetService.updateEnterpriseAccountSetOrg(enterpriseAccountSetOrgDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Delete the oganization link to an enterprises account set")
    @RequestMapping(value = {"/deleteEnterpriseAccountSetOrg"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto deleteEnterpriseAccountSetOrg(@RequestBody EnterpriseAccountSetDto enterpriseAccountSetDto) {
        return enterpriseAccountSetService.deleteEnterpriseAccountSetOrg(enterpriseAccountSetDto);
    }

//    @ApiOperation(value = "Download Enterprise account template")
    @RequestMapping(value = {"/downEntepriseAccountTemplate"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void downEntepriseAccountTemplate(HttpServletResponse response) {

        String filePath;
        File templateFile = null;
        InputStream inputStream = null;
        try {
            filePath = EnterpriseAccountManagerController.class.getClassLoader().getResource("").toURI().getPath();
            templateFile = new File(filePath + EnterpriseAccountConstant.EnterpriseAccountTemplate);
            inputStream = new BufferedInputStream(new FileInputStream(templateFile));
            String customFileName = EnterpriseAccountConstant.EntepriseAccountFileName + DateTime.now().toString("yyyyMMddHHmmss") + ".xlsx";//客户端保存的文件名
            response.setHeader("Content-Disposition", String.format("inline; filename=\"" + customFileName + "\""));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            FileCopyUtils.copy(inputStream, response.getOutputStream());
        } catch (FileNotFoundException e) {
            logger.error("Template file not found. Template file should be located at " + EnterpriseAccountConstant.EnterpriseAccountTemplate);
            throw new ApplicationException("Tempate file not found.");
        } catch (Exception e) {
            logger.error("Error downloading template file " + EnterpriseAccountConstant.EntepriseAccountFileName + ".xlsx", e);
            throw new ApplicationException("Error downloading template file " + EnterpriseAccountConstant.EntepriseAccountFileName + ".xlsx", e);
        } finally {
            try {
                templateFile = null;
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                logger.error("Error closing inputstream. ", e);
            } finally {
                inputStream = null;
            }
        }
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "Upload and save enterprise accounts.")
    @RequestMapping(value = {"/Upload"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto uploadEnterpriseAccount(@ModelAttribute EnterpriseAccountUploadDto uploadForm,
                                               @RequestParam(value = "file", required = false) CommonsMultipartFile inputFile) {

        if (inputFile == null || inputFile.getSize() <= 0) {
            return new OperationResultDto<>(false, ErrorMessage.NoFile);
        }

        logger.debug("file name: " + inputFile.getOriginalFilename());
        InputStream inputStream = null;
        try {
            inputStream = inputFile.getInputStream();
        } catch (IOException e) {
            throw Lang.wrapThrow(e);
        }

        //save file
        String filePath = FileUtils.getTempDirectory().getAbsolutePath() + File.separator + "EnterpriseAccount" + File.separator
                + CommonUtils.getUUID() + "_" + inputFile.getOriginalFilename();
        logger.debug("Saving excel to {}", filePath);
        OperationResultDto<Object> saveResult = fileService.saveFile(inputStream, filePath);
        if (saveResult.getResult() != null && !saveResult.getResult()) {
            return saveResult;
        }

        //validate file
        OperationResultDto<List<EnterpriseAccountDto>> validateResult = enterpriseAccountService.validateImportEnterpriseAccount(filePath);
        if (!validateResult.getResult()) {
            return validateResult;
        }

        //import
        String enterpriseAccountSetId = uploadForm.getEnterpriseAccountSetId();
        //创建新账套
        if (enterpriseAccountSetId == null || enterpriseAccountSetId.isEmpty()) {
            logger.debug("Uploading and save enterprise account set. "
                    + "[name=" + uploadForm.getName() + ", code=" + uploadForm.getCode()
                    + ", selectedOrgList=" + uploadForm.getSelectedOrgList() + ", isImportAppend=" + uploadForm.getIsImportAppend() + ".]");
            enterpriseAccountService.addEnterpriseAccountSetAndImportData(uploadForm.getName(), uploadForm.getCode(), validateResult.getData());
        }
        //仅仅上传科目
        else {
            logger.debug("Uploading enterprise account. "
                    + "[accountset id=" + uploadForm.getEnterpriseAccountSetId() + ", name=" + uploadForm.getName() + ", code=" + uploadForm.getCode()
                    + ", selectedOrgList=" + uploadForm.getSelectedOrgList() + ", isImportAppend=" + uploadForm.getIsImportAppend() + ".]");
            EnterpriseAccountSetDto enterpriseAccountSetDto = new EnterpriseAccountSetDto();
            enterpriseAccountSetDto.setId(uploadForm.getEnterpriseAccountSetId());
            enterpriseAccountSetDto.setName(uploadForm.getName());
            enterpriseAccountSetDto.setIsImportAppend(Boolean.valueOf(uploadForm.getIsImportAppend()));
            enterpriseAccountService.repeatImportEnterpriseAccountSet(enterpriseAccountSetDto, validateResult.getData());

        }
        return OperationResultDto.success();
    }

//    @ApiOperation(value = "Gets the list by enterprise account set identifier.")
    @RequestMapping(value = {"/getListByEnterpriseAccountSetID"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    EnterpriseAccountAndValidateInfo getListByEnterpriseAccountSetId(@RequestParam String enterpriseAccountSetID) {
        return enterpriseAccountService.getListByEnterpriseAccountSetId(enterpriseAccountSetID);
    }

//    @ApiOperation(value = "Gets the specified enterprise account by identifier")
    @RequestMapping(value = {"/getsingle"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    EnterpriseAccountDto getEnterpriseAccount(@RequestParam String id) {
        return enterpriseAccountService.getEnterpriseAccount(id);
    }

//    @ApiOperation(value = "Add an enterprise account")
    @RequestMapping(value = {"/add"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto<List<EnterpriseAccountDto>> addEnterpriseAccount(@RequestBody EnterpriseAccountDto enterpriseAccountDto) {
        return enterpriseAccountService.addEnterpriseAccount(enterpriseAccountDto);
    }

//    @ApiOperation(value = "Update an enterprise account")
    @RequestMapping(value = {"/update"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto<List<EnterpriseAccountDto>> updateEnterpriseAccount(@RequestBody EnterpriseAccountDto enterpriseAccountDto) {
        return enterpriseAccountService.updateEnterpriseAccount(enterpriseAccountDto);
    }

    @SuppressWarnings("rawtypes")
//    @ApiOperation(value = "一键删除重复企业科目")
    @RequestMapping(value = {"/clearRepeatEnterpriseAccountList"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    OperationResultDto clearRepeatEnterpriseAccountList(@RequestBody EnterpriseAccountSetDto enterpriseAccountSetDto) {
        return enterpriseAccountService.clearRepeatEnterpriseAccountList(enterpriseAccountSetDto);
    }

//    @ApiOperation(value = "获取企业账套Mapping")
    @RequestMapping(value = {"/getAccountMappingOrg"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody
    List<AccountMappingDto> getAccountMappingOrg(@RequestParam(name = "organizationID") String organizationId) {
        return enterpriseAccountService.getAccountMappingOrg(organizationId);
    }

    @ResponseBody
//    @ApiOperation(value = "按组织机构查找")
    @RequestMapping(value = "getEnterpriseAccountSetListByOrgID", method = RequestMethod.GET)
    public List<EnterpriseAccountSetDto> getEnterpriseAccountSetListByOrgId(@RequestParam(name = "orgID") String orgId) {
        return enterpriseAccountService.getEnterpriseAccountSetListByOrgId(orgId);
    }

    @ResponseBody
//    @ApiOperation(value = "科目对应列表")
    @RequestMapping(value = "getEnterpriseAccountList", method = RequestMethod.GET)
    public List<EnterpriseAccountDto> getEnterpriseAccountList(@RequestParam(name = "espID") String espId,
                                                               @RequestParam(name = "orgID") String orgId,
                                                               @RequestParam String filterType) {
        return enterpriseAccountService.getList(espId, orgId, filterType);
    }

    @ResponseBody
//    @ApiOperation(value = "科目自动对应")
    @RequestMapping(value = "autoMap", method = RequestMethod.GET)
    public OperationResultDto autoMap(@RequestParam String orgId, @RequestParam String accountSetId) {
        try {
            return enterpriseAccountService.autoMap(orgId, accountSetId);
        } catch (Exception e) {
            logger.error("autoMap error.", e);
        }
        return OperationResultDto.error("自动对应失败");
    }

    @ResponseBody
//    @ApiOperation(value = "手动对应")
    @RequestMapping(value = "mapAccount", method = RequestMethod.POST)
    public OperationResultDto mapAccount(@RequestBody AccountMapDto dto) {
        try {
            return enterpriseAccountService.mapAccount(dto);
        } catch (Exception e) {
            logger.error("mapAccount error.", e);
        }
        return OperationResultDto.error("手动对应失败");
    }

    @ResponseBody
//    @ApiOperation(value = "取消对应")
    @RequestMapping(value = "clearMap", method = RequestMethod.POST)
    public OperationResultDto clearMap(@RequestBody String[] epAccountIds, @RequestParam String orgId) {
        try {
            return enterpriseAccountService.clearMap(epAccountIds, orgId);
        } catch (Exception e) {
            logger.error("clearMap error.", e);
        }
        return OperationResultDto.error("取消对应失败");
    }
}