using TakeoutSaaS.Domain.Common.Enums;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Domain.Tenants.Entities;

namespace TakeoutSaaS.Application.App.Common.Geo;

/// <summary>
/// 地理定位状态写入助手。
/// </summary>
public static class GeoLocationStateHelper
{
    /// <summary>
    /// 最大重试次数。
    /// </summary>
    public const int MaxRetryCount = 5;

    /// <summary>
    /// 获取重试间隔。
    /// </summary>
    public static TimeSpan GetRetryDelay(int retryCount)
    {
        return retryCount switch
        {
            <= 1 => TimeSpan.FromMinutes(1),
            2 => TimeSpan.FromMinutes(5),
            3 => TimeSpan.FromMinutes(30),
            4 => TimeSpan.FromHours(2),
            _ => TimeSpan.FromHours(12)
        };
    }

    /// <summary>
    /// 写入门店定位成功状态。
    /// </summary>
    public static void MarkSuccess(Store store, double latitude, double longitude, DateTime now)
    {
        store.Latitude = latitude;
        store.Longitude = longitude;
        store.GeoStatus = GeoLocationStatus.Success;
        store.GeoFailReason = null;
        store.GeoRetryCount = 0;
        store.GeoUpdatedAt = now;
        store.GeoNextRetryAt = null;
    }

    /// <summary>
    /// 写入商户定位成功状态。
    /// </summary>
    public static void MarkSuccess(Merchant merchant, double latitude, double longitude, DateTime now)
    {
        merchant.Latitude = latitude;
        merchant.Longitude = longitude;
        merchant.GeoStatus = GeoLocationStatus.Success;
        merchant.GeoFailReason = null;
        merchant.GeoRetryCount = 0;
        merchant.GeoUpdatedAt = now;
        merchant.GeoNextRetryAt = null;
    }

    /// <summary>
    /// 写入租户定位成功状态。
    /// </summary>
    public static void MarkSuccess(Tenant tenant, double latitude, double longitude, DateTime now)
    {
        tenant.Latitude = latitude;
        tenant.Longitude = longitude;
        tenant.GeoStatus = GeoLocationStatus.Success;
        tenant.GeoFailReason = null;
        tenant.GeoRetryCount = 0;
        tenant.GeoUpdatedAt = now;
        tenant.GeoNextRetryAt = null;
    }

    /// <summary>
    /// 写入门店待重试状态。
    /// </summary>
    public static void MarkPending(Store store, string? reason, DateTime now)
    {
        store.GeoStatus = GeoLocationStatus.Pending;
        store.GeoFailReason = reason;
        store.GeoRetryCount = 0;
        store.GeoNextRetryAt = now.Add(GetRetryDelay(1));
    }

    /// <summary>
    /// 写入商户待重试状态。
    /// </summary>
    public static void MarkPending(Merchant merchant, string? reason, DateTime now)
    {
        merchant.GeoStatus = GeoLocationStatus.Pending;
        merchant.GeoFailReason = reason;
        merchant.GeoRetryCount = 0;
        merchant.GeoNextRetryAt = now.Add(GetRetryDelay(1));
    }

    /// <summary>
    /// 写入租户待重试状态。
    /// </summary>
    public static void MarkPending(Tenant tenant, string? reason, DateTime now)
    {
        tenant.GeoStatus = GeoLocationStatus.Pending;
        tenant.GeoFailReason = reason;
        tenant.GeoRetryCount = 0;
        tenant.GeoNextRetryAt = now.Add(GetRetryDelay(1));
    }

    /// <summary>
    /// 写入门店重试失败状态。
    /// </summary>
    public static void MarkRetryFailure(Store store, string? reason, DateTime now)
    {
        var nextRetryCount = store.GeoRetryCount + 1;
        store.GeoRetryCount = nextRetryCount;
        store.GeoFailReason = reason;
        if (nextRetryCount >= MaxRetryCount)
        {
            store.GeoStatus = GeoLocationStatus.Failed;
            store.GeoNextRetryAt = null;
            return;
        }

        store.GeoStatus = GeoLocationStatus.Pending;
        store.GeoNextRetryAt = now.Add(GetRetryDelay(nextRetryCount + 1));
    }

    /// <summary>
    /// 写入商户重试失败状态。
    /// </summary>
    public static void MarkRetryFailure(Merchant merchant, string? reason, DateTime now)
    {
        var nextRetryCount = merchant.GeoRetryCount + 1;
        merchant.GeoRetryCount = nextRetryCount;
        merchant.GeoFailReason = reason;
        if (nextRetryCount >= MaxRetryCount)
        {
            merchant.GeoStatus = GeoLocationStatus.Failed;
            merchant.GeoNextRetryAt = null;
            return;
        }

        merchant.GeoStatus = GeoLocationStatus.Pending;
        merchant.GeoNextRetryAt = now.Add(GetRetryDelay(nextRetryCount + 1));
    }

    /// <summary>
    /// 写入租户重试失败状态。
    /// </summary>
    public static void MarkRetryFailure(Tenant tenant, string? reason, DateTime now)
    {
        var nextRetryCount = tenant.GeoRetryCount + 1;
        tenant.GeoRetryCount = nextRetryCount;
        tenant.GeoFailReason = reason;
        if (nextRetryCount >= MaxRetryCount)
        {
            tenant.GeoStatus = GeoLocationStatus.Failed;
            tenant.GeoNextRetryAt = null;
            return;
        }

        tenant.GeoStatus = GeoLocationStatus.Pending;
        tenant.GeoNextRetryAt = now.Add(GetRetryDelay(nextRetryCount + 1));
    }
}
