using Microsoft.EntityFrameworkCore;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Platform;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.Bootstrap;

public sealed class BuiltinStarterOfferingSeeder(TikuDbContext dbContext)
{
    public const string OfferingCode = "starter";
    public const int OfferingVersion = 1;

    private static readonly string[] FeatureCodes =
    [
        SaasFeatureCatalog.CoreBackoffice,
        SaasFeatureCatalog.SiteContent
    ];

    public async Task SeedAsync(CancellationToken cancellationToken = default)
    {
        var availableFeatureCodes = await dbContext.SaasFeatures.AsNoTracking()
            .Where(feature => FeatureCodes.Contains(feature.Code) && feature.Status == SaasFeatureStatus.Active)
            .Select(feature => feature.Code)
            .ToArrayAsync(cancellationToken);
        if (availableFeatureCodes.Length != FeatureCodes.Length)
            throw new InvalidOperationException(
                "The built-in feature catalog must be seeded before the starter offering.");

        var now = DateTimeOffset.UtcNow;
        var offering = await dbContext.SaasOfferings
            .SingleOrDefaultAsync(item => item.Code == OfferingCode, cancellationToken);
        if (offering is null)
        {
            offering = new SaasOffering
            {
                Code = OfferingCode,
                Name = "建站基础版",
                Type = SaasOfferingType.BasePlan,
                Status = SaasOfferingStatus.Active,
                Description = "包含租户后台与站点内容管理的免费基础版本。",
                SortOrder = 0
            };
            dbContext.SaasOfferings.Add(offering);
        }
        else
        {
            offering.Name = "建站基础版";
            offering.Type = SaasOfferingType.BasePlan;
            offering.Status = SaasOfferingStatus.Active;
            offering.Description = "包含租户后台与站点内容管理的免费基础版本。";
            offering.SortOrder = 0;
            offering.UpdatedAt = now;
        }

        var version = await dbContext.SaasOfferingVersions
            .SingleOrDefaultAsync(
                item => item.OfferingId == offering.Id && item.Version == OfferingVersion,
                cancellationToken);
        if (version is null)
        {
            version = new SaasOfferingVersion
            {
                OfferingId = offering.Id,
                Version = OfferingVersion,
                Status = SaasOfferingVersionStatus.Draft,
                BillingCycle = PlatformBillingCycle.Yearly,
                OriginalAmountCents = 0,
                AmountCents = 0,
                Currency = "CNY",
                EffectiveAt = now,
                Metadata = JsonDefaults.Object()
            };
            dbContext.SaasOfferingVersions.Add(version);
        }
        else if (version.Status == SaasOfferingVersionStatus.Draft)
        {
            version.BillingCycle = PlatformBillingCycle.Yearly;
            version.OriginalAmountCents = 0;
            version.AmountCents = 0;
            version.Currency = "CNY";
            version.EffectiveAt ??= now;
            version.RetiredAt = null;
            version.UpdatedAt = now;
        }
        else if (version.Status == SaasOfferingVersionStatus.Retired)
        {
            throw new InvalidOperationException(
                "The built-in starter offering version 1 is retired and cannot be repaired automatically.");
        }

        var existingFeatureCodes = await dbContext.SaasOfferingVersionFeatures
            .Where(binding => binding.OfferingVersionId == version.Id)
            .Select(binding => binding.FeatureCode)
            .ToHashSetAsync(StringComparer.Ordinal, cancellationToken);
        var missingFeatureCodes = FeatureCodes
            .Where(code => !existingFeatureCodes.Contains(code))
            .ToArray();
        if (version.Status == SaasOfferingVersionStatus.Published && missingFeatureCodes.Length > 0)
            throw new InvalidOperationException(
                "The published built-in starter offering is missing required features and cannot be repaired in place.");

        dbContext.SaasOfferingVersionFeatures.AddRange(missingFeatureCodes.Select(code =>
            new SaasOfferingVersionFeature
            {
                OfferingVersionId = version.Id,
                FeatureCode = code
            }));

        await dbContext.SaveChangesAsync(cancellationToken);

        if (version.Status == SaasOfferingVersionStatus.Draft)
        {
            version.Status = SaasOfferingVersionStatus.Published;
            version.PublishedAt = now;
            version.EffectiveAt ??= now;
            await dbContext.SaveChangesAsync(cancellationToken);
        }
    }
}