package party.cybsec.griefdetect.core;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import party.cybsec.griefdetect.GriefDetectPlugin;
import party.cybsec.griefdetect.config.PluginConfig;

import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * Webhook manager for sending detection alerts to Discord.
 * Async-only, rate-limited with retry and area-based cooldown.
 */
public class WebhookManager {
    
    private final GriefDetectPlugin plugin;
    private final PluginConfig config;
    private final OkHttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    // Rate limiting and cooldown
    private final ConcurrentHashMap<String, Long> areaCooldowns;
    private final ConcurrentHashMap<String, Integer> retryCounts;
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    
    // Configuration
    private boolean enabled;
    private String webhookUrl;
    private int retryAttempts;
    private long retryDelay;
    private long areaCooldown;
    
    public WebhookManager(GriefDetectPlugin plugin) {
        this.plugin = plugin;
        this.config = plugin.getPluginConfig();
        this.httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();
        this.objectMapper = new ObjectMapper();
        this.areaCooldowns = new ConcurrentHashMap<>();
        this.retryCounts = new ConcurrentHashMap<>();
        
        loadConfiguration();
    }
    
    /**
     * Load configuration settings.
     */
    private void loadConfiguration() {
        enabled = config.isWebhookEnabled();
        webhookUrl = config.getWebhookUrl();
        retryAttempts = config.getWebhookRetryAttempts();
        retryDelay = config.getWebhookRetryDelay();
        areaCooldown = config.getWebhookCooldown() * 1000L; // Convert to milliseconds
    }
    
    /**
     * Send a webhook notification for a detection event.
     * 
     * @param event The detection event to report
     */
    public void sendWebhook(DetectionEvent event) {
        if (!enabled || webhookUrl.isEmpty()) {
            return;
        }
        
        // Check area cooldown
        String areaKey = getAreaKey(event.getWorld(), event.getCenter());
        long now = System.currentTimeMillis();
        
        if (areaCooldowns.containsKey(areaKey) && (now - areaCooldowns.get(areaKey)) < areaCooldown) {
            return; // Still in cooldown
        }
        
        // Create webhook payload
        ObjectNode payload = createWebhookPayload(event);
        
        try {
            String jsonPayload = objectMapper.writeValueAsString(payload);
            RequestBody body = RequestBody.create(JSON, jsonPayload);
            Request request = new Request.Builder()
                .url(webhookUrl)
                .post(body)
                .addHeader("Content-Type", "application/json")
                .build();
            
            httpClient.newCall(request).enqueue(new WebhookCallback(areaKey, event.getDetectionType()));
            areaCooldowns.put(areaKey, now);
            
        } catch (Exception e) {
            plugin.getLogger().severe("Failed to send webhook: " + e.getMessage());
        }
    }
    
    /**
     * Create the Discord webhook payload.
     */
    private ObjectNode createWebhookPayload(DetectionEvent event) {
        ObjectNode embed = objectMapper.createObjectNode();
        
        // Embed title and color
        embed.put("title", "LavaCast Detection");
        embed.put("description", String.format(
            "**Type:** %s\n" +
            "**Confidence:** %.1f%%\n" +
            "**Location:** %s:%d,%d,%d\n" +
            "**Blocks:** %d\n" +
            "**Bounding Box:** %dx%dx%d",
            event.getDetectionType(),
            event.getConfidence(),
            event.getWorld().getName(),
            event.getCenter().getBlockX(),
            event.getCenter().getBlockY(),
            event.getCenter().getBlockZ(),
            event.getBlockCount(),
            event.getMaxX() - event.getMinX() + 1,
            event.getMaxY() - event.getMinY() + 1,
            event.getMaxZ() - event.getMinZ() + 1
        ));
        
        // Embed color based on confidence
        embed.put("color", getConfidenceColor(event.getConfidence()));
        
        // Fields for additional information
        StringBuilder fieldsText = new StringBuilder();
        
        // Nearest players field
        if (!event.getNearestPlayers().isEmpty()) {
            fieldsText.append("**Players:**\n");
            for (DetectionEvent.NearestPlayerInfo player : event.getNearestPlayers()) {
                String gamemode = player.isSurvival() ? "Survival" : "Creative";
                fieldsText.append(String.format("- %s (%.1fm, %s)\n", 
                    player.getPlayerName(), player.getDistance(), gamemode));
            }
        }
        
        // Teleport command field
        String tpCommand = String.format("/tp %d %d %d", 
            event.getCenter().getBlockX(), 
            event.getCenter().getBlockY(), 
            event.getCenter().getBlockZ());
        fieldsText.append("\n**Teleport:** `").append(tpCommand).append("`");
        
        if (fieldsText.length() > 0) {
            embed.put("fields", fieldsText.toString());
        }
        
        // Timestamp
        embed.put("timestamp", Instant.now().toString());
        
        // Footer with server info
        ObjectNode footer = objectMapper.createObjectNode();
        footer.put("text", "GriefDetect v" + plugin.getDescription().getVersion());
        embed.set("footer", footer);
        
        // Create final payload
        ObjectNode payload = objectMapper.createObjectNode();
        payload.set("embeds", objectMapper.createArrayNode().add(embed));
        
        return payload;
    }
    
    /**
     * Get color based on confidence level.
     */
    private int getConfidenceColor(double confidence) {
        if (confidence >= 90) return 0xFF0000; // Red
        if (confidence >= 75) return 0xFFA500; // Orange
        if (confidence >= 50) return 0xFFFF00; // Yellow
        return 0x00FF00; // Green
    }
    
    /**
     * Generate area key for cooldown tracking.
     */
    private String getAreaKey(World world, Location location) {
        // Use chunk coordinates for area tracking
        int chunkX = location.getBlockX() >> 4;
        int chunkZ = location.getBlockZ() >> 4;
        return world.getName() + ":" + chunkX + ":" + chunkZ;
    }
    
    /**
     * Webhook callback handler for async responses.
     */
    private class WebhookCallback implements Callback {
        private final String areaKey;
        private final String detectionType;
        
        public WebhookCallback(String areaKey, String detectionType) {
            this.areaKey = areaKey;
            this.detectionType = detectionType;
        }
        
        @Override
        public void onFailure(Call call, IOException e) {
            plugin.getLogger().warning("Webhook request failed: " + e.getMessage());
            
            // Retry logic
            String retryKey = areaKey + ":" + detectionType;
            int attempts = retryCounts.getOrDefault(retryKey, 0);
            
            if (attempts < retryAttempts) {
                retryCounts.put(retryKey, attempts + 1);
                
                Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> {
                    // Resend the webhook (simplified retry)
                    plugin.getLogger().info("Retrying webhook for " + detectionType);
                }, retryDelay / 50); // Convert milliseconds to ticks
            }
        }
        
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                plugin.getLogger().info("Webhook sent successfully for " + detectionType);
            } else {
                plugin.getLogger().warning("Webhook failed with code: " + response.code());
            }
            response.close();
        }
    }
    
    /**
     * Reload configuration.
     */
    public void reload() {
        loadConfiguration();
        plugin.getLogger().info("Webhook configuration reloaded");
    }
}