package kz.konturai.parser.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;

@Service
public class InstagramPostingService {

    private static final Logger logger = LoggerFactory.getLogger(InstagramPostingService.class);
    private static final String FACEBOOK_GRAPH_API_BASE = "https://graph.facebook.com/v18.0";

    private final WebClient webClient;
    private final ObjectMapper objectMapper;
    private final ImageStorageService imageStorageService;

    @Value("${facebook.api.timeout:30000}")
    private int timeoutMs;

    public InstagramPostingService(ImageStorageService imageStorageService) {
        HttpClient httpClient = HttpClient.create()
                .responseTimeout(Duration.ofMillis(30000));

        this.webClient = WebClient.builder()
                .baseUrl(FACEBOOK_GRAPH_API_BASE)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();

        this.objectMapper = new ObjectMapper();
        this.imageStorageService = imageStorageService;
    }

    /**
     * Publishes an image post to Instagram Business account via Graph API.
     * 
     * This method implements the 3-step Instagram publishing workflow:
     * 1. Get Instagram Business Account ID from Facebook Page
     * 2. Create Media Container with image URL
     * 3. Publish the media container
     * 
     * @param pageAccessToken Facebook Page Access Token (must have instagram_basic
     *                        permission)
     * @param caption         The caption for the Instagram post
     * @param imageBytes      The image data as byte array
     * @return ID of the published Instagram post
     * @throws RuntimeException if posting fails at any step
     */
    public String postToInstagram(String pageAccessToken, String caption, byte[] imageBytes) {
        try {
            // Step 1: Get Instagram Business Account ID
            String igUserId = getInstagramBusinessAccountId(pageAccessToken);
            logger.info("Retrieved Instagram Business Account ID: {}", igUserId);

            // Upload image to storage and get public URL
            String imageUrl = imageStorageService.uploadImage(imageBytes, "instagram-image.jpg");
            logger.info("Image uploaded to storage, URL: {}", imageUrl);

            // Step 2: Create Media Container
            String creationId = createMediaContainer(igUserId, imageUrl, caption, pageAccessToken);
            logger.info("Media container created with ID: {}", creationId);

            // Step 3: Publish Media
            String postId = publishMedia(igUserId, creationId, pageAccessToken);
            logger.info("Successfully posted to Instagram. Post ID: {}", postId);

            return postId;

        } catch (WebClientResponseException e) {
            logger.error("Instagram API error: {} - {}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new RuntimeException("Failed to post to Instagram: " + e.getMessage(), e);
        } catch (Exception e) {
            logger.error("Unexpected error posting to Instagram", e);
            throw new RuntimeException("Failed to post to Instagram", e);
        }
    }

    /**
     * Step 1: Gets the Instagram Business Account ID linked to the Facebook Page.
     * 
     * @param pageAccessToken Facebook Page Access Token
     * @return Instagram Business Account ID
     * @throws RuntimeException if the account ID cannot be retrieved
     */
    private String getInstagramBusinessAccountId(String pageAccessToken) {
        try {
            String response = webClient.get()
                    .uri(uriBuilder -> uriBuilder
                            .path("/me")
                            .queryParam("access_token", pageAccessToken)
                            .queryParam("fields", "instagram_business_account")
                            .build())
                    .retrieve()
                    .bodyToMono(String.class)
                    .block(Duration.ofMillis(timeoutMs));

            JsonNode root = objectMapper.readTree(response);
            JsonNode instagramAccount = root.get("instagram_business_account");

            if (instagramAccount == null || !instagramAccount.has("id")) {
                throw new RuntimeException(
                        "Instagram Business Account is not linked to this Facebook Page. " +
                                "Please link your Instagram Business account to the Facebook Page first.");
            }

            return instagramAccount.get("id").asText();

        } catch (WebClientResponseException e) {
            String responseBody = e.getResponseBodyAsString();
            logger.error("Failed to get Instagram Business Account ID: {} - {}",
                    e.getStatusCode(), responseBody != null ? responseBody : "No response body");
            throw new RuntimeException("Failed to get Instagram Business Account ID: " + e.getMessage(), e);
        } catch (JsonProcessingException e) {
            logger.error("Failed to parse Instagram Business Account ID response", e);
            throw new RuntimeException("Failed to parse Instagram Business Account ID response", e);
        } catch (Exception e) {
            logger.error("Failed to get Instagram Business Account ID", e);
            throw new RuntimeException("Failed to get Instagram Business Account ID: " + e.getMessage(), e);
        }
    }

    /**
     * Step 2: Creates a media container for the Instagram post.
     * 
     * @param igUserId    Instagram Business Account ID
     * @param imageUrl    Public URL of the image (must be accessible by Instagram)
     * @param caption     Caption for the post
     * @param accessToken Page Access Token
     * @return Creation ID (media container ID)
     * @throws RuntimeException if media container creation fails
     */
    private String createMediaContainer(String igUserId, String imageUrl, String caption, String accessToken) {
        try {
            String response = webClient.post()
                    .uri(uriBuilder -> uriBuilder
                            .path("/{igUserId}/media")
                            .queryParam("access_token", accessToken)
                            .queryParam("image_url", imageUrl)
                            .queryParam("caption", caption != null ? caption : "")
                            .build(igUserId))
                    .retrieve()
                    .bodyToMono(String.class)
                    .block(Duration.ofMillis(timeoutMs));

            JsonNode jsonNode = objectMapper.readTree(response);

            if (!jsonNode.has("id")) {
                throw new RuntimeException("Media container creation response missing 'id' field");
            }

            return jsonNode.get("id").asText();

        } catch (WebClientResponseException e) {
            String responseBody = e.getResponseBodyAsString();
            logger.error("Failed to create Instagram media container: {} - {}",
                    e.getStatusCode(), responseBody != null ? responseBody : "No response body");
            throw new RuntimeException("Failed to create media container: " + e.getMessage(), e);
        } catch (JsonProcessingException e) {
            logger.error("Failed to parse media container creation response", e);
            throw new RuntimeException("Failed to parse media container creation response", e);
        } catch (Exception e) {
            logger.error("Unexpected error creating media container", e);
            throw new RuntimeException("Failed to create media container: " + e.getMessage(), e);
        }
    }

    /**
     * Step 3: Publishes the media container to Instagram.
     * 
     * @param igUserId    Instagram Business Account ID
     * @param creationId  The creation ID from Step 2
     * @param accessToken Page Access Token
     * @return Final Instagram post ID
     * @throws RuntimeException if publishing fails
     */
    private String publishMedia(String igUserId, String creationId, String accessToken) {
        try {
            String response = webClient.post()
                    .uri(uriBuilder -> uriBuilder
                            .path("/{igUserId}/media_publish")
                            .queryParam("access_token", accessToken)
                            .queryParam("creation_id", creationId)
                            .build(igUserId))
                    .retrieve()
                    .bodyToMono(String.class)
                    .block(Duration.ofMillis(timeoutMs));

            JsonNode jsonNode = objectMapper.readTree(response);

            if (!jsonNode.has("id")) {
                throw new RuntimeException("Media publish response missing 'id' field");
            }

            return jsonNode.get("id").asText();

        } catch (WebClientResponseException e) {
            String responseBody = e.getResponseBodyAsString();
            logger.error("Failed to publish Instagram media: {} - {}",
                    e.getStatusCode(), responseBody != null ? responseBody : "No response body");
            throw new RuntimeException("Failed to publish media: " + e.getMessage(), e);
        } catch (JsonProcessingException e) {
            logger.error("Failed to parse media publish response", e);
            throw new RuntimeException("Failed to parse media publish response", e);
        } catch (Exception e) {
            logger.error("Unexpected error publishing media", e);
            throw new RuntimeException("Failed to publish media: " + e.getMessage(), e);
        }
    }
}
