package kr.itn.itnhub.mattermost;

import com.fasterxml.jackson.databind.JsonNode;
import kr.itn.itnhub.config.MattermostProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

@Component
public class MattermostRestClient implements MattermostClient {

    private final RestClient rest;
    private final String teamId;

    @Autowired
    public MattermostRestClient(MattermostProperties props) {
        this(props, RestClient.builder());
    }

    /**
     * 전송 계층(transport)을 직접 지정할 수 있는 생성자. 운영 코드에서는 사용하지 않으며,
     * 테스트 등 별도의 {@link RestClient.Builder} 설정이 필요한 호출자를 위한 확장 지점이다.
     */
    MattermostRestClient(MattermostProperties props, RestClient.Builder builder) {
        this.teamId = props.teamId();
        this.rest = builder
                .baseUrl(props.baseUrl())
                .defaultHeader("Authorization", "Bearer " + props.token())
                .build();
    }

    @Override
    public Optional<String> findChannelIdByInternalName(String internalName) {
        try {
            JsonNode body = rest.get()
                    .uri("/api/v4/teams/{teamId}/channels/name/{name}", teamId, internalName)
                    .retrieve()
                    .onStatus(status -> status.value() == 404, (req, res) -> {
                        throw new NotFound();
                    })
                    .body(JsonNode.class);

            return idOf(body);
        } catch (NotFound e) {
            return Optional.empty();
        } catch (RestClientException e) {
            throw new MattermostException("채널 조회 실패: " + internalName, e);
        }
    }

    /** 팀 채널 목록 조회 시 한 페이지에 요청하는 최대 개수. */
    private static final int DISPLAY_NAME_PAGE_SIZE = 200;

    /**
     * 무한 루프 방지용 페이지 상한. VBA 원본의 안전장치를 그대로 옮긴 것으로,
     * 정상적인 팀이라면 도달할 일이 없다.
     */
    private static final int DISPLAY_NAME_MAX_PAGES = 100;

    /**
     * 표시명으로 채널을 찾는다. {@code /users/me/teams/{teamId}/channels}는 토큰 사용자가
     * "가입한" 채널만 돌려주므로 쓰지 않는다 - 이 조회는 레거시 채널(현재 내부명 규칙 이전에
     * 만들어져 내부명으로는 못 찾는 채널)을 복구하기 위한 것인데, 토큰 사용자가 그 채널에
     * 가입해 있지 않으면 조회가 누락되어 중복 채널이 생성된다. 대신 팀 전체 채널을
     * {@code /teams/{teamId}/channels}로 페이지네이션하며 훑는다 - 이 엔드포인트는 비공개
     * 채널까지 보려면 PAT가 system-admin 권한이어야 한다(README 참고).
     */
    @Override
    public Optional<String> findChannelIdByDisplayName(String displayName) {
        try {
            for (int page = 0; page < DISPLAY_NAME_MAX_PAGES; page++) {
                int currentPage = page;
                JsonNode channels = rest.get()
                        .uri(uri -> uri.path("/api/v4/teams/{teamId}/channels")
                                .queryParam("page", currentPage)
                                .queryParam("per_page", DISPLAY_NAME_PAGE_SIZE)
                                .build(teamId))
                        .retrieve()
                        .body(JsonNode.class);

                if (channels == null || !channels.isArray()) {
                    return Optional.empty();
                }

                for (JsonNode channel : channels) {
                    if (displayName.equals(channel.path("display_name").asText())) {
                        return idOf(channel);
                    }
                }

                if (channels.size() < DISPLAY_NAME_PAGE_SIZE) {
                    return Optional.empty();
                }
            }
            throw new MattermostException(
                    "채널 목록 조회 페이지 한도(" + DISPLAY_NAME_MAX_PAGES + ") 초과: " + displayName);
        } catch (RestClientException e) {
            throw new MattermostException("채널 목록 조회 실패", e);
        }
    }

    @Override
    public String createPrivateChannel(String internalName, String displayName) {
        Map<String, String> body = new LinkedHashMap<>();
        body.put("team_id", teamId);
        body.put("name", internalName);
        body.put("display_name", displayName);
        body.put("type", "P");
        body.put("purpose", "");
        body.put("header", "");

        try {
            JsonNode created = rest.post()
                    .uri("/api/v4/channels")
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(body)
                    .retrieve()
                    .body(JsonNode.class);

            return idOf(created).orElseThrow(() ->
                    new MattermostException("채널 생성 응답에 id가 없습니다: " + displayName));
        } catch (RestClientException e) {
            throw new MattermostException("채널 생성 실패: " + displayName, e);
        }
    }

    private Optional<String> idOf(JsonNode node) {
        if (node == null) {
            return Optional.empty();
        }
        String id = node.path("id").asText("");
        return id.isBlank() ? Optional.empty() : Optional.of(id);
    }

    /** 404를 정상 흐름으로 되돌리기 위한 내부 신호. 밖으로 새지 않는다. */
    private static final class NotFound extends RuntimeException {
    }
}
