using System.Text.Json;
using Backend.Domain;
using Microsoft.EntityFrameworkCore;

namespace Backend.Data;

public static partial class SeedDataBootstrapper
{
    private static async Task EnsureSeedOperationalDataAsync(AwardsDbContext db, Season season)
    {
        var categories = await db.Categories
            .Where(item => item.SeasonId == season.Id)
            .ToDictionaryAsync(item => item.Slug, StringComparer.OrdinalIgnoreCase);
        var candidates = await db.Candidates
            .Where(item => item.SeasonId == season.Id)
            .ToArrayAsync();

        var normalizedLegacyState = await NormalizeLegacyDemoLabelsAsync(db);

        if (!await db.ClipSubmissions.AnyAsync(item => item.SeasonId == season.Id))
        {
            db.ClipSubmissions.AddRange(
                new ClipSubmission
                {
                    SeasonId = season.Id,
                    CategoryId = ResolveCategoryId(categories, "vtuber-des-jahres"),
                    CandidateId = ResolveCandidateId(categories, candidates, "vtuber-des-jahres", "Hoshimi Miyu"),
                    SubmittedByTwitchId = "local_user_3",
                    ClipUrl = "https://clips.twitch.tv/StarlitDebutMoment",
                    Title = "Starlight Debut Moment",
                    Creator = "Hoshimi Miyu",
                    Platform = "Twitch",
                    Status = "approved",
                    ReviewNote = "Geprüfter Clip fuer Voting-Vorschau.",
                    ReviewedByTwitchId = "jayuhime_admin",
                    CreatedFromIp = "127.0.0.1",
                    CreatedAt = new DateTimeOffset(2026, 6, 18, 8, 15, 0, TimeSpan.Zero),
                    ReviewedAt = new DateTimeOffset(2026, 6, 18, 12, 5, 0, TimeSpan.Zero),
                },
                new ClipSubmission
                {
                    SeasonId = season.Id,
                    CategoryId = ResolveCategoryId(categories, "vtuber-des-jahres"),
                    CandidateId = ResolveCandidateId(categories, candidates, "vtuber-des-jahres", "Kurainu"),
                    SubmittedByTwitchId = "local_user_4",
                    ClipUrl = "https://clips.twitch.tv/KurainuFinaleHype",
                    Title = "Finale-Hype mit Chat-Chaos",
                    Creator = "Kurainu",
                    Platform = "Twitch",
                    Status = "approved",
                    ReviewNote = "Geprüfter Clip fuer Voting-Vorschau.",
                    ReviewedByTwitchId = "jayuhime_admin",
                    CreatedFromIp = "127.0.0.1",
                    CreatedAt = new DateTimeOffset(2026, 6, 18, 8, 35, 0, TimeSpan.Zero),
                    ReviewedAt = new DateTimeOffset(2026, 6, 18, 12, 10, 0, TimeSpan.Zero),
                },
                new ClipSubmission
                {
                    SeasonId = season.Id,
                    CategoryId = ResolveCategoryId(categories, "best-gaming"),
                    CandidateId = ResolveCandidateId(categories, candidates, "best-gaming", "Kurainu"),
                    SubmittedByTwitchId = "local_user",
                    ClipUrl = "https://clips.twitch.tv/EpicGamingMoment",
                    Title = "Epischer Clutch im Finale",
                    Creator = "Kurainu",
                    Platform = "Twitch",
                    Status = "pending",
                    CreatedFromIp = "127.0.0.1",
                    CreatedAt = new DateTimeOffset(2026, 6, 17, 9, 10, 0, TimeSpan.Zero),
                },
                new ClipSubmission
                {
                    SeasonId = season.Id,
                    CategoryId = ResolveCategoryId(categories, "gesang-musik"),
                    CandidateId = ResolveCandidateId(categories, candidates, "gesang-musik", "Melo Diva"),
                    SubmittedByTwitchId = "local_user_2",
                    ClipUrl = "https://www.youtube.com/watch?v=liveCoverMoment",
                    Title = "Live-Cover mit Gänsehaut",
                    Creator = "Melo Diva",
                    Platform = "YouTube",
                    Status = "approved",
                    ReviewNote = "Geprüfter Clip fuer Review-Workflow.",
                    ReviewedByTwitchId = "jayuhime_admin",
                    CreatedFromIp = "127.0.0.1",
                    CreatedAt = new DateTimeOffset(2026, 6, 18, 10, 30, 0, TimeSpan.Zero),
                    ReviewedAt = new DateTimeOffset(2026, 6, 18, 12, 0, 0, TimeSpan.Zero),
                });
        }

        if (!normalizedLegacyState.HasRiskSeed && !await db.RiskFlags.AnyAsync(item => item.Source == "seed"))
        {
            db.RiskFlags.Add(new RiskFlag
            {
                SeasonId = season.Id,
                TwitchUserId = "sample_user",
                Source = "seed",
                Type = "rapid_vote_updates",
                Severity = "medium",
                Status = "open",
                Summary = "Mehrere Voting-Aenderungen in kurzer Zeit erkannt.",
                CreatedFromIp = "127.0.0.1",
                UserAgent = "seed-bootstrap",
                MetadataJson = JsonSerializer.Serialize(new { recentVoteSubmissions = 3 }),
                CreatedAt = new DateTimeOffset(2026, 6, 17, 8, 40, 0, TimeSpan.Zero),
            });
        }

        if (!normalizedLegacyState.HasAuditSeed && !await db.AdminAuditEntries.AnyAsync(item => item.ActionType == "seed.initialize"))
        {
            db.AdminAuditEntries.Add(new AdminAuditEntry
            {
                AdminTwitchUserId = "system",
                ActionType = "seed.initialize",
                EntityType = "database",
                EntityId = season.Year.ToString(),
                Summary = "Startinhalte wurden in der Datenbank bereitgestellt.",
                MetadataJson = JsonSerializer.Serialize(new { categories = SeedCatalog.CategorySeeds.Length }),
                CreatedFromIp = "seed",
                UserAgent = "seed-bootstrap",
                CreatedAt = new DateTimeOffset(2026, 6, 17, 8, 32, 0, TimeSpan.Zero),
            });
        }
    }

    private static int? ResolveCategoryId(IReadOnlyDictionary<string, Category> categories, string slug) =>
        categories.TryGetValue(slug, out var category) ? category.Id : null;

    private static int? ResolveCandidateId(
        IReadOnlyDictionary<string, Category> categories,
        IEnumerable<Candidate> candidates,
        string categorySlug,
        string displayName)
    {
        var categoryId = ResolveCategoryId(categories, categorySlug);
        return categoryId is int resolvedCategoryId
            ? candidates.FirstOrDefault(item =>
                item.CategoryId == resolvedCategoryId
                && string.Equals(item.DisplayName, displayName, StringComparison.OrdinalIgnoreCase))?.Id
            : null;
    }

    private static async Task<LegacySeedState> NormalizeLegacyDemoLabelsAsync(AwardsDbContext db)
    {
        var legacySessions = await db.UserSessions
            .Where(item => item.TwitchUserId == "admin_demo" || item.TwitchUserId == "jayuhime_demo" || item.TwitchUserId == "demo_user")
            .ToArrayAsync();

        foreach (var session in legacySessions)
        {
            session.TwitchUserId = session.TwitchUserId switch
            {
                "admin_demo" => "jayuhime_admin",
                "jayuhime_demo" => "jayuhime_viewer",
                "demo_user" => "local_user",
                _ => session.TwitchUserId,
            };
            session.DisplayName = session.DisplayName switch
            {
                "Admin Demo" => "Jayuhime Admin",
                "Demo User" => "Local User",
                _ => session.DisplayName,
            };
        }

        var legacyClipSubmissions = await db.ClipSubmissions
            .Where(item =>
                item.SubmittedByTwitchId == "demo_user" ||
                item.SubmittedByTwitchId == "demo_user_2" ||
                item.ClipUrl.Contains("Demo") ||
                item.ClipUrl.Contains("demo") ||
                (item.ReviewNote != null && item.ReviewNote.Contains("Demo-Clip")))
            .ToArrayAsync();

        foreach (var clip in legacyClipSubmissions)
        {
            clip.SubmittedByTwitchId = clip.SubmittedByTwitchId switch
            {
                "demo_user" => "local_user",
                "demo_user_2" => "local_user_2",
                _ => clip.SubmittedByTwitchId,
            };
            clip.ClipUrl = clip.ClipUrl
                .Replace("DemoGamingMoment", "EpicGamingMoment")
                .Replace("demoSong", "liveCoverMoment");
            clip.ReviewNote = clip.ReviewNote?.Replace("Demo-Clip", "Geprüfter Clip");
        }
        await LinkExistingClipsToCandidatesAsync(db);

        var legacyRiskFlags = await db.RiskFlags
            .Where(item =>
                item.Source == "demo" ||
                item.Summary.StartsWith("Demo:") ||
                item.TwitchUserId == "jayuhime_demo" ||
                item.TwitchUserId == "demo_user")
            .ToArrayAsync();

        foreach (var flag in legacyRiskFlags)
        {
            flag.Source = "seed";
            flag.TwitchUserId = flag.TwitchUserId switch
            {
                "demo_user" => "local_user",
                "jayuhime_demo" => "jayuhime_viewer",
                _ => flag.TwitchUserId,
            };
            flag.Summary = flag.Summary.Replace("Demo: ", string.Empty);
            flag.UserAgent = flag.UserAgent == "demo-seed" ? "seed-bootstrap" : flag.UserAgent;
        }

        var legacyAuditEntries = await db.AdminAuditEntries
            .Where(item =>
                item.ActionType == "demo.seed" ||
                item.Summary.Contains("Demo-Inhalte") ||
                item.AdminTwitchUserId == "admin_demo" ||
                item.AdminTwitchUserId == "jayuhime_demo")
            .ToArrayAsync();

        foreach (var entry in legacyAuditEntries)
        {
            entry.AdminTwitchUserId = entry.AdminTwitchUserId switch
            {
                "admin_demo" => "jayuhime_admin",
                "jayuhime_demo" => "jayuhime_viewer",
                _ => entry.AdminTwitchUserId,
            };
            if (entry.ActionType == "demo.seed")
            {
                entry.ActionType = "seed.initialize";
            }
            if (entry.Summary.Contains("Demo-Inhalte"))
            {
                entry.Summary = "Startinhalte wurden in der Datenbank bereitgestellt.";
            }
        }

        return new LegacySeedState(
            legacyRiskFlags.Length > 0 || await db.RiskFlags.AnyAsync(item => item.Source == "seed"),
            legacyAuditEntries.Length > 0 || await db.AdminAuditEntries.AnyAsync(item => item.ActionType == "seed.initialize"));
    }

    private static async Task LinkExistingClipsToCandidatesAsync(AwardsDbContext db)
    {
        var clips = await db.ClipSubmissions
            .Where(item => item.CandidateId == null && item.CategoryId != null && item.Creator != string.Empty)
            .ToArrayAsync();
        if (clips.Length == 0)
        {
            return;
        }

        var seasonIds = clips.Select(item => item.SeasonId).Distinct().ToArray();
        var categoryIds = clips.Select(item => item.CategoryId!.Value).Distinct().ToArray();
        var candidates = await db.Candidates
            .Where(item => seasonIds.Contains(item.SeasonId) && categoryIds.Contains(item.CategoryId))
            .ToArrayAsync();

        foreach (var clip in clips)
        {
            var creatorKey = NormalizeSeedCandidateKey(clip.Creator);
            var candidate = candidates.FirstOrDefault(item =>
                item.SeasonId == clip.SeasonId
                && item.CategoryId == clip.CategoryId
                && (NormalizeSeedCandidateKey(item.DisplayName) == creatorKey
                    || NormalizeSeedCandidateKey(item.ChannelSlug) == creatorKey));

            if (candidate is not null)
            {
                clip.CandidateId = candidate.Id;
            }
        }
    }

    private static string NormalizeSeedCandidateKey(string value) =>
        new(
            value
                .Trim()
                .TrimStart('@')
                .ToLowerInvariant()
                .Where(char.IsLetterOrDigit)
                .ToArray());

    private sealed record LegacySeedState(bool HasRiskSeed, bool HasAuditSeed);
}
