using System;
using System.Data.SQLite;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Электронная_Фармация.Properties;
namespace Электронная_Фармация.Classes
{
///
/// Глобальные номера заказов вида EX-0000001.
/// Источник истины — сервер (никогда не повторяются между аптеками).
/// Offline fallback — локальный счётчик EX- (потом сервер может заменить при отправке).
///
public static class OrderNumberHelper
{
private static readonly Regex ExPattern = new Regex(@"^EX-(\d{1,7})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
///
/// Выделяет номер для нового локального заказа.
/// Сначала пробует сервер /api/buyer/global-sign/next, иначе локальный EX-#######.
///
public static string AllocateForNewOrder()
{
try
{
var fromServer = AllocateFromServerAsync().GetAwaiter().GetResult();
if (!string.IsNullOrWhiteSpace(fromServer))
{
return fromServer;
}
}
catch (Exception ex)
{
AppDebugLog.Info("OrderNumber", $"Сервер недоступен для GlobalSign: {ex.Message}");
}
return AllocateLocalFallback();
}
public static async Task AllocateFromServerAsync()
{
var token = Settings.Default.stringToken;
var baseUrl = AppConfig.ApiBaseUrl;
if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(baseUrl))
{
return null;
}
using (var client = new HttpClient { Timeout = TimeSpan.FromSeconds(8) })
{
client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Trim());
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var response = await client.PostAsync("api/buyer/global-sign/next", new StringContent(string.Empty)).ConfigureAwait(false))
{
var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
AppDebugLog.Info("OrderNumber", $"global-sign/next HTTP {(int)response.StatusCode}: {body}");
return null;
}
using (var doc = JsonDocument.Parse(body))
{
if (doc.RootElement.TryGetProperty("global_sign", out var el))
{
var sign = el.GetString();
if (IsValidEx(sign))
{
return Normalize(sign);
}
}
}
}
}
return null;
}
public static string AllocateLocalFallback()
{
long max = 0;
try
{
using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
{
con.Open();
using (var cmd = new SQLiteCommand(
"SELECT OrderNumber FROM Orders WHERE OrderNumber LIKE 'EX-%'", con))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var n = ParseExNumber(reader[0]?.ToString());
if (n > max)
{
max = n;
}
}
}
}
}
catch (Exception ex)
{
AppDebugLog.Info("OrderNumber", $"Локальный max EX-: {ex.Message}");
}
return FormatEx(max + 1);
}
public static bool IsValidEx(string value)
{
return ParseExNumber(value) > 0;
}
public static string Normalize(string value)
{
var n = ParseExNumber(value);
return n > 0 ? FormatEx(n) : value?.Trim();
}
public static string FormatEx(long n)
{
if (n < 1)
{
n = 1;
}
if (n > 9999999)
{
return "EX-" + n.ToString();
}
return "EX-" + n.ToString("D7");
}
public static long ParseExNumber(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return 0;
}
var m = ExPattern.Match(value.Trim());
if (!m.Success)
{
return 0;
}
return long.TryParse(m.Groups[1].Value, out var n) ? n : 0;
}
}
}