using System.Text.RegularExpressions;
using ClawdDotNet.Core.Storage;
using Microsoft.Extensions.Logging;

namespace ClawdDotNet.Core.Tasks;

/// <summary>
/// Einmalige Überführung der improvisierten <c>coordination/*.md</c>-Dateien ins Taskboard
/// (A1, Schritt 4).
///
/// Bewusst konservativ, weil die Altdateien kein Schema haben: Nur <c>task_*.md</c> gelten
/// als Aufgaben und werden als <b>Backlog</b>-Aufgaben mit Assignee <c>@human</c>
/// übernommen — ein Mensch ordnet sie zu, bevor irgendetwas läuft. <c>status_*</c>,
/// <c>broadcast</c>, Incident-Berichte und <c>*.json</c>-Artefakte sind keine Aufgaben und
/// bleiben unangetastet. Was sich nicht sicher deuten lässt, wird nicht verfälscht.
///
/// Idempotent: Eine übernommene Datei wandert nach <c>coordination/migrated/</c> — sie
/// wird nicht gelöscht (die Historie bleibt), aber ein zweiter Start findet sie nicht mehr.
/// </summary>
public sealed class CoordinationMigration
{
    private readonly TaskboardService _board;
    private readonly string _coordinationDir;
    private readonly ILogger _logger;

    private static readonly Regex TaskHeading =
        new(@"^#\s*Task\b[^:]*:\s*(.+)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
    private static readonly Regex AnyHeading =
        new(@"^#\s+(.+)$", RegexOptions.Compiled);

    public CoordinationMigration(
        TaskboardService board, string coordinationDirectory, ILoggerFactory loggerFactory)
    {
        _board = board;
        _coordinationDir = coordinationDirectory;
        _logger = loggerFactory.CreateLogger("ClawdDotNet.Core.Tasks.Migration");
    }

    /// <summary>Führt die Migration aus. Gibt die Zahl der übernommenen Aufgaben zurück.</summary>
    public async Task<int> RunAsync(CancellationToken ct)
    {
        if (!Directory.Exists(_coordinationDir))
            return 0;

        var migratedDir = Path.Combine(_coordinationDir, "migrated");
        var count = 0;

        // Nur die oberste Ebene — der migrated/-Unterordner wird so nie erneut gelesen.
        foreach (var path in Directory.EnumerateFiles(_coordinationDir, "task_*.md"))
        {
            string text;
            try { text = AtomicFile.ReadAllText(path); }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Migration: {File} nicht lesbar — übersprungen", path);
                continue;
            }

            var fileName = Path.GetFileName(path);
            var title = ExtractTitle(text, fileName);

            var body =
                $"_Übernommen aus coordination/{fileName} bei der Taskboard-Migration (A1). " +
                "Assignee und Status bitte prüfen._\n\n" + text.Trim();

            await _board.CreateAsync(new TaskItem
            {
                Title = title,
                Body = body,
                Status = TaskItemStatus.Backlog, // erst nach menschlicher Sichtung bereit
                Assignee = TaskAssignee.Human,    // sicherer Standard, bis jemand zuordnet
                Type = TaskItemType.Work
            }, ct);

            try
            {
                Directory.CreateDirectory(migratedDir);
                File.Move(path, Path.Combine(migratedDir, fileName), overwrite: true);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex,
                    "Migration: {File} übernommen, konnte aber nicht verschoben werden", fileName);
            }

            count++;
        }

        if (count > 0)
            _logger.LogInformation("Taskboard-Migration: {Count} coordination/task_*-Datei(en) übernommen", count);

        return count;
    }

    private static string ExtractTitle(string text, string fileName)
    {
        foreach (var line in text.Replace("\r\n", "\n").Split('\n'))
        {
            var match = TaskHeading.Match(line.Trim());
            if (match.Success)
                return Trim(match.Groups[1].Value);
        }

        foreach (var line in text.Replace("\r\n", "\n").Split('\n'))
        {
            var match = AnyHeading.Match(line.Trim());
            if (match.Success)
                return Trim(match.Groups[1].Value);
        }

        // Kein Titel im Text: aus dem Dateinamen ableiten ("task_video_x" → "video x").
        var stem = Path.GetFileNameWithoutExtension(fileName);
        if (stem.StartsWith("task_", StringComparison.OrdinalIgnoreCase))
            stem = stem[5..];
        return Trim(stem.Replace('_', ' '));
    }

    private static string Trim(string value)
    {
        var v = value.Trim();
        return v.Length > 120 ? v[..120].TrimEnd() : v;
    }
}
