package kz.konturai.parser.service;

import com.fasterxml.jackson.databind.JsonNode;
import kz.konturai.parser.config.FacebookLeadCallCenterProperties;
import kz.konturai.parser.model.FacebookLead;
import kz.konturai.parser.repository.FacebookLeadRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;

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

@Service
public class FacebookLeadCallCenterSyncService {

    private static final List<String> RETRYABLE_SYNC_STATUSES = List.of("PENDING", "FAILED");
    private static final Logger log = LoggerFactory.getLogger(FacebookLeadCallCenterSyncService.class);

    private final FacebookLeadCallCenterProperties properties;
    private final FacebookLeadRepository facebookLeadRepository;
    private final WebClient.Builder webClientBuilder;

    public FacebookLeadCallCenterSyncService(
            FacebookLeadCallCenterProperties properties,
            FacebookLeadRepository facebookLeadRepository,
            WebClient.Builder webClientBuilder) {
        this.properties = properties;
        this.facebookLeadRepository = facebookLeadRepository;
        this.webClientBuilder = webClientBuilder;
    }

    public void syncPendingLeads() {
        if (!properties.isEnabled()) {
            return;
        }

        List<FacebookLead> pendingLeads =
                facebookLeadRepository.findByCallCenterSyncStatusInOrCallCenterSyncStatusIsNull(RETRYABLE_SYNC_STATUSES);
        if (pendingLeads.isEmpty()) {
            log.info("No pending Facebook leads for call-center sync");
            return;
        }

        log.info("Retrying call-center sync for {} Facebook lead(s)", pendingLeads.size());
        for (FacebookLead lead : pendingLeads) {
            syncLead(lead);
        }
    }

    public void syncLead(FacebookLead lead) {
        if (lead == null) {
            return;
        }

        if (!properties.isEnabled()) {
            log.info("Call-center sync is disabled. Lead {} remains only in marketing-parser DB",
                    lead.getExternalCommentId());
            return;
        }

        if (!StringUtils.hasText(properties.getBaseUrl())) {
            log.warn("Call-center sync skipped for lead {}: baseUrl is not configured", lead.getExternalCommentId());
            return;
        }

        FacebookLead managedLead = loadManagedLead(lead);
        try {
            Map<String, String> authHeaders = resolveAuthHeaders();

            if (!StringUtils.hasText(managedLead.getCallCenterCustomerId())) {
                String customerId = createCustomer(managedLead, authHeaders);
                managedLead.setCallCenterCustomerId(customerId);
                managedLead.setCallCenterSyncStatus("CUSTOMER_CREATED");
                managedLead.setCallCenterSyncError(null);
                managedLead = facebookLeadRepository.save(managedLead);
                log.info("Facebook lead {} linked to call-center customer {}",
                        managedLead.getExternalCommentId(), customerId);
            }

            if (!StringUtils.hasText(managedLead.getCallCenterInteractionId())) {
                String interactionId = createInteraction(managedLead, authHeaders);
                managedLead.setCallCenterInteractionId(interactionId);
                log.info("Facebook lead {} linked to call-center interaction {}",
                        managedLead.getExternalCommentId(), interactionId);
            }

            managedLead.setCallCenterSyncStatus("SYNCED");
            managedLead.setCallCenterSyncError(null);
            managedLead.setCallCenterSyncedAt(LocalDateTime.now());
            facebookLeadRepository.save(managedLead);

            log.info("Facebook lead {} successfully synced to call-center",
                    managedLead.getExternalCommentId());
        } catch (Exception e) {
            managedLead.setCallCenterSyncStatus("FAILED");
            managedLead.setCallCenterSyncError(shorten(e.getMessage()));
            facebookLeadRepository.save(managedLead);
            log.error("Failed to sync Facebook lead {} to call-center",
                    managedLead.getExternalCommentId(), e);
        }
    }

    private FacebookLead loadManagedLead(FacebookLead lead) {
        if (StringUtils.hasText(lead.getId())) {
            return facebookLeadRepository.findById(lead.getId()).orElse(lead);
        }
        if (StringUtils.hasText(lead.getExternalCommentId())) {
            return facebookLeadRepository.findByExternalCommentId(lead.getExternalCommentId()).orElse(lead);
        }
        return lead;
    }

    private Map<String, String> resolveAuthHeaders() {
        if (StringUtils.hasText(properties.getAuthUsername()) && StringUtils.hasText(properties.getAuthPassword())) {
            try {
                String accessToken = loginAndGetAccessToken();
                return Map.of("Authorization", "Bearer " + accessToken);
            } catch (Exception e) {
                log.warn("Call-center bearer auth failed. Falling back to legacy headers. message={}",
                        shorten(e.getMessage()));
            }
        }

        Map<String, String> headers = new LinkedHashMap<>();
        headers.put("X-User", properties.getActorUser());
        headers.put("X-Role", properties.getActorRole());
        return headers;
    }

    private String loginAndGetAccessToken() {
        Map<String, Object> payload = Map.of(
                "username", properties.getAuthUsername(),
                "password", properties.getAuthPassword()
        );

        try {
            JsonNode response = callCenterClient().post()
                    .uri(properties.getAuthLoginPath())
                    .bodyValue(payload)
                    .retrieve()
                    .bodyToMono(JsonNode.class)
                    .block(Duration.ofMillis(properties.getTimeoutMs()));

            String accessToken = response != null ? response.path("access_token").asText(null) : null;
            if (!StringUtils.hasText(accessToken)) {
                throw new IllegalStateException("Call-center auth response does not contain access_token");
            }
            return accessToken;
        } catch (WebClientResponseException e) {
            log.error("Call-center auth login failed. status={}, body={}",
                    e.getStatusCode().value(), e.getResponseBodyAsString());
            throw e;
        }
    }

    private String createCustomer(FacebookLead lead, Map<String, String> authHeaders) {
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("display_name", customerDisplayName(lead));
        payload.put("phones", List.of());
        payload.put("preferred_phone", null);
        payload.put("tags", List.of("facebook", "hot-lead"));

        try {
            WebClient.RequestBodySpec request = callCenterClient().post()
                    .uri(properties.getCustomerPath());
            request = applyHeaders(request, authHeaders);

            JsonNode response = request
                    .bodyValue(payload)
                    .retrieve()
                    .bodyToMono(JsonNode.class)
                    .block(Duration.ofMillis(properties.getTimeoutMs()));

            String customerId = response != null ? response.path("customer_id").asText(null) : null;
            if (!StringUtils.hasText(customerId)) {
                throw new IllegalStateException("Call-center customer response does not contain customer_id");
            }
            return customerId;
        } catch (WebClientResponseException e) {
            log.error("Call-center customer creation failed. status={}, body={}",
                    e.getStatusCode().value(), e.getResponseBodyAsString());
            throw e;
        }
    }

    private String createInteraction(FacebookLead lead, Map<String, String> authHeaders) {
        if (!StringUtils.hasText(lead.getCallCenterCustomerId())) {
            throw new IllegalStateException("Call-center customer must be created before interaction");
        }

        Map<String, Object> payload = Map.of(
                "channel", properties.getInteractionChannel(),
                "subject", interactionSubject(lead),
                "customer_id", lead.getCallCenterCustomerId(),
                "queue_id", properties.getInteractionQueueId(),
                "priority", 3
        );

        try {
            WebClient.RequestBodySpec request = callCenterClient().post()
                    .uri(properties.getInteractionPath());
            request = applyHeaders(request, authHeaders);

            JsonNode response = request
                    .bodyValue(payload)
                    .retrieve()
                    .bodyToMono(JsonNode.class)
                    .block(Duration.ofMillis(properties.getTimeoutMs()));

            String interactionId = response != null ? response.path("interaction_id").asText(null) : null;
            if (!StringUtils.hasText(interactionId)) {
                throw new IllegalStateException("Call-center interaction response does not contain interaction_id");
            }
            return interactionId;
        } catch (WebClientResponseException e) {
            log.error("Call-center interaction creation failed. status={}, body={}",
                    e.getStatusCode().value(), e.getResponseBodyAsString());
            throw e;
        }
    }

    private WebClient callCenterClient() {
        return webClientBuilder
                .baseUrl(properties.getBaseUrl())
                .build();
    }

    private WebClient.RequestBodySpec applyHeaders(
            WebClient.RequestBodySpec request,
            Map<String, String> headers) {
        WebClient.RequestBodySpec current = request;
        for (Map.Entry<String, String> header : headers.entrySet()) {
            if (StringUtils.hasText(header.getValue())) {
                current = current.header(header.getKey(), header.getValue());
            }
        }
        return current;
    }

    private String customerDisplayName(FacebookLead lead) {
        String authorName = StringUtils.hasText(lead.getAuthorName())
                ? lead.getAuthorName().trim()
                : "Facebook lead";
        if (authorName.length() >= 2) {
            return authorName;
        }
        return "Facebook lead " + lead.getExternalCommentId();
    }

    private String interactionSubject(FacebookLead lead) {
        String prefix = StringUtils.hasText(properties.getSubjectPrefix())
                ? properties.getSubjectPrefix().trim()
                : "[Facebook]";
        String message = StringUtils.hasText(lead.getMessage())
                ? lead.getMessage().trim()
                : "Новый горячий лид из комментария Facebook";
        String subject = prefix + " " + message;
        return subject.length() <= 255 ? subject : subject.substring(0, 252) + "...";
    }

    private String shorten(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        String normalized = value.trim().replaceAll("\\s+", " ");
        return normalized.length() <= 500 ? normalized : normalized.substring(0, 497) + "...";
    }
}
