package com.ga.external.push;

import com.fasterxml.jackson.databind.JsonNode;
import com.ga.common.adapter.push.PushAdapter;
import com.ga.common.adapter.push.PushRequest;
import com.ga.common.adapter.push.PushResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Firebase Cloud Messaging(FCM) HTTP v1 어댑터 — 실구현.
 * <p>
 * 활성 조건: <code>app.adapter.push=fcm</code> (기본은 mock). 키는 {@link FcmProperties} 로 외부화.
 * <code>POST /v1/projects/{projectId}/messages:send</code> 에 Bearer 토큰으로 발송.
 * 응답의 <code>name</code>(projects/{id}/messages/{msgId})을 messageId 로 반환.
 * 필수 설정 누락 시 fail-fast. 외부호출 감사로그는 ga-api 의 AOP가 남긴다.
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "app.adapter", name = "push", havingValue = "fcm")
@EnableConfigurationProperties(FcmProperties.class)
public class FcmPushAdapter implements PushAdapter {

    private final FcmProperties props;
    private final RestClient client;

    public FcmPushAdapter(FcmProperties props) {
        this.props = props;
        this.client = RestClient.builder().baseUrl(props.getBaseUrl()).build();
    }

    @Override
    public PushResponse send(PushRequest request) {
        require(props.getProjectId(), "app.adapter.fcm.project-id");
        require(props.getAccessToken(), "app.adapter.fcm.access-token");
        require(request.getDeviceToken(), "deviceToken");

        Map<String, Object> notification = new LinkedHashMap<>();
        notification.put("title", request.getTitle() == null ? "" : request.getTitle());
        notification.put("body", request.getBody() == null ? "" : request.getBody());

        Map<String, Object> data = new LinkedHashMap<>();
        if (request.getRefType() != null) data.put("refType", request.getRefType());
        if (request.getRefId() != null) data.put("refId", String.valueOf(request.getRefId()));

        Map<String, Object> message = new LinkedHashMap<>();
        message.put("token", request.getDeviceToken());
        message.put("notification", notification);
        if (!data.isEmpty()) message.put("data", data);

        Map<String, Object> body = Map.of("message", message);

        try {
            JsonNode res = client.post()
                    .uri("/v1/projects/{projectId}/messages:send", props.getProjectId())
                    .header("Authorization", "Bearer " + props.getAccessToken())
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(body)
                    .retrieve()
                    .body(JsonNode.class);
            String name = res == null ? "" : res.path("name").asText("");
            boolean ok = StringUtils.hasText(name);
            return PushResponse.builder()
                    .messageId(name)
                    .sent(ok)
                    .resultCode(ok ? "0000" : "ERROR")
                    .resultMessage(ok ? "sent" : "no message name")
                    .sentAt(LocalDateTime.now())
                    .build();
        } catch (RestClientException e) {
            log.warn("[FCM] 발송 실패 refType={} refId={} err={}",
                    request.getRefType(), request.getRefId(), e.getMessage());
            return PushResponse.builder()
                    .sent(false).resultCode("ERROR").resultMessage(e.getMessage())
                    .sentAt(LocalDateTime.now()).build();
        }
    }

    private void require(String value, String key) {
        if (!StringUtils.hasText(value)) {
            throw new IllegalStateException("FCM 설정 누락: " + key + " 가 필요합니다.");
        }
    }
}
