package com.caliverse.admin.domain.service;

import com.caliverse.admin.domain.adminlog.AdminPasswordInitLog;
import com.caliverse.admin.domain.adminlog.IAdminLog;
import com.caliverse.admin.domain.dao.admin.AdminMapper;
import com.caliverse.admin.domain.dao.admin.GroupMapper;
import com.caliverse.admin.domain.dao.admin.HistoryMapper;
import com.caliverse.admin.domain.entity.*;
import com.caliverse.admin.domain.request.AdminRequest;
import com.caliverse.admin.domain.request.AuthenticateRequest;
import com.caliverse.admin.domain.response.AdminResponse;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.code.SuccessCode;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.common.utils.CommonUtils;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class AdminService {
    private static final Logger logger = LoggerFactory.getLogger(AdminService.class);

    private final AdminMapper adminMapper;
    private final GroupMapper groupMapper;
    private final PasswordEncoder passwordEncoder;
    private final HistoryService historyService;
    @Value("${password.expiration-days}")
    private int passwordExpiration;

    @Value("${spring.mail.host}")
    private String host;
    @Value("${spring.mail.port}")
    private int port;
    @Value("${spring.mail.username}")
    private String username;
    @Value("${spring.mail.password}")
    private String password;
    // 비번 초기화
    @Transactional(transactionManager = "transactionManager")
    public AdminResponse initPassword(AuthenticateRequest authenticateRequest){

        Optional<Admin> admin = adminMapper.findByEmail(authenticateRequest.getEmail());
        String initPwd = randomPwd();
        adminMapper.initPwd(passwordEncoder.encode(initPwd), admin.get().getId(), CommonUtils.getAdmin().getId());
        //유저 비번 기록 남기기
        adminMapper.saveHistoryPwd(passwordEncoder.encode(initPwd), admin.get().getId());
        //smtp
        sendMail(authenticateRequest.getEmail(),initPwd);

        IAdminLog adminLog = new AdminPasswordInitLog(admin.get().getName(), admin.get().getEmail());
        adminLog.saveLogToDB();

        log.info("initPassword id: {}, email: {}", admin.get().getId(), admin.get().getEmail());

        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(AdminResponse.ResultData.builder().message(SuccessCode.INIT.getMessage()).build())
                .build();
    }

    // 비번 재설정
    @Transactional(transactionManager = "transactionManager")
    public AdminResponse updatePassword(AuthenticateRequest authenticateRequest){

        String oldPwd = authenticateRequest.getPassword();
        String newPwd = authenticateRequest.getNewPassword();
        String pwdById = adminMapper.findPwdById(CommonUtils.getAdmin().getId());

        /*
        https://nzin-publisher-bts.atlassian.net/browse/CAL-120
        임시 비밀번호 재설정 화면에서 현재 비밀번호 입력 필드가 노출되는 현상
         */
        //입력한 비번이랑 현재 db 비번이랑 동일한지 체크
        /*if(!passwordEncoder.matches(oldPwd,pwdById)){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.PASSWORD_ERROR.getMessage());
        }*/

        //기존 사용이력있는 비밀번호 재사용 할수 없습니다.
        List<String> pwdList = adminMapper.findPwdHistoryById(CommonUtils.getAdmin().getId());
        long count = pwdList.stream().filter(pwd -> passwordEncoder.matches(newPwd,pwd)).count();

        if(count == 0){
            adminMapper.updatePwd(passwordEncoder.encode(newPwd), CommonUtils.getAdmin().getId(), STATUS.PERMITTED );
            //유저 비번 기록 남기기
            adminMapper.saveHistoryPwd(passwordEncoder.encode(newPwd), CommonUtils.getAdmin().getId());
        }else{
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.PASSWORD_INCLUDE.getMessage());
        }

        log.info("updatePassword Changed Password user: {}", CommonUtils.getAdmin().getId());

        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(AdminResponse.ResultData.builder().message(SuccessCode.UPDATE.getMessage()).build())
                .build();
    }

    // 사용자 정보 조회
    public AdminResponse getAdminInfo(){
        AdminResponse.ResultData resultData = null;

        Optional<Admin> admin = adminMapper.findByEmail(CommonUtils.getAdmin().getEmail());
        if(admin.stream().count() > 0){
            // 권한 그룹 조회
            List<Authority> groupAuth = groupMapper.getGroupAuth(admin.get().getGroupId());

            resultData = AdminResponse.ResultData.builder()
                    .id(admin.get().getId())
                    .name(admin.get().getName())
                    .groupId(admin.get().getGroupId())
                    .email(admin.get().getEmail())
                    .status(admin.get().getStatus())
                    .authorityList(groupAuth)
                    .expiredDt(admin.get().getPwUpdateDt().plus(passwordExpiration, ChronoUnit.DAYS))
                    .authLevelType(admin.get().getAuthLevelType())
                    .build();
        }
        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(resultData)
                .build();
    }
    // 운영자 조회
    public AdminResponse getAdminList(Map<String, String> requestParams){

        AdminResponse.ResultData adminData = null;

        //페이징 처리
        requestParams = CommonUtils.pageSetting(requestParams);

        List<Admin> adminList = adminMapper.getAdminList(requestParams);

        int allCnt = adminMapper.getAllCnt(requestParams);

        return AdminResponse.builder()
                .resultData(AdminResponse.ResultData.builder()
                        .total(adminMapper.getTotal())
                        .totalAll(allCnt)
                        .pageNo(requestParams.get("page_no")!=null?
                                Integer.valueOf(requestParams.get("page_no").toString()):1)
                        .adminList(adminList).build())
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .build();
    }

    // 로그인 승인/불가
    @Transactional(transactionManager = "transactionManager")
    public AdminResponse updateStatus(AdminRequest adminRequest){
        Map<String , Object> map = new HashMap<>();
        adminRequest.getList().forEach(
                item -> {

                    map.put("email", item.getEmail());
                    map.put("id", CommonUtils.getAdmin().getId());

                    if(item.getIsApprove().equals(AdminRequest.Approve.APPROVE)){
                        map.put("status", STATUS.PERMITTED);
                    }else if (item.getIsApprove().equals(AdminRequest.Approve.REJECT)){
                        map.put("status", STATUS.REJECT);
                    }else{
                        map.put("status", STATUS.ROLE_NOT_PERMITTED);
                    }
                    //로그인 승인
                    adminMapper.updateStatus(map);

                    //로그 기록
                    Optional<Admin> admin = adminMapper.findByEmail(item.getEmail());
//                    map.put("adminId",  admin.get().getId());
//                    map.put("name",  CommonUtils.getAdmin().getName());
//                    map.put("mail",  CommonUtils.getAdmin().getEmail());
//                    map.put("type", HISTORYTYPE.LOGIN_PERMITTED);
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("name",admin.get().getName());
                    jsonObject.put("email",item.getEmail());
//                    map.put("content",jsonObject.toString());
//                    historyMapper.saveLog(map);
                    historyService.setLog(HISTORYTYPE.LOGIN_PERMITTED, jsonObject);
                }
        );

        log.info("updateStatus User: {}", map);

        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(AdminResponse.ResultData.builder().message(SuccessCode.UPDATE.getMessage()).build())
                .build();
    }

    //운영자 그룹 저장
    @Transactional(transactionManager = "transactionManager")
    public AdminResponse updateGroup(AdminRequest adminRequest){
        Map<String , Object> map = new HashMap<>();
        adminRequest.getList().forEach(
                item -> {
                    map.put("email", item.getEmail());
                    map.put("group_id", item.getGroupId());
                    map.put("id", CommonUtils.getAdmin().getId());

                    //변경전 그룹 명세 조회
                    Optional<Admin> admin = adminMapper.findByEmail(item.getEmail());

                    Map<String, String> beforeGroup = groupMapper.getGroupInfo(admin.get().getGroupId());
                    //쿼리 실행
                    adminMapper.updateGroup(map);
                    //로그 기록
                    //변경후 그룹 명세 조회
                    Map<String, String> afterGroup = groupMapper.getGroupInfo(item.getGroupId());

//                    map.put("adminId",  CommonUtils.getAdmin().getId());
//                    map.put("name",  CommonUtils.getAdmin().getName());
//                    map.put("mail",  CommonUtils.getAdmin().getEmail());
//                    map.put("type", HISTORYTYPE.ADMIN_INFO_UPDATE);
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("name",admin.get().getName());
                    jsonObject.put("email",item.getEmail());
                    jsonObject.put("Authority(Before)", beforeGroup != null?beforeGroup.get("name"):"");
                    jsonObject.put("Authority(after)", afterGroup != null? afterGroup.get("name"):"");
//                    map.put("content",jsonObject.toString());
//                    historyMapper.saveLog(map);
                    historyService.setLog(HISTORYTYPE.ADMIN_INFO_UPDATE, jsonObject);
                }
            );

        log.info("updateGroup Groups: {}", map);
        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(AdminResponse.ResultData.builder().message(SuccessCode.UPDATE.getMessage()).build())
                .build();
    }

    // 운영자 선택 삭제
    @Transactional(transactionManager = "transactionManager")
    public AdminResponse deleteAdmin(AdminRequest adminRequest){
        Map<String , Object> map = new HashMap<>();
        adminRequest.getList().forEach(
                item -> {
                    map.put("email", item.getEmail());
                    map.put("deleted", String.valueOf(1));
                    //로그 기록
                    Optional<Admin> admin = adminMapper.findByEmail(item.getEmail());

                    //쿼리 실행
                    adminMapper.deleteAdmin(map);

//                    map.put("adminId",  CommonUtils.getAdmin().getId());
//                    map.put("name",  CommonUtils.getAdmin().getName());
//                    map.put("mail",  CommonUtils.getAdmin().getEmail());
//                    map.put("type", HISTORYTYPE.ADMIN_INFO_DELETE);
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("name",admin.get().getName());
                    jsonObject.put("email",item.getEmail());
//                    map.put("content",jsonObject.toString());
//                    historyMapper.saveLog(map);
                    historyService.setLog(HISTORYTYPE.ADMIN_INFO_DELETE, jsonObject);
                }
            );
        log.info("deleteAdmin Deleted Admin: {}", map);
        return AdminResponse.builder()
                .status(CommonCode.SUCCESS.getHttpStatus())
                .result(CommonCode.SUCCESS.getResult())
                .resultData(AdminResponse.ResultData.builder().message(SuccessCode.DELETE.getMessage()).build())
                .build();
    }

    private String randomPwd(){
        final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        SecureRandom secureRandom = new SecureRandom();
        StringBuilder password = new StringBuilder();

        for (int i = 0; i < 10; i++) {
            int randomIndex = secureRandom.nextInt(CHARACTERS.length());
            char randomChar = CHARACTERS.charAt(randomIndex);
            password.append(randomChar);
        }

        return password.toString();
    }

    //초기화 메일 Send
    private void sendMail(String userEmail, String newPassword){
        JavaMailSender javaMailSender = getJavaMailSender();
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();

        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true, "UTF-8");

            helper.setSubject("【칼리버스】비밀번호 초기화");

            helper.setFrom(username);

            helper.setTo(userEmail);

            //helper.setCc(ccAddr);

            //        helper.setBcc("14xxxxx098@qq.com");

            helper.setSentDate(new Date());


            helper.setText(mailContent(newPassword),true);

            //첨부파일
            //helper.addAttachment(userNm+"이력서",new File(filePath+fileName));

            javaMailSender.send(mimeMessage);

            log.info("sendMail mail : {}", userEmail);

        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.SENDMAIL_ERROR.getMessage());
        }
    }

    //메일 설정
    private JavaMailSender getJavaMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(host);
        mailSender.setPort(port);
        mailSender.setUsername(username);
        mailSender.setPassword(password);

        // 메일 설정, 프로퍼티 추가
        Properties properties = mailSender.getJavaMailProperties();
        properties.put("mail.transport.protocol", "smtp");
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.ssl.enable", "true");
        properties.put("mail.smtp.socketFactory.port", 465);
        properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        properties.put("mail.smtp.ssl.checkserveridentity", "true");
        properties.put("mail.smtp.socketFactory.fallback", "false");

        return mailSender;
    }

    //메일 내용 form
    private String mailContent(String newPassword){

        String body =
                        "<html>"+
                        "<head>"+
                        "<meta charset='UTF-8'>"+
                        "</head>"+
                        "<body style='font-family:Arial, sans-serif;'>"+
                        "<p>안녕하세요! 비밀번호 초기화가 성공적으로 이루어졌습니다.</p>" +
                        "<p>임시 비밀번호: <strong>"+newPassword+"</strong></p>"+
                        "<p>로그인하여 계정에 액세스한 다음, [비밀번호 변경]을 통해 새 비밀번호를 설정해 주세요.</p>" +
                        "<p>더 많은 질문이나 도움이 필요한 경우, 언제든지 연락해 주세요.</p>" +
                        "</body>" +
                        "</html>";

        return body;
    }
}
