using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Scheduling;
using Microsoft.Extensions.Logging;

namespace ClawdDotNet.Core.Tasks;

/// <summary>
/// Überführt die alten Scheduler-Konfigurationen ins Taskboard — der letzte Schritt, um
/// die Alt-Scheduler abzulösen: alles Periodische ist danach ein Task.
///
/// - Ein <c>scheduler</c> (Agent nach Cron) → ein Task mit <c>when: cron</c> und Assignee
///   <c>@new:&lt;agent&gt;</c> (frischer Lauf, wie der alte AgentScheduler).
/// - Jeder <c>toolJobs</c>-Eintrag (Poll) → ein Task vom Typ <c>tool_job</c>.
///
/// Einmalig und nicht-destruktiv: Existiert der Task (stabile Id) schon, wird er
/// übersprungen — spätere Änderungen an der Task-Datei bleiben erhalten.
/// </summary>
public sealed class SchedulerTaskMigration
{
    private readonly TaskboardService _board;
    private readonly ITaskRepository _repo;
    private readonly ILogger _logger;

    public SchedulerTaskMigration(TaskboardService board, ITaskRepository repo, ILoggerFactory loggerFactory)
    {
        _board = board;
        _repo = repo;
        _logger = loggerFactory.CreateLogger("ClawdDotNet.Core.Tasks.SchedulerMigration");
    }

    public async Task<int> RunAsync(IEnumerable<AgentConfig> agents, CancellationToken ct)
    {
        // IANA-Schreibweise, nicht TimeZoneInfo.Local.Id: Unter Windows lieferte das
        // "W. Europe Standard Time", unter Linux "Europe/Berlin". Die erzeugten
        // Task-Dateien wandern zwischen Rechnern — sie brauchen die Form, die überall
        // gilt.
        var localTz = TimeZones.LocalIanaId;
        var created = 0;

        foreach (var agent in agents)
        {
            if (agent.Scheduler is { } scheduler && !string.IsNullOrWhiteSpace(scheduler.Cron))
                created += await MigrateSchedulerAsync(agent, scheduler, localTz, ct);

            foreach (var job in agent.ToolJobs)
                created += await MigrateToolJobAsync(agent, job, localTz, ct);
        }

        if (created > 0)
            _logger.LogInformation("Scheduler-Migration: {Count} Task(s) aus Alt-Konfiguration angelegt", created);

        return created;
    }

    private async Task<int> MigrateSchedulerAsync(
        AgentConfig agent, SchedulerConfig scheduler, string tz, CancellationToken ct)
    {
        var id = $"sched-{agent.AgentId}";
        if (await _repo.GetAsync(id, ct) is not null)
            return 0;

        await _board.CreateAsync(new TaskItem
        {
            Id = id,
            Title = $"Geplanter Lauf: {Display(agent)}",
            Body = scheduler.TaskMessage,
            Status = TaskItemStatus.Todo,
            Type = TaskItemType.Work,
            Assignee = $"@new:{agent.AgentId}", // frischer Lauf, wie der alte AgentScheduler
            When = new TaskWhen { Kind = TaskWhenKind.Cron, Value = scheduler.Cron, TimeZone = tz }
        }, ct);

        return 1;
    }

    private async Task<int> MigrateToolJobAsync(
        AgentConfig agent, ToolJobConfig job, string tz, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(job.Cron) || string.IsNullOrWhiteSpace(job.ToolName))
            return 0;

        var id = $"tj-{agent.AgentId}-{job.JobId}";
        if (await _repo.GetAsync(id, ct) is not null)
            return 0;

        await _board.CreateAsync(new TaskItem
        {
            Id = id,
            Title = $"Poll: {job.ToolName}/{job.JobTypeId} → {Display(agent)}",
            Body = $"Wiederkehrender {job.ToolName}-Poll ({job.JobTypeId}). Weckt {Display(agent)} bei einem Ereignis.",
            // Deaktivierte Jobs kommen als backlog — der Scanner nimmt nur todo/backlog...
            // backlog wird aber nicht geclaimt, also ruht der Poll, bis jemand ihn aktiviert.
            Status = job.Enabled ? TaskItemStatus.Todo : TaskItemStatus.Backlog,
            Type = TaskItemType.ToolJob,
            ToolName = job.ToolName,
            JobTypeId = job.JobTypeId,
            Assignee = $"@{agent.AgentId}", // Zielagent zum Wecken
            When = new TaskWhen { Kind = TaskWhenKind.Cron, Value = job.Cron, TimeZone = tz }
        }, ct);

        return 1;
    }

    private static string Display(AgentConfig agent)
        => string.IsNullOrWhiteSpace(agent.DisplayName) ? agent.AgentId : agent.DisplayName;
}
