using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using WebClientTpos.Server.Infrastructure;

namespace WebClientTpos.Server.Controllers;

/// <summary>
/// EN: Shop management controller — proxies to MerchantService for shop CRUD, settings, stats, devices.
///     Stats endpoint aggregates data from Merchant, Catalog, and Order services.
/// VI: Controller quản lý cửa hàng — proxy đến MerchantService cho CRUD shop, settings, stats, devices.
///     Endpoint stats tổng hợp dữ liệu từ Merchant, Catalog, và Order services.
/// </summary>
[ApiController]
[Route("api/bff")]
public class ShopController : ControllerBase
{
    private readonly HttpClient _merchant;
    private readonly HttpClient _catalog;
    private readonly HttpClient _order;
    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        PropertyNameCaseInsensitive = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };

    public ShopController(IHttpClientFactory httpClientFactory)
    {
        _merchant = httpClientFactory.CreateClient("MerchantService");
        _catalog = httpClientFactory.CreateClient("CatalogService");
        _order = httpClientFactory.CreateClient("OrderService");
    }

    /// <summary>
    /// EN: Get all shops belonging to the current merchant.
    /// VI: Lấy tất cả cửa hàng thuộc merchant hiện tại.
    /// </summary>
    [HttpGet("shops")]
    public Task<IActionResult> GetShops() =>
        _merchant.GetAsync("/api/v1/shops").ProxyAsync();

    /// <summary>
    /// EN: Get shop by ID.
    /// VI: Lấy cửa hàng theo ID.
    /// </summary>
    [HttpGet("shops/{shopId:guid}")]
    public Task<IActionResult> GetShopById(Guid shopId) =>
        _merchant.GetAsync($"/api/v1/shops/{shopId}").ProxyAsync();

    /// <summary>
    /// EN: Update shop info.
    /// VI: Cập nhật thông tin cửa hàng.
    /// </summary>
    [HttpPut("shops/{shopId:guid}")]
    public Task<IActionResult> UpdateShop(Guid shopId, [FromBody] JsonElement body) =>
        _merchant.PutAsJsonAsync($"/api/v1/shops/{shopId}", body).ProxyAsync();

    /// <summary>
    /// EN: Get shop settings.
    /// VI: Lấy cài đặt cửa hàng.
    /// </summary>
    [HttpGet("shops/{shopId:guid}/settings")]
    public Task<IActionResult> GetShopSettings(Guid shopId) =>
        _merchant.GetAsync($"/api/v1/shops/{shopId}/settings").ProxyAsync();

    /// <summary>
    /// EN: Update shop settings.
    /// VI: Cập nhật cài đặt cửa hàng.
    /// </summary>
    [HttpPut("shops/{shopId:guid}/settings")]
    public Task<IActionResult> UpdateShopSettings(Guid shopId, [FromBody] JsonElement body) =>
        _merchant.PutAsJsonAsync($"/api/v1/shops/{shopId}/settings", body).ProxyAsync();

    /// <summary>
    /// EN: Get aggregated stats per shop — calls Merchant (shops+staff), Catalog (product count), Order (revenue+orders) in parallel.
    /// VI: Lấy thống kê tổng hợp theo shop — gọi Merchant (shops+staff), Catalog (số SP), Order (doanh thu+đơn) song song.
    /// </summary>
    [HttpGet("shops/stats")]
    public async Task<IActionResult> GetShopStats()
    {
        // Step 1: Get shops list from Merchant service
        var shopsResponse = await _merchant.GetAsync("/api/v1/shops");
        if (!shopsResponse.IsSuccessStatusCode)
            return StatusCode((int)shopsResponse.StatusCode, "Failed to load shops");

        var shopsJson = await shopsResponse.Content.ReadAsStringAsync();
        var shops = JsonSerializer.Deserialize<List<ShopBasicDto>>(shopsJson, JsonOpts) ?? [];
        if (shops.Count == 0)
            return Ok("[]");

        // Step 2: Fetch staff list once (shared across all shops) + per-shop stats in parallel
        var staffMapTask = GetStaffCountMapAsync();
        var perShopTasks = shops.Select(async shop =>
        {
            var productTask = GetProductCountAsync(shop.Id);
            var orderTask = GetOrderStatsAsync(shop.Id);
            await Task.WhenAll(productTask, orderTask);
            return (shop.Id, ProductCount: await productTask, OrderStats: await orderTask);
        });

        await Task.WhenAll(staffMapTask, Task.WhenAll(perShopTasks));
        var staffMap = await staffMapTask;
        var perShopResults = await Task.WhenAll(perShopTasks);

        var results = perShopResults.Select(r => new
        {
            shopId = r.Id,
            productCount = r.ProductCount,
            orderCount = r.OrderStats.OrderCount,
            staffCount = staffMap.GetValueOrDefault(r.Id, 0),
            revenue = r.OrderStats.Revenue
        });

        return new ContentResult
        {
            StatusCode = 200,
            Content = JsonSerializer.Serialize(results, JsonOpts),
            ContentType = "application/json"
        };
    }

    private async Task<int> GetProductCountAsync(Guid shopId)
    {
        try
        {
            var resp = await _catalog.GetAsync($"/api/v1/products?shopId={shopId}&pageSize=1");
            if (!resp.IsSuccessStatusCode) return 0;
            var json = await resp.Content.ReadAsStringAsync();
            var doc = JsonDocument.Parse(json);
            // Try common paged response shapes
            if (doc.RootElement.TryGetProperty("totalCount", out var tc)) return tc.GetInt32();
            if (doc.RootElement.TryGetProperty("TotalCount", out var tc2)) return tc2.GetInt32();
            if (doc.RootElement.TryGetProperty("total", out var t)) return t.GetInt32();
            if (doc.RootElement.ValueKind == JsonValueKind.Array) return doc.RootElement.GetArrayLength();
            return 0;
        }
        catch { return 0; }
    }

    private async Task<(int OrderCount, decimal Revenue)> GetOrderStatsAsync(Guid shopId)
    {
        try
        {
            // Primary: Use POS dashboard endpoint which has Revenue + OrderCount
            var resp = await _order.GetAsync($"/api/v1/orders/dashboard?shopId={shopId}&period=30d");
            if (resp.IsSuccessStatusCode)
            {
                var json = await resp.Content.ReadAsStringAsync();
                var doc = JsonDocument.Parse(json);
                var orders = 0;
                var revenue = 0m;
                if (doc.RootElement.TryGetProperty("orderCount", out var oc)) orders = (int)oc.GetInt64();
                if (doc.RootElement.TryGetProperty("revenue", out var rv)) revenue = rv.GetDecimal();
                return (orders, revenue);
            }
            // Fallback: Use orders list to get totalCount
            var ordersResp = await _order.GetAsync($"/api/v1/orders?shopId={shopId}&pageSize=1");
            if (!ordersResp.IsSuccessStatusCode) return (0, 0m);
            var ordersJson = await ordersResp.Content.ReadAsStringAsync();
            var ordersDoc = JsonDocument.Parse(ordersJson);
            if (ordersDoc.RootElement.TryGetProperty("totalCount", out var otc))
                return (otc.GetInt32(), 0m);
            return (0, 0m);
        }
        catch { return (0, 0m); }
    }

    /// <summary>
    /// EN: Fetch all merchant staff once and return a per-shop count map.
    /// VI: Lấy tất cả staff merchant 1 lần và trả về map đếm theo shop.
    /// </summary>
    private async Task<Dictionary<Guid, int>> GetStaffCountMapAsync()
    {
        var map = new Dictionary<Guid, int>();
        try
        {
            var resp = await _merchant.GetAsync("/api/v1/merchants/me/staff");
            if (!resp.IsSuccessStatusCode) return map;
            var json = await resp.Content.ReadAsStringAsync();
            var doc = JsonDocument.Parse(json);
            if (doc.RootElement.ValueKind != JsonValueKind.Array) return map;
            foreach (var staff in doc.RootElement.EnumerateArray())
            {
                if (!staff.TryGetProperty("shopAssignments", out var assignments)) continue;
                foreach (var assignment in assignments.EnumerateArray())
                {
                    if (assignment.TryGetProperty("shopId", out var sid) &&
                        Guid.TryParse(sid.GetString(), out var assignedShopId))
                    {
                        map[assignedShopId] = map.GetValueOrDefault(assignedShopId, 0) + 1;
                    }
                }
            }
        }
        catch { /* silent fallback */ }
        return map;
    }

    private record ShopBasicDto
    {
        public Guid Id { get; init; }
        public string? Name { get; init; }
    }

    /// <summary>
    /// EN: Publish a shop (draft → active).
    /// VI: Xuất bản cửa hàng (draft → active).
    /// </summary>
    [HttpPost("shops/{shopId:guid}/publish")]
    public Task<IActionResult> PublishShop(Guid shopId) =>
        _merchant.PostAsync($"/api/v1/shops/{shopId}/publish", null).ProxyAsync();

    /// <summary>
    /// EN: Deactivate a shop (set inactive, can be reactivated later).
    /// VI: Tạm ngưng hoạt động cửa hàng (có thể kích hoạt lại sau).
    /// </summary>
    [HttpPost("shops/{shopId:guid}/deactivate")]
    public Task<IActionResult> DeactivateShop(Guid shopId) =>
        _merchant.PostAsync($"/api/v1/shops/{shopId}/deactivate", null).ProxyAsync();

    /// <summary>
    /// EN: Close a shop permanently (irreversible).
    /// VI: Đóng cửa hàng vĩnh viễn (không thể hoàn tác).
    /// </summary>
    [HttpPost("shops/{shopId:guid}/close")]
    public Task<IActionResult> CloseShop(Guid shopId) =>
        _merchant.PostAsync($"/api/v1/shops/{shopId}/close", null).ProxyAsync();

    /// <summary>
    /// EN: Get device tokens registered for this merchant's staff.
    /// VI: Lấy danh sách device token đã đăng ký cho nhân viên của merchant.
    /// </summary>
    [HttpGet("devices")]
    public Task<IActionResult> GetDevices() =>
        _merchant.GetAsync("/api/v1/devices").ProxyAsync();
}
