using Microsoft.EntityFrameworkCore;
using Tiku.Application.Catalog;
using Tiku.Application.Content;
using Tiku.Domain.Catalog;
using Tiku.Infrastructure.Security;

namespace Tiku.Infrastructure.Content;

public sealed partial class DirectContentService
{
    public async Task<CatalogList<School>> GetSchoolsAsync(
        DirectContentActor actor,
        AdminLimitFilter filter,
        CancellationToken cancellationToken = default)
    {
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        var regionIds = scope.RegionIds.ToArray();
        var query = dbContext.Schools.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(scope, null, item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
        if (filter.RegionId.HasValue) query = query.Where(item => item.RegionId == filter.RegionId.Value);

        if (!string.IsNullOrWhiteSpace(filter.Keyword))
        {
            var keyword = filter.Keyword.Trim();
            query = query.Where(item => item.Name.Contains(keyword));
        }

        return new CatalogList<School>(await query
            .OrderBy(item => item.Name)
            .Take(ResolveLimit(filter.Limit))
            .ToArrayAsync(cancellationToken));
    }

    public async Task<ContentManagementResult<School>> UpsertSchoolAsync(
        DirectContentActor actor,
        SchoolCommand command,
        CancellationToken cancellationToken = default)
    {
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        ArgumentException.ThrowIfNullOrWhiteSpace(command.Name);
        await AssertReferenceAsync<Region>(actor.TenantId, command.RegionId, "region_not_found", cancellationToken);
        var item = await ResolveByIdOrLegacyAsync(dbContext.Schools, actor.TenantId, command.Id, command.LegacyId,
            cancellationToken);
        var isNew = item is null;
        EnsureRegionWriteAllowed(scope, actor, item?.RegionId, command.RegionId, isNew, "school_not_found");
        item ??= new School { Id = command.Id ?? Guid.NewGuid(), TenantId = actor.TenantId };
        item.RegionId = command.RegionId;
        item.LegacyId = Normalize(command.LegacyId);
        item.Name = command.Name.Trim();
        item.ProfessionalExamDate = Normalize(command.ProfessionalExamDate);
        item.Metadata = JsonObjectOrDefault(command.Metadata);
        if (isNew) dbContext.Schools.Add(item);

        await dbContext.SaveChangesAsync(cancellationToken);
        return new ContentManagementResult<School>(item);
    }

    public async Task<CatalogList<Major>> GetMajorsAsync(
        DirectContentActor actor,
        AdminLimitFilter filter,
        CancellationToken cancellationToken = default)
    {
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        var regionIds = scope.RegionIds.ToArray();
        var query = dbContext.Majors.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(scope, null, item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
        if (filter.RegionId.HasValue) query = query.Where(item => item.RegionId == filter.RegionId.Value);

        if (filter.SchoolId.HasValue) query = query.Where(item => item.SchoolId == filter.SchoolId.Value);

        if (!string.Equals(filter.Status, "all", StringComparison.OrdinalIgnoreCase))
            query = query.Where(item => item.IsActive);

        if (!string.IsNullOrWhiteSpace(filter.Keyword))
        {
            var keyword = filter.Keyword.Trim();
            query = query.Where(item =>
                item.Name.Contains(keyword) || (item.Description != null && item.Description.Contains(keyword)));
        }

        return new CatalogList<Major>(await query
            .OrderBy(item => item.SortOrder)
            .ThenBy(item => item.Name)
            .Take(ResolveLimit(filter.Limit))
            .ToArrayAsync(cancellationToken));
    }

    public async Task<ContentManagementResult<Major>> UpsertMajorAsync(
        DirectContentActor actor,
        MajorCommand command,
        CancellationToken cancellationToken = default)
    {
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        ArgumentException.ThrowIfNullOrWhiteSpace(command.Name);
        await AssertReferenceAsync<Region>(actor.TenantId, command.RegionId, "region_not_found", cancellationToken);
        await AssertReferenceAsync<School>(actor.TenantId, command.SchoolId, "school_not_found", cancellationToken);
        var item = await ResolveByIdOrLegacyAsync(dbContext.Majors, actor.TenantId, command.Id, command.LegacyId,
            cancellationToken);
        var isNew = item is null;
        EnsureRegionWriteAllowed(scope, actor, item?.RegionId, command.RegionId, isNew, "major_not_found");
        item ??= new Major { Id = command.Id ?? Guid.NewGuid(), TenantId = actor.TenantId };
        item.RegionId = command.RegionId;
        item.SchoolId = command.SchoolId;
        item.LegacyId = Normalize(command.LegacyId);
        item.Name = command.Name.Trim();
        item.Description = Normalize(command.Description);
        item.StudyTips = Normalize(command.StudyTips);
        item.SortOrder = command.Order ?? item.SortOrder;
        item.IsActive = command.IsActive ?? item.IsActive;
        if (isNew) dbContext.Majors.Add(item);

        await dbContext.SaveChangesAsync(cancellationToken);
        return new ContentManagementResult<Major>(item);
    }
}