package kr.itn.itnhub.system;

import kr.itn.itnhub.mattermost.MattermostClient;
import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import kr.itn.itnhub.stage.StageHistoryMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 시스템관리 &gt; 채널 초기화. 웹앱이 만든 Mattermost 채널을 보관 처리하고, 기관의 채널 상태를
 * 비워 채널관리에서 처음부터 다시 만들 수 있게 되돌린다.
 *
 * <p><b>대상은 웹앱 DB가 아는 채널뿐이다.</b> organization.channel_id_mj / channel_id_law에
 * 들어 있는 ID만 건드린다. Mattermost 팀에는 엑셀 VBA(M07)로 만든 예전 채널과 공용 채널
 * (Town Square, FAQ, 대쉬보드, 양식 모음, 자료실)이 함께 있는데, 이름으로 훑어서 지우면
 * 그것들까지 날아간다. 그래서 이름 검색 경로는 의도적으로 만들지 않았다.</p>
 *
 * <p><b>사용자 계정은 건드리지 않는다.</b> 웹앱은 만든 계정을 기록하지 않고 이메일로 조회해
 * 없을 때만 만드는 구조라, 원래 있던 실제 직원 계정과 구분할 방법이 없다. 계정까지 정리하려면
 * 생성 출처를 DB에 남기는 작업이 먼저다.</p>
 *
 * <p>보관은 Mattermost의 아카이브다(영구 삭제 아님). 게시글·파일은 남고 시스템 콘솔에서
 * 복구할 수 있다. 권리확인/권리처리·업무메모·담당자 배정 등 DB 데이터도 그대로 둔다.</p>
 */
@Service
public class SystemResetService {

    private static final Logger log = LoggerFactory.getLogger(SystemResetService.class);

    static final String KIND_MJ = "문정원";
    static final String KIND_LAW = "법률검토";

    private final OrganizationMapper orgMapper;
    private final StageHistoryMapper stageHistoryMapper;
    private final MattermostClient mattermost;

    public SystemResetService(OrganizationMapper orgMapper, StageHistoryMapper stageHistoryMapper,
                              MattermostClient mattermost) {
        this.orgMapper = orgMapper;
        this.stageHistoryMapper = stageHistoryMapper;
        this.mattermost = mattermost;
    }

    /** 초기화하면 무엇이 사라지는지 미리 보여준다. 아무것도 바꾸지 않는다. */
    public List<ResetTarget> preview() {
        List<ResetTarget> targets = new ArrayList<>();
        for (Organization org : orgMapper.findAll()) {
            List<ResetTarget.ResetChannel> channels = channelsOf(org);
            if (channels.isEmpty()) {
                continue;
            }
            targets.add(new ResetTarget(org.getId(), org.getOrgNo(), org.getOrgName(),
                    org.getStage(), channels));
        }
        return targets;
    }

    /**
     * 실제 초기화. 채널 하나가 실패해도 나머지는 계속한다 - 이미 지워진 채널이나 권한 문제로
     * 전체가 멈추면, 절반만 정리된 상태에서 다시 실행할 방법이 없어지기 때문이다.
     *
     * <p>DB 정리는 <b>그 기관의 채널을 모두 보관한 뒤에만</b> 한다. 한쪽만 보관된 상태에서
     * ID를 지워버리면 남은 채널을 다시 찾을 방법이 사라진다.</p>
     */
    public ResetReport reset() {
        int archived = 0;
        int cleared = 0;
        List<String> failures = new ArrayList<>();

        for (ResetTarget target : preview()) {
            boolean allArchived = true;

            for (ResetTarget.ResetChannel channel : target.channels()) {
                try {
                    mattermost.archiveChannel(channel.channelId());
                    archived++;
                } catch (RuntimeException e) {
                    allArchived = false;
                    failures.add("%s %s (%s): %s".formatted(
                            target.orgNo(), target.orgName(), channel.kind(), e.getMessage()));
                    log.warn("채널 보관 실패 org={} kind={}", target.orgNo(), channel.kind(), e);
                }
            }

            if (allArchived) {
                orgMapper.clearChannels(target.orgId());
                stageHistoryMapper.deleteByOrg(target.orgId());
                cleared++;
            }
        }

        log.info("채널 초기화 완료: 보관 {}개, 기관 {}곳 초기화, 실패 {}건", archived, cleared, failures.size());
        return new ResetReport(archived, cleared, failures);
    }

    private List<ResetTarget.ResetChannel> channelsOf(Organization org) {
        List<ResetTarget.ResetChannel> channels = new ArrayList<>(2);
        if (hasText(org.getChannelIdMj())) {
            channels.add(new ResetTarget.ResetChannel(KIND_MJ, org.getChannelIdMj()));
        }
        if (hasText(org.getChannelIdLaw())) {
            channels.add(new ResetTarget.ResetChannel(KIND_LAW, org.getChannelIdLaw()));
        }
        return channels;
    }

    private boolean hasText(String value) {
        return value != null && !value.isBlank();
    }
}
