using System.Text.Json;
using ClawdDotNet.Core.State;
using ClawdDotNet.Core.Tools;
using Microsoft.Extensions.Logging;
using Telegram.Bot;
using Telegram.Bot.Types;

namespace ClawdDotNet.Tools.Telegram;

public sealed class TelegramTool : IAgentTool, IToolJobProvider
{
    public string Name => "Telegram";

    public string Description => "Erlaubt das Senden und Empfangen von Nachrichten über Telegram.";

    public JsonElement InputSchema { get; } = JsonDocument.Parse("""
        {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "enum": ["send_message", "get_updates"],
                    "description": "Die auszuführende Aktion"
                },
                "chatId": {
                    "type": "string",
                    "description": "Die ID des Chats (optional, falls Standard-ID konfiguriert)"
                },
                "text": {
                    "type": "string",
                    "description": "Der zu sendende Text (nur für send_message)"
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximale Anzahl an Nachrichten zum Abrufen (nur für get_updates)"
                }
            },
            "required": ["action"]
        }
        """).RootElement.Clone();

    // ─── IAgentTool ───

    public async Task<ToolResult> ExecuteAsync(
        JsonElement input,
        AgentToolContext context,
        CancellationToken ct)
    {
        var action = input.GetProperty("action").GetString()
            ?? throw new ArgumentException("'action' is required");

        var botToken = context.ToolConfig.TryGetValue("botToken", out var bt) ? bt?.ToString() : null;
        if (string.IsNullOrWhiteSpace(botToken))
        {
            return ToolResult.Fail("Konfigurationsfehler: 'botToken' ist nicht definiert.");
        }

        var botClient = new TelegramBotClient(botToken);

        try
        {
            return action switch
            {
                "send_message" => await HandleSendMessageAsync(botClient, input, context, ct),
                "get_updates" => await HandleGetUpdatesAsync(botClient, input, context, ct),
                _ => ToolResult.Fail($"Unbekannte Aktion: {action}")
            };
        }
        catch (Exception ex)
        {
            context.Logger.LogError(ex, "Fehler bei Telegram Aktion {Action}", action);
            return ToolResult.Fail($"Fehler: {ex.Message}");
        }
    }

    // ─── IToolJobProvider ───

    public IReadOnlyList<ToolJobDefinition> GetJobDefinitions() =>
    [
        new("telegram_poll", "Telegram Polling",
            "Prüft regelmäßig auf neue Telegram-Nachrichten und weckt den Agenten bei neuen Nachrichten.")
    ];

    public async Task<ToolJobResult> ExecuteJobAsync(
        string jobTypeId,
        IReadOnlyDictionary<string, object?> toolConfig,
        IStateStore stateStore,
        ILogger logger,
        CancellationToken ct,
        string? agentId = null,
        string? workspacePath = null)
    {
        if (jobTypeId != "telegram_poll")
            return ToolJobResult.NoAction($"Unbekannter Job-Typ: {jobTypeId}");

        var botToken = toolConfig.TryGetValue("botToken", out var bt) ? bt?.ToString() : null;
        if (string.IsNullOrWhiteSpace(botToken))
            return ToolJobResult.NoAction("Kein botToken konfiguriert");

        var client = new TelegramBotClient(botToken);

        var offsetKey = "telegram_poll_offset";
        var lastOffsetStr = await stateStore.GetAsync(offsetKey, ct);
        var lastOffset = int.TryParse(lastOffsetStr, out var o) ? o : 0;

        try
        {
            var updates = await client.GetUpdates(
                offset: lastOffset, limit: 100, cancellationToken: ct);

            var messages = updates
                .Where(u => u.Message is not null)
                .Where(u => IsChatIdAllowed(u.Message!.Chat.Id.ToString(), toolConfig))
                .ToList();

            if (messages.Count == 0)
            {
                if (updates.Length > 0)
                {
                    var newOffset = updates.Max(u => u.Id) + 1;
                    await stateStore.SetAsync(offsetKey, newOffset.ToString(), ct);
                }
                return ToolJobResult.NoAction("Polled: 0 neue Nachrichten");
            }

            var newOffsetFinal = updates.Max(u => u.Id) + 1;
            await stateStore.SetAsync(offsetKey, newOffsetFinal.ToString(), ct);

            var summary = string.Join("\n", messages.Select(u =>
            {
                var from = u.Message!.From?.Username ?? u.Message.From?.FirstName ?? "Unbekannt";
                var text = u.Message.Text ?? "(kein Text)";
                return $"- {from}: {text}";
            }));

            var wakeMessage = $"[Telegram] {messages.Count} neue Nachricht(en) empfangen:\n\n{summary}";

            logger.LogInformation("Telegram Poll: {Count} neue Nachrichten", messages.Count);

            return ToolJobResult.Wake(wakeMessage, $"Polled: {messages.Count} neue Nachrichten");
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Telegram Polling fehlgeschlagen");
            return ToolJobResult.NoAction($"Polling-Fehler: {ex.Message}");
        }
    }

    // ─── Private Helpers ───

    private async Task<ToolResult> HandleSendMessageAsync(
        ITelegramBotClient client, JsonElement input, AgentToolContext context, CancellationToken ct)
    {
        var chatIdStr = input.TryGetProperty("chatId", out var ci) ? ci.GetString() : null;
        var text = input.TryGetProperty("text", out var t) ? t.GetString() : null;

        if (string.IsNullOrWhiteSpace(chatIdStr))
        {
            chatIdStr = context.ToolConfig.TryGetValue("defaultChatId", out var dci) ? dci?.ToString() : null;
        }

        if (string.IsNullOrWhiteSpace(chatIdStr) || string.IsNullOrWhiteSpace(text))
        {
            return ToolResult.Fail("'chatId' (oder Standard-ID in Konfig) und 'text' sind für send_message erforderlich.");
        }

        if (!IsChatIdAllowed(chatIdStr, context.ToolConfig))
        {
            return ToolResult.Fail($"Sicherheitsfehler: Zugriff auf ChatId '{chatIdStr}' ist nicht erlaubt.");
        }

        await client.SendMessage(new ChatId(chatIdStr), text, cancellationToken: ct);
        return ToolResult.Ok($"Nachricht erfolgreich an {chatIdStr} gesendet.");
    }

    private async Task<ToolResult> HandleGetUpdatesAsync(
        ITelegramBotClient client, JsonElement input, AgentToolContext context, CancellationToken ct)
    {
        var limit = input.TryGetProperty("limit", out var l) ? l.GetInt32() : 10;

        var updates = await client.GetUpdates(limit: limit, cancellationToken: ct);

        var allowedUpdates = updates
            .Where(u => u.Message != null && IsChatIdAllowed(u.Message.Chat.Id.ToString(), context.ToolConfig))
            .Select(u => new
            {
                u.Id,
                Message = new
                {
                    u.Message!.Id,
                    ChatId = u.Message.Chat.Id,
                    From = u.Message.From?.Username ?? u.Message.From?.FirstName,
                    u.Message.Text,
                    u.Message.Date
                }
            })
            .ToList();

        return ToolResult.Ok(JsonSerializer.Serialize(allowedUpdates, new JsonSerializerOptions { WriteIndented = true }));
    }

    private static bool IsChatIdAllowed(string chatId, IReadOnlyDictionary<string, object?> toolConfig)
    {
        if (toolConfig.TryGetValue("defaultChatId", out var dci) && dci?.ToString() == chatId)
            return true;

        if (!toolConfig.TryGetValue("allowedChatIds", out var val) || val is not JsonElement je)
            return false;

        var allowed = je.EnumerateArray().Select(x => x.GetString()).ToList();
        return allowed.Contains(chatId);
    }

    private bool IsChatIdAllowed(string chatId, AgentToolContext context)
        => IsChatIdAllowed(chatId, context.ToolConfig);
}
