using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using NPin.Framework.Ddd.Application;
using NPin.Framework.SqlSugarCore.Abstractions;
using NPin.Framework.TenantManagement.Application.Contracts;
using NPin.Framework.TenantManagement.Application.Contracts.Dtos;
using NPin.Framework.TenantManagement.Domain;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;

namespace NPin.Framework.TenantManagement.Application;

public class TenantService : NPinCrudAppService<TenantAggregateRoot, TenantGetOutputDto, TenantGetListOutputDto, Guid,
    TenantGetListInput, TenantCreateInput, TenantUpdateInput>, ITenantService
{
    private ISqlSugarRepository<TenantAggregateRoot, Guid> _repository;
    private IDataSeeder _dataSeeder;

    public TenantService(IRepository<TenantAggregateRoot, Guid> repository,
        ISqlSugarRepository<TenantAggregateRoot, Guid> sqlSugarRepository, IDataSeeder dataSeeder) : base(repository)
    {
        _repository = sqlSugarRepository;
        _dataSeeder = dataSeeder;
    }

    /// <summary>
    /// 多查
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public override async Task<PagedResultDto<TenantGetListOutputDto>> GetListAsync(TenantGetListInput input)
    {
        RefAsync<int> total = 0;
        var entities = await _repository.DbQueryable
            .WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!))
            .WhereIF(input.StartTime is not null && input.EndTime is not null,
                x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
            .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
        return new PagedResultDto<TenantGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
    }

    /// <summary>
    /// 租户选项列表（显示名称）
    /// </summary>
    /// <returns></returns>
    public async Task<List<TenantSelectOutputDto>> GetSelectAsync()
    {
        var entities = await _repository.DbQueryable.ToListAsync();
        return entities.Select(x => new TenantSelectOutputDto { Id = x.Id, Name = x.Name })
            .ToList();
    }

    /// <summary>
    /// 创建租户
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public override async Task<TenantGetOutputDto> CreateAsync(TenantCreateInput input)
    {
        // 检查是否存在
        if (await _repository.IsAnyAsync(x => x.Name == input.Name))
        {
            throw new UserFriendlyException("创建失败，当前租户已存在");
        }

        return await base.CreateAsync(input);
    }

    /// <summary>
    /// 更新租户信息
    /// </summary>
    /// <param name="id"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    public override async Task<TenantGetOutputDto> UpdateAsync(Guid id, TenantUpdateInput input)
    {
        if (await _repository.IsAnyAsync(x => x.Name == input.Name && x.Id != id))
        {
            throw new UserFriendlyException("更新失败，更新后租户已存在");
        }

        return await base.UpdateAsync(id, input);
    }

    // 只是为了可以在swagger上展示?
    /// <summary>
    /// 租户删除
    /// </summary>
    /// <param name="ids"></param>
    /// <returns></returns>
    public override Task DeleteAsync(IEnumerable<Guid> ids)
    {
        return base.DeleteAsync(ids);
    }

    /// <summary>
    /// 初始化租户
    /// </summary>
    /// <param name="id"></param>
    [HttpPost("tenant/init/{id}")]
    public async Task InitAsync([FromRoute] Guid id)
    {
        using (CurrentTenant.Change(id))
        {
            // 初始化 租户数据库/表 结构
            await CodeFirst(LazyServiceProvider);
            // 插入 种子数据
            await _dataSeeder.SeedAsync(id);
        }
    }

    /// <summary>
    /// 数据库 / 表 初始化
    /// </summary>
    /// <param name="service"></param>
    private async Task CodeFirst(IServiceProvider service)
    {
        var moduleContainer = service.GetRequiredService<IModuleContainer>();
        var db = await _repository.GetDbContextAsync();

        // 尝试创建数据库
        db.DbMaintenance.CreateDatabase();

        List<Type> types = new List<Type>();
        foreach (var module in moduleContainer.Modules)
        {
            types.AddRange(module.Assembly.GetTypes()
                .Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
                .Where(x => x.GetCustomAttribute<SugarTable>() != null)
                .Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
        }

        if (types.Count > 0)
        {
            db.CopyNew().CodeFirst.InitTables(types.ToArray());
        }
    }
}