using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Infrastructure.Data;
using SportsDivision.Infrastructure.Identity;

namespace SportsDivision.Infrastructure.Seeding;

public class DatabaseSeeder
{
    private readonly ApplicationDbContext _context;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;

    public DatabaseSeeder(ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        _context = context;
        _userManager = userManager;
        _roleManager = roleManager;
    }

    public async Task SeedAsync()
    {
        await _context.Database.MigrateAsync();
        await SeedRolesAsync();
        await SeedAdminUserAsync();
        await SeedZonesAsync();
        await SeedSchoolsAsync();
        await SeedEventLevelsAsync();
        await SeedEventsAsync();
        await SeedScoringConstantsAsync();
        await SeedPlacementPointsAsync();
    }

    private async Task SeedRolesAsync()
    {
        string[] roles = { "Admin", "Official", "Coach", "Principal", "Student" };
        foreach (var role in roles)
            if (!await _roleManager.RoleExistsAsync(role))
                await _roleManager.CreateAsync(new IdentityRole(role));
    }

    private async Task SeedAdminUserAsync()
    {
        if (await _userManager.FindByEmailAsync("admin@sportsdivision.dm") != null) return;
        var admin = new ApplicationUser
        {
            UserName = "admin@sportsdivision.dm",
            Email = "admin@sportsdivision.dm",
            FirstName = "System",
            LastName = "Administrator",
            EmailConfirmed = true,
            IsActive = true
        };
        var result = await _userManager.CreateAsync(admin, "Admin@123!");
        if (result.Succeeded) await _userManager.AddToRoleAsync(admin, "Admin");
    }

    private async Task SeedZonesAsync()
    {
        if (await _context.Zones.AnyAsync()) return;
        await _context.Zones.AddRangeAsync(new List<Zone>
        {
            new() { Name = "North West", Code = "NW" },
            new() { Name = "North", Code = "N" },
            new() { Name = "South", Code = "S" },
            new() { Name = "South East", Code = "SE" },
            new() { Name = "North East", Code = "NE" },
            new() { Name = "South West", Code = "SW" },
            new() { Name = "West Central", Code = "WC" },
            new() { Name = "Secondary School / College", Code = "SSC" }
        });
        await _context.SaveChangesAsync();
    }

    private async Task SeedSchoolsAsync()
    {
        if (await _context.Schools.AnyAsync()) return;
        var zones = await _context.Zones.ToDictionaryAsync(z => z.Code, z => z.ZoneId);
        var schools = new List<School>
        {
            new() { Name = "Academic School of Learning", ShortName = "ACADEMIX", SchoolLevel = SchoolLevel.College, ZoneId = zones["SSC"] },
            new() { Name = "Arthur Waldron Seventh-day Adventist Academy", ShortName = "AWSDAA", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Business Training Center", ShortName = "BTC", SchoolLevel = SchoolLevel.College, ZoneId = zones["SSC"] },
            new() { Name = "Castle Bruce Secondary School", ShortName = "CBSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Convent High School", ShortName = "CHS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Dominica Community High School", ShortName = "DCHS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Dominica Grammar School", ShortName = "DGS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Dominica State College", ShortName = "DSC", SchoolLevel = SchoolLevel.College, ZoneId = zones["SSC"] },
            new() { Name = "Goodwill Secondary School", ShortName = "GSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Isaiah Thomas Secondary School", ShortName = "ITSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "North East Comprehensive School", ShortName = "NECS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Orion Academy", ShortName = "ORION", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Pierre Charles Secondary School", ShortName = "PCSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Portsmouth Secondary School", ShortName = "PSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "St Martin's Secondary School", ShortName = "SMSS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "St Mary's Academy", ShortName = "SMA", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Wesley High School", ShortName = "WHS", SchoolLevel = SchoolLevel.Secondary, ZoneId = zones["SSC"] },
            new() { Name = "Cambell Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Colihaut Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Coulibistrie Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Dublanc Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Kelleb Laurent Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Mahaut Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Salisbury Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Warner Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NW"] },
            new() { Name = "Baroness Patricia Scotland Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Bense Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Clifton Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Paix Bouche Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Penville Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Roosevelt Douglas Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Savanne Paille Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "St. John's Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Thibaud Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["N"] },
            new() { Name = "Atkinson Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Calibishie Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Concord Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Lighthouse Christian Academy", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Salybia Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Wesley Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Wills Stratmore Steven Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Woodford Hill Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["NE"] },
            new() { Name = "Belles Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Castle Bruce Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Delices Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Grand Fond Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Jones Beaupierre Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Morne Jaune Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "San Sauver Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Sineku Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SE"] },
            new() { Name = "Bagatelle Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["S"] },
            new() { Name = "Bellevue Chopin Primary", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["S"] },
            new() { Name = "Grand Bay Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["S"] },
            new() { Name = "Petite Savanne Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["S"] },
            new() { Name = "Tete Morne Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["S"] },
            new() { Name = "Giraudel Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Morne Prosper Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Newtown Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Soufriere Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "St. Luke's Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Trafalgar Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Wotton Waven Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["SW"] },
            new() { Name = "Berean Academy", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "Christian Union Primary", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "Goodwill Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "Massacre Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "Pioneer Preparatory", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "St. Martin's Primary School", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
            new() { Name = "St. Mary's Primary", SchoolLevel = SchoolLevel.Primary, ZoneId = zones["WC"] },
        };
        await _context.Schools.AddRangeAsync(schools);
        await _context.SaveChangesAsync();
    }

    private async Task SeedEventLevelsAsync()
    {
        if (await _context.EventLevels.AnyAsync()) return;
        await _context.EventLevels.AddRangeAsync(new List<EventLevel>
        {
            new() { Name = "Junior Boys", Sex = Sex.Male, SchoolLevel = SchoolLevel.Primary, IsAgeBased = false, SortOrder = 1 },
            new() { Name = "Junior Girls", Sex = Sex.Female, SchoolLevel = SchoolLevel.Primary, IsAgeBased = false, SortOrder = 2 },
            new() { Name = "Senior Boys", Sex = Sex.Male, SchoolLevel = SchoolLevel.Primary, IsAgeBased = false, SortOrder = 3 },
            new() { Name = "Senior Girls", Sex = Sex.Female, SchoolLevel = SchoolLevel.Primary, IsAgeBased = false, SortOrder = 4 },
            new() { Name = "Under 14 Boys", Sex = Sex.Male, MaxAge = 13, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 5 },
            new() { Name = "Under 14 Girls", Sex = Sex.Female, MaxAge = 13, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 6 },
            new() { Name = "Under 15 Boys", Sex = Sex.Male, MaxAge = 14, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 7 },
            new() { Name = "Under 15 Girls", Sex = Sex.Female, MaxAge = 14, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 8 },
            new() { Name = "Under 16 Boys", Sex = Sex.Male, MaxAge = 15, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 9 },
            new() { Name = "Under 16 Girls", Sex = Sex.Female, MaxAge = 15, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 10 },
            new() { Name = "Under 17 Boys", Sex = Sex.Male, MaxAge = 16, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 11 },
            new() { Name = "Under 17 Girls", Sex = Sex.Female, MaxAge = 16, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 12 },
            new() { Name = "Under 20 Boys", Sex = Sex.Male, MaxAge = 19, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 13 },
            new() { Name = "Under 20 Girls", Sex = Sex.Female, MaxAge = 19, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 14 },
            new() { Name = "Under 21 Boys", Sex = Sex.Male, MaxAge = 20, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 15 },
            new() { Name = "Under 21 Girls", Sex = Sex.Female, MaxAge = 20, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 16 },
            new() { Name = "Open Boys", Sex = Sex.Male, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 17 },
            new() { Name = "Open Girls", Sex = Sex.Female, SchoolLevel = SchoolLevel.Secondary, IsAgeBased = true, SortOrder = 18 },
        });
        await _context.SaveChangesAsync();
    }

    private async Task SeedEventsAsync()
    {
        if (await _context.Events.AnyAsync()) return;
        var events = new List<Event>
        {
            new() { Name = "80m", Category = EventCategory.Track, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "100m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "150m", Category = EventCategory.Track, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "200m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "300m", Category = EventCategory.Track, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "400m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "800m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "1200m", Category = EventCategory.Track, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "1500m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "3000m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "5000m", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "80mH", Category = EventCategory.Track, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "4X80m Relay", Category = EventCategory.Track, IsRelay = true, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "4X100m Relay", Category = EventCategory.Track, IsRelay = true, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "4X400m", Category = EventCategory.Track, IsRelay = true, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Long Jump", Category = EventCategory.Field, PrimarySchool = true, SecondarySchool = true },
            new() { Name = "Triple Jump", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Shot Put 3kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Shot Put 4kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Shot Put 5kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Shot Put 6kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Discus 1kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Discus 1.25kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Discus 1.5kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Discus 1.75kg", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Javelin 400g", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Javelin 500g", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Javelin 600g", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Javelin 700g", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Javelin 800g", Category = EventCategory.Field, PrimarySchool = false, SecondarySchool = true },
            new() { Name = "Throwing the Cricket Ball", Category = EventCategory.Field, PrimarySchool = true, SecondarySchool = false },
            new() { Name = "High Jump", Category = EventCategory.HighJump, PrimarySchool = true, SecondarySchool = true },
        };
        await _context.Events.AddRangeAsync(events);
        await _context.SaveChangesAsync();

        var allEvents = await _context.Events.ToListAsync();
        var primaryLevels = await _context.EventLevels.Where(l => l.SchoolLevel == SchoolLevel.Primary).ToListAsync();
        var secondaryLevels = await _context.EventLevels.Where(l => l.SchoolLevel == SchoolLevel.Secondary).ToListAsync();

        foreach (var evt in allEvents)
        {
            if (evt.PrimarySchool) foreach (var level in primaryLevels) evt.EventLevels.Add(level);
            if (evt.SecondarySchool) foreach (var level in secondaryLevels) evt.EventLevels.Add(level);
        }
        await _context.SaveChangesAsync();
    }

    private async Task SeedScoringConstantsAsync()
    {
        if (await _context.ScoringConstants.AnyAsync()) return;
        var events = await _context.Events.ToListAsync();
        var constants = new List<ScoringConstant>();
        void Add(string name, decimal a, decimal b, decimal c, string unit)
        {
            var e = events.FirstOrDefault(ev => ev.Name == name);
            if (e != null) constants.Add(new ScoringConstant { EventId = e.EventId, A = a, B = b, C = c, Unit = unit });
        }
        Add("100m", 25.4347m, 18m, 1.81m, "seconds");
        Add("400m", 1.53775m, 82m, 1.81m, "seconds");
        Add("1500m", 0.03768m, 480m, 1.85m, "seconds");
        Add("80mH", 5.74352m, 28.5m, 1.92m, "seconds");
        Add("Long Jump", 90.56762m, 2.2m, 1.4m, "metres");
        Add("Triple Jump", 90.56762m, 2.2m, 1.4m, "metres");
        Add("High Jump", 585.63492m, 0.75m, 1.42m, "metres");
        Add("Shot Put 3kg", 51.39m, 1.5m, 1.05m, "metres"); Add("Shot Put 4kg", 51.39m, 1.5m, 1.05m, "metres");
        Add("Shot Put 5kg", 51.39m, 1.5m, 1.05m, "metres"); Add("Shot Put 6kg", 51.39m, 1.5m, 1.05m, "metres");
        Add("Discus 1kg", 12.91m, 4m, 1.1m, "metres"); Add("Discus 1.25kg", 12.91m, 4m, 1.1m, "metres");
        Add("Discus 1.5kg", 12.91m, 4m, 1.1m, "metres"); Add("Discus 1.75kg", 12.91m, 4m, 1.1m, "metres");
        Add("Javelin 400g", 10.14m, 7m, 1.08m, "metres"); Add("Javelin 500g", 10.14m, 7m, 1.08m, "metres");
        Add("Javelin 600g", 10.14m, 7m, 1.08m, "metres"); Add("Javelin 700g", 10.14m, 7m, 1.08m, "metres");
        Add("Javelin 800g", 10.14m, 7m, 1.08m, "metres");
        await _context.ScoringConstants.AddRangeAsync(constants);
        await _context.SaveChangesAsync();
    }

    private async Task SeedPlacementPointsAsync()
    {
        if (await _context.PlacementPointConfigs.AnyAsync()) return;
        await _context.PlacementPointConfigs.AddRangeAsync(new List<PlacementPointConfig>
        {
            new() { Placement = 1, Points = 10 }, new() { Placement = 2, Points = 8 },
            new() { Placement = 3, Points = 6 }, new() { Placement = 4, Points = 5 },
            new() { Placement = 5, Points = 4 }, new() { Placement = 6, Points = 3 },
            new() { Placement = 7, Points = 2 }, new() { Placement = 8, Points = 1 },
        });
        await _context.SaveChangesAsync();
    }
}
