package com.kospo.talk.service.impl;

import com.kospo.talk.config.exception.ErrorCode;
import com.kospo.talk.config.utils.CommonUtils;
import com.kospo.talk.exception.CustomException;
import com.kospo.talk.model.Manual;
import com.kospo.talk.model.ManualId;
import com.kospo.talk.model.VManual;
import com.kospo.talk.payload.ISmUserDto;
import com.kospo.talk.payload.ManualDto;
import com.kospo.talk.payload.VManualDto;
import com.kospo.talk.repository.ManualRepository;
import com.kospo.talk.repository.SmUserRepository;
import com.kospo.talk.repository.VManualRepository;
import com.kospo.talk.service.ManualService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;

import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ManualServiceImpl implements ManualService {
    @Value("${server.manual}")
    private String manualPath;
    private final ManualRepository manualRepository;
    private final VManualRepository vManualRepository;
    private final SmUserRepository smUserRepository;

    @Override
    public List<VManualDto> introList(String workId) {
        return vManualRepository.findByImageAndVideo(workId).stream().map(VManualDto::to).toList();
    }

    @Override
    public List<ManualDto> findDoc(String workId) {
        return manualRepository.findByDoc(workId).stream().map(ManualDto::to).toList();
    }

    @Override
    public List<VManualDto> findVideo(String search, String workId) {
        return vManualRepository.findVideoByOnmAndWorkId(search, workId).stream().map(VManualDto::to).toList();
    }

    @Override
    public List<VManualDto> findVideoSm(String workId, String sabun) {
        return vManualRepository.findBySabunAndWorkIdAndDeleteYn(workId, sabun,false).stream().map(VManualDto::to).toList();
    }

    @Override
    public List<ManualDto> findDocSearch(String search, String workId, String type) {
        return manualRepository.findDocByOnmAndWorkId(search, workId, type).stream().map(ManualDto::to).toList();
    }

    @Override
    public List<ManualDto> findDocSearchPerson(String sabun, String type) {
        return manualRepository.findDocBySabunAndType(sabun, type).stream().map(ManualDto::to).toList();
    }

    @Override
    public byte[] loadImage(ManualId id) {
        Manual att = manualRepository.findById(id).get();
        File inputFile = new File(att.getPath(), att.getId().getVnm());
        BufferedImage originalImage = null;
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(inputFile);

            return fis.readAllBytes();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public HashMap<String, Object> loadOoc(ManualId id) {
        Manual att = manualRepository.findById(id).get();
        File inputFile = new File(att.getPath(), att.getId().getVnm());
        BufferedImage originalImage = null;
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(inputFile);
            HashMap<String, Object> map = new HashMap<>();
            map.put("onm", att.getOnm());
            map.put("data", fis.readAllBytes());
            return map;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public FileSystemResource loadVideo(int id, String type) throws IOException {
        VManual att = vManualRepository.findById(id).get();
        Path path = null;
        if(type.equals("S")) {
            path = Paths.get(att.getPath() + "/" + att.getSvnm());
        } else {
            path = Paths.get(att.getPath() + "/" + att.getVnm());
        }

        FileSystemResource resource = new FileSystemResource(path);
//        InputStream inputStream = Files.newInputStream(path);
//        return new InputStreamResource(inputStream);
        return resource;
    }

    @Transactional
    @Override
    public void save(ManualDto manualDto) {
        if(!CommonUtils.checkAllowExt(manualDto.getOnm())) {
            throw new CustomException(ErrorCode.INVALID_FILE);
        }
        if(manualDto.getType().equals("0000")) {
            manualRepository.updateByWorkIdAndType(manualDto.getWorkId(), manualDto.getType());
        }

        Manual getManual = manualRepository.findByWorkIdAndOnm(manualDto.getWorkId(), manualDto.getOnm(), manualDto.getVersion());
        String vnm = "";
        if(getManual == null) {
            vnm = CommonUtils.generateFileName();
            manualRepository.save(
                    Manual.builder()
                            .id(
                                    ManualId.builder()
                                            .workId(manualDto.getWorkId())
                                            .vnm(vnm).build()
                            )
                            .version(manualDto.getVersion())
                            .type(manualDto.getType())
                            .onm(manualDto.getOnm())
                            .path(manualPath)
                            .deleteYn(false)
                            .size(manualDto.getData().length)
                            .insDate(LocalDateTime.now()).build()
            );
        } else {
            vnm = getManual.getId().getVnm();
        }

        CommonUtils.uploadAndResume(manualPath, vnm, manualDto.getData());
    }

    @Transactional
    @Override
    public void videoSave(VManualDto vManualDto) {
        VManual getManual = vManualRepository.findByWorkIdAndOnm(vManualDto.getWorkId(), vManualDto.getOnm());
        String vnm = "";
        String svnm = "";
        if(getManual == null) {
            vnm = CommonUtils.generateFileName();
            svnm = CommonUtils.generateFileName();
            vManualRepository.save(VManual.builder()
                    .workId(vManualDto.getWorkId())
                    .sonm(vManualDto.getSonm())
                    .svnm(svnm)
                    .onm(vManualDto.getOnm())
                    .vnm(vnm)
                    .path(manualPath)
                    .deleteYn(false)
                    .size(vManualDto.getData().length)
                    .insDate(LocalDateTime.now()).build());
        } else {
            vnm = getManual.getVnm();
            svnm = getManual.getSvnm();
        }
        CommonUtils.uploadAndResume(manualPath, vnm, vManualDto.getData());
        if(vManualDto.getSdata().length > 0) {
            CommonUtils.uploadAndResume(manualPath, svnm, vManualDto.getSdata());
        }



    }

    @Override
    public String videoDelete(String sabun, int id) {
        String result = "";
        VManual vManual = vManualRepository.findById(id).get();
        List<ISmUserDto> smLists = smUserRepository.findBySmUserInSabun(sabun);
        List<ISmUserDto> checkers = smLists.stream().filter((sm) -> sm.getWorkId().equals(vManual.getWorkId())).toList();
        if(checkers.size() != 0) {
            vManual.setDeleteYn(true);
            vManualRepository.save(vManual);
            result = "success";
        } else {
            result = "fail";
        }

        return result;
    }

    @Override
    public void update(ManualDto manualDto) {
        if(!CommonUtils.checkAllowExt(manualDto.getOnm())) {
            throw new CustomException(ErrorCode.INVALID_FILE);
        }
//        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Manual manual = manualRepository.findByIdWorkIdAndVersionAndType(manualDto.getWorkId(), manualDto.getVersion(), manualDto.getType());

        if(manualDto.getData() != null && !manualDto.getData().equals("")) {
            /*파일 새로 갱신시 기존 메뉴얼 삭제 (파일은 삭제 안함)*/
            manualRepository.delete(manual);
            String vnm = fileUpload(manualDto);
            manual.setId(ManualId.builder().workId(manualDto.getWorkId()).vnm(vnm).build());
            manual.setOnm(manualDto.getOnm());
        }

        manualRepository.save(manual);
    }

    @Override
    public void delete(String workId, String vnm) {
        Manual manual = manualRepository.findById(ManualId.builder()
                .workId(workId)
                .vnm(vnm).build()
        ).get();
        manualRepository.delete(manual);
    }


    public String fileUpload(ManualDto md) {
//        String[] parts = md.getData().split(",");
//        String fileString = parts.length > 1 ? parts[1] : parts[0];
        String vnm = CommonUtils.generateFileName();
        try {
            try (FileOutputStream fos = new FileOutputStream(new File(manualPath, vnm))) {
                fos.write(md.getData());
            }
        } catch(IOException e) {
            throw new RuntimeException(e);
        }
        return vnm;
    }


    public String videoShortUpload(VManualDto md) {
//        String[] parts = md.getData().split(",");
//        String fileString = parts.length > 1 ? parts[1] : parts[0];
        String vnm = CommonUtils.generateFileName();
        try {
            try (FileOutputStream fos = new FileOutputStream(new File(manualPath, vnm))) {
                fos.write(md.getSdata());
            }
        } catch(IOException e) {
            throw new RuntimeException(e);
        }
        return vnm;
    }


    public String videoUpload(VManualDto md) {
//        String[] parts = md.getData().split(",");
//        String fileString = parts.length > 1 ? parts[1] : parts[0];
        String vnm = CommonUtils.generateFileName();
        try {
            try (FileOutputStream fos = new FileOutputStream(new File(manualPath, vnm))) {
                fos.write(md.getData());
            }
        } catch(IOException e) {
            throw new RuntimeException(e);
        }
        return vnm;
    }
}
