package com.sino.mci.inbound.engine;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.admin.entity.Tenant;
import com.sino.mci.admin.repository.TenantMapper;
import com.sino.mci.exception.WebhookPushException;
import com.sino.mci.inbound.entity.MessageEvent;
import com.sino.mci.inbound.intent.IntentResult;
import com.sino.mci.inbound.webhook.WebhookPayloadBuilder;
import com.sino.mci.inbound.webhook.WebhookSignature;
import com.sino.mci.inbound.webhook.WebhookSignatureService;
import com.sino.mci.shared.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class WebhookPushService {

    private static final String AUTH_CONFIG_WEBHOOK_SECRET = "webhook_secret";
    private static final String AUTH_CONFIG_SIGN_ALGORITHM = "sign_algorithm";

    private final TenantMapper tenantMapper;
    private final RestTemplate restTemplate;
    private final WebhookPayloadBuilder payloadBuilder;
    private final WebhookSignatureService signatureService;

    public void pushToBusiness(String tenantCode, MessageEvent event, IntentResult intent) {
        pushToBusiness(tenantCode, event, intent, null);
    }

    public void pushToBusiness(String tenantCode, MessageEvent event, IntentResult intent, String customUrl) {
        Tenant tenant = loadTenant(tenantCode);
        String webhookUrl = resolveWebhookUrl(tenant, customUrl);

        Map<String, Object> authConfig = parseAuthConfig(tenant.getAuthConfig());
        String secret = getString(authConfig, AUTH_CONFIG_WEBHOOK_SECRET);
        if (!StringUtils.hasText(secret)) {
            throw new WebhookPushException("Tenant " + tenantCode + " has no webhook_secret configured");
        }
        String algorithm = getString(authConfig, AUTH_CONFIG_SIGN_ALGORITHM);
        if (!StringUtils.hasText(algorithm)) {
            algorithm = "hmac-sha256";
        }

        Map<String, Object> payload = payloadBuilder.buildPayload(event, intent);
        String payloadJson = JsonUtils.toJson(payload);
        if (payloadJson == null) {
            throw new WebhookPushException("Failed to serialize webhook payload for tenant " + tenantCode);
        }

        WebhookSignature signature = signatureService.sign(tenantCode, payloadJson, secret, algorithm);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("X-Msg-Platform-Signature", signature.signature());
        headers.set("X-Msg-Platform-Timestamp", String.valueOf(signature.timestamp()));
        headers.set("X-Msg-Platform-Tenant", tenantCode);

        HttpEntity<String> entity = new HttpEntity<>(payloadJson, headers);
        try {
            ResponseEntity<String> response = restTemplate.exchange(
                    webhookUrl, HttpMethod.POST, entity, String.class);
            if (!response.getStatusCode().is2xxSuccessful()) {
                throw new WebhookPushException("Webhook push failed with status " + response.getStatusCode()
                        + " for tenant " + tenantCode);
            }
            log.info("Webhook pushed to {}, status={}, tenant={}", webhookUrl, response.getStatusCode(), tenantCode);
        } catch (RestClientException e) {
            throw new WebhookPushException("Webhook push failed for tenant " + tenantCode + ": " + e.getMessage(), e);
        }
    }

    private Tenant loadTenant(String tenantCode) {
        Tenant tenant = tenantMapper.selectOne(
                new LambdaQueryWrapper<Tenant>().eq(Tenant::getTenantCode, tenantCode));
        if (tenant == null) {
            throw new WebhookPushException("Tenant not found for webhook push: " + tenantCode);
        }
        return tenant;
    }

    private String resolveWebhookUrl(Tenant tenant, String customUrl) {
        if (StringUtils.hasText(customUrl)) {
            return customUrl;
        }
        String webhookUrl = tenant.getInboundWebhookUrl();
        if (!StringUtils.hasText(webhookUrl)) {
            throw new WebhookPushException("Tenant " + tenant.getTenantCode() + " has no inbound webhook URL configured");
        }
        return webhookUrl;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> parseAuthConfig(String authConfigJson) {
        if (authConfigJson == null || authConfigJson.isBlank()) {
            return Map.of();
        }
        Map<String, Object> config = JsonUtils.fromJson(authConfigJson, Map.class);
        return config != null ? config : Map.of();
    }

    private String getString(Map<String, Object> config, String key) {
        Object value = config.get(key);
        return value != null ? value.toString() : null;
    }
}
