package com.datamate.datamanagement.application;

import com.datamate.common.infrastructure.common.Response;
import com.datamate.datamanagement.infrastructure.client.PdfTextExtractClient;
import com.datamate.datamanagement.infrastructure.client.dto.PdfTextExtractRequest;
import com.datamate.datamanagement.infrastructure.client.dto.PdfTextExtractResponse;
import feign.FeignException;
import feign.Request;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class PdfTextExtractAsyncService {
    private final PdfTextExtractClient pdfTextExtractClient;

    @Async
    public void extractPdfText(String datasetId, String fileId) {
        try {
            Response<PdfTextExtractResponse> response = pdfTextExtractClient.extractPdfText(
                new PdfTextExtractRequest(datasetId, fileId)
            );
            if (response == null) {
                log.warn("PdfTextExtract returned null response, datasetId={}, fileId={}", datasetId, fileId);
                return;
            }
            String code = response.getCode();
            if (!"0".equals(code) && !"200".equals(code)) {
                log.warn(
                    "PdfTextExtract failed, datasetId={}, fileId={}, code={}, message={}",
                    datasetId,
                    fileId,
                    code,
                    response.getMessage()
                );
                return;
            }
            PdfTextExtractResponse data = response.getData();
            if (data != null) {
                log.info(
                    "PdfTextExtract succeeded, datasetId={}, fileId={}, textFileId={}",
                    datasetId,
                    fileId,
                    data.getTextFileId()
                );
            } else {
                log.info("PdfTextExtract succeeded, datasetId={}, fileId={}", datasetId, fileId);
            }
        } catch (FeignException feignException) {
            logFeignException(datasetId, fileId, feignException);
        } catch (Exception e) {
            log.error("PdfTextExtract call failed, datasetId={}, fileId={}", datasetId, fileId, e);
        }
    }

    private void logFeignException(String datasetId, String fileId, FeignException feignException) {
        Request request = feignException.request();
        String httpMethod = request == null || request.httpMethod() == null
            ? "UNKNOWN"
            : request.httpMethod().name();
        String requestUrl = request == null || request.url() == null
            ? "UNKNOWN"
            : request.url();
        String responseBody = resolveFeignResponseBody(feignException);
        String rootCauseChain = buildCauseChain(feignException, 12);
        log.error(
            "PdfTextExtract call failed with FeignException, datasetId={}, fileId={}, status={}, method={}, url={}, responseBody=\n{}\nrootCauseChain={}",
            datasetId,
            fileId,
            feignException.status(),
            httpMethod,
            requestUrl,
            responseBody,
            rootCauseChain,
            feignException
        );
    }

    private String resolveFeignResponseBody(FeignException feignException) {
        String responseBody = feignException.contentUTF8();
        if (responseBody == null || responseBody.isBlank()) {
            responseBody = feignException.getMessage();
        }
        if (responseBody == null || responseBody.isBlank()) {
            return "EMPTY_RESPONSE_BODY";
        }
        return responseBody;
    }

    private String buildCauseChain(Throwable throwable, int maxDepth) {
        StringBuilder causeChain = new StringBuilder();
        Throwable current = throwable;
        int depth = 0;
        while (current != null && depth < maxDepth) {
            if (causeChain.length() > 0) {
                causeChain.append(" <- ");
            }
            causeChain.append(current.getClass().getSimpleName())
                .append(": ")
                .append(normalizeCauseMessage(current.getMessage()));
            current = current.getCause();
            depth++;
        }
        if (current != null) {
            causeChain.append(" <- ...");
        }
        return causeChain.toString();
    }

    private String normalizeCauseMessage(String message) {
        if (message == null || message.isBlank()) {
            return "EMPTY_MESSAGE";
        }
        return message.replace("\r", " ").replace("\n", " ").trim();
    }
}
