using System.Collections.Concurrent;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using WebClientTpos.Server.AiChat;

namespace WebClientTpos.Server.Controllers;

/// <summary>
/// EN: AI Chat BFF controller — orchestrates LLM chat with MCP tool execution loop.
///     Supports OpenAI, OpenRouter, and Claude providers. Stores config in-memory (v1).
/// VI: AI Chat BFF controller — dieu phoi LLM chat voi vong lap thuc thi MCP tool.
///     Ho tro provider OpenAI, OpenRouter, va Claude. Luu config trong memory (v1).
/// </summary>
[ApiController]
[Route("api/bff/ai")]
public class AiChatController : ControllerBase
{
    /// <summary>
    /// EN: Maximum number of tool execution loops to prevent infinite recursion.
    /// VI: So vong lap thuc thi tool toi da de ngan de quy vo han.
    /// </summary>
    private const int MaxToolLoops = 5;

    private static readonly ConcurrentDictionary<string, AiChatConfig> Configs = new();

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        PropertyNameCaseInsensitive = true
    };

    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<AiChatController> _logger;

    public AiChatController(IHttpClientFactory httpClientFactory, ILogger<AiChatController> logger)
    {
        _httpClientFactory = httpClientFactory;
        _logger = logger;
    }

    // ═══════════════════════════════════════════════════════════════════════════════
    // EN: Chat endpoint — main orchestration with tool execution loop
    // VI: Endpoint chat — dieu phoi chinh voi vong lap thuc thi tool
    // ═══════════════════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Send a chat message to the configured AI provider, with automatic MCP tool execution.
    /// VI: Gui tin nhan chat den AI provider da cau hinh, voi thuc thi MCP tool tu dong.
    /// </summary>
    [HttpPost("chat")]
    public async Task<IActionResult> Chat([FromBody] AiChatRequestDto request, CancellationToken ct)
    {
        // EN: Validate request
        // VI: Xac thuc request
        if (string.IsNullOrWhiteSpace(request.ShopId))
            return BadRequest(new { success = false, error = new { code = "MISSING_SHOP_ID", message = "shopId is required" } });

        if (request.Messages is null or { Count: 0 })
            return BadRequest(new { success = false, error = new { code = "MISSING_MESSAGES", message = "messages are required" } });

        // EN: Get AI config for this shop
        // VI: Lay AI config cho shop nay
        if (!Configs.TryGetValue(request.ShopId, out var config) || !config.Enabled)
        {
            return BadRequest(new { success = false, error = new { code = "AI_NOT_CONFIGURED", message = "AI chat is not configured or disabled for this shop" } });
        }

        try
        {
            // EN: Build system prompt with shop context
            // VI: Xay dung system prompt voi nguu canh shop
            var systemPrompt = BuildSystemPrompt(config.SystemPrompt, request.ShopId);

            // EN: Get MCP tool definitions for this shop
            // VI: Lay dinh nghia MCP tool cho shop nay
            var shopGuid = Guid.Parse(request.ShopId);
            var tools = McpToolRegistry.GetToolDefinitions(shopGuid);

            // EN: Convert incoming messages to internal format
            // VI: Chuyen doi tin nhan den sang dinh dang noi bo
            var messages = request.Messages.Select(m => new AiChatMessage(m.Role, m.Content)).ToList();

            // EN: Select the appropriate AI provider
            // VI: Chon AI provider phu hop
            var provider = CreateProvider(config.Provider);

            // EN: Tool execution loop — re-send to LLM after each tool execution round
            // VI: Vong lap thuc thi tool — gui lai den LLM sau moi luot thuc thi tool
            var toolsUsed = new List<string>();
            var loopCount = 0;

            while (loopCount < MaxToolLoops)
            {
                var aiRequest = new AiChatRequest(
                    config.Model,
                    config.ApiKey,
                    config.BaseUrl,
                    messages,
                    systemPrompt,
                    tools);

                var aiResponse = await provider.SendAsync(aiRequest, ct);

                if (!aiResponse.RequiresToolExecution || aiResponse.ToolCalls is null)
                {
                    // EN: No more tool calls — return the final response
                    // VI: Khong con tool call — tra ve response cuoi cung
                    return Ok(new
                    {
                        success = true,
                        data = new
                        {
                            content = aiResponse.Content,
                            toolsUsed = toolsUsed.Count > 0 ? toolsUsed : null
                        }
                    });
                }

                // EN: Append assistant message with tool calls to conversation
                // VI: Them tin nhan assistant voi tool calls vao cuoc hoi thoai
                messages.Add(new AiChatMessage(
                    "assistant",
                    aiResponse.Content,
                    aiResponse.ToolCalls));

                // EN: Execute each tool call and append results
                // VI: Thuc thi tung tool call va them ket qua
                var loggerFactory = HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
                var executor = new McpToolExecutor(_httpClientFactory, loggerFactory.CreateLogger<McpToolExecutor>());

                foreach (var toolCall in aiResponse.ToolCalls)
                {
                    _logger.LogInformation(
                        "EN: Executing tool '{ToolName}' (loop {Loop}). " +
                        "VI: Thuc thi tool (vong lap).",
                        toolCall.Name, loopCount + 1);

                    var result = await executor.ExecuteToolAsync(toolCall.Name, toolCall.ArgumentsJson, ct);
                    toolsUsed.Add(toolCall.Name);

                    // EN: Append tool result message to conversation
                    // VI: Them tin nhan ket qua tool vao cuoc hoi thoai
                    messages.Add(new AiChatMessage(
                        "tool",
                        result,
                        ToolCallId: toolCall.Id,
                        Name: toolCall.Name));
                }

                loopCount++;
            }

            // EN: Max loops reached — return whatever we have
            // VI: Da dat so vong toi da — tra ve nhung gi co
            _logger.LogWarning("EN: Max tool loops ({MaxLoops}) reached for shop {Shop}.",
                               MaxToolLoops, request.ShopId);

            return Ok(new
            {
                success = true,
                data = new
                {
                    content = "I've completed the available tool operations. Please let me know if you need anything else.",
                    toolsUsed = toolsUsed.Count > 0 ? toolsUsed : null
                }
            });
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "EN: AI provider request failed for shop {Shop}.", request.ShopId);
            return StatusCode(502, new { success = false, error = new { code = "AI_PROVIDER_ERROR", message = ex.Message } });
        }
        catch (JsonException ex)
        {
            _logger.LogError(ex, "EN: Failed to parse AI response for shop {Shop}.", request.ShopId);
            return StatusCode(502, new { success = false, error = new { code = "AI_PARSE_ERROR", message = "Failed to parse AI provider response" } });
        }
    }

    // ═══════════════════════════════════════════════════════════════════════════════
    // EN: Config endpoints — read/write AI configuration per shop
    // VI: Endpoint cau hinh — doc/ghi AI configuration theo shop
    // ═══════════════════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Get the AI chat configuration for a shop. API key is masked for security.
    /// VI: Lay cau hinh AI chat cho shop. API key duoc an di de bao mat.
    /// </summary>
    [HttpGet("config")]
    public IActionResult GetConfig([FromQuery] string shopId)
    {
        if (string.IsNullOrWhiteSpace(shopId))
            return BadRequest(new { success = false, error = new { code = "MISSING_SHOP_ID", message = "shopId is required" } });

        if (!Configs.TryGetValue(shopId, out var config))
        {
            return Ok(new { success = true, data = (object?)null });
        }

        // EN: Mask the API key — show only last 4 characters
        // VI: An API key — chi hien 4 ky tu cuoi
        var maskedKey = config.ApiKey.Length > 4
            ? new string('*', config.ApiKey.Length - 4) + config.ApiKey[^4..]
            : "****";

        return Ok(new
        {
            success = true,
            data = new
            {
                provider = config.Provider,
                apiKey = maskedKey,
                model = config.Model,
                baseUrl = config.BaseUrl,
                systemPrompt = config.SystemPrompt,
                enabled = config.Enabled
            }
        });
    }

    /// <summary>
    /// EN: Save or update the AI chat configuration for a shop.
    /// VI: Luu hoac cap nhat cau hinh AI chat cho shop.
    /// </summary>
    [HttpPut("config")]
    public IActionResult SaveConfig([FromBody] AiChatConfigDto dto)
    {
        if (string.IsNullOrWhiteSpace(dto.ShopId))
            return BadRequest(new { success = false, error = new { code = "MISSING_SHOP_ID", message = "shopId is required" } });

        if (string.IsNullOrWhiteSpace(dto.Provider))
            return BadRequest(new { success = false, error = new { code = "MISSING_PROVIDER", message = "provider is required (openai, openrouter, claude)" } });

        if (string.IsNullOrWhiteSpace(dto.ApiKey))
            return BadRequest(new { success = false, error = new { code = "MISSING_API_KEY", message = "apiKey is required" } });

        if (string.IsNullOrWhiteSpace(dto.Model))
            return BadRequest(new { success = false, error = new { code = "MISSING_MODEL", message = "model is required" } });

        // EN: Resolve base URL from provider if not explicitly provided
        // VI: Xac dinh base URL tu provider neu khong duoc cung cap
        var baseUrl = dto.BaseUrl ?? dto.Provider.ToLowerInvariant() switch
        {
            "openai" => "https://api.openai.com/v1",
            "openrouter" => "https://openrouter.ai/api/v1",
            "claude" => "https://api.anthropic.com",
            _ => ""
        };

        var config = new AiChatConfig(
            dto.Provider.ToLowerInvariant(),
            dto.ApiKey,
            dto.Model,
            baseUrl,
            dto.SystemPrompt,
            dto.Enabled ?? true);

        Configs[dto.ShopId] = config;

        _logger.LogInformation("EN: AI config saved for shop {Shop} — provider: {Provider}, model: {Model}.",
                               dto.ShopId, config.Provider, config.Model);

        return Ok(new { success = true, data = new { message = "AI chat configuration saved" } });
    }

    // ═══════════════════════════════════════════════════════════════════════════════
    // EN: Private helpers
    // VI: Cac phuong thuc tro giup noi bo
    // ═══════════════════════════════════════════════════════════════════════════════

    /// <summary>
    /// EN: Build system prompt with shop context appended.
    /// VI: Xay dung system prompt voi ngu canh shop duoc them vao.
    /// </summary>
    private static string BuildSystemPrompt(string? customPrompt, string shopId)
    {
        var basePrompt = customPrompt ?? "You are a helpful AI assistant for a aPOS POS shop. " +
                                         "You can help manage products, inventory, recipes, and view analytics. " +
                                         "Answer in the same language the user writes in (Vietnamese or English).";

        return $"{basePrompt}\n\n" +
               $"[Context] Current shop ID: {shopId}. " +
               "When calling tools, use this shop ID if the user doesn't specify one. " +
               "Always confirm destructive actions (delete, large stock changes) before executing.";
    }

    /// <summary>
    /// EN: Create the appropriate AI chat provider based on provider name.
    /// VI: Tao AI chat provider phu hop dua tren ten provider.
    /// </summary>
    private IAiChatProvider CreateProvider(string provider)
    {
        var httpClient = new HttpClient();
        var loggerFactory = HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();

        return provider switch
        {
            "openai" or "openrouter" => new OpenAiChatProvider(httpClient, loggerFactory.CreateLogger<OpenAiChatProvider>()),
            "claude" => new ClaudeChatProvider(httpClient, loggerFactory.CreateLogger<ClaudeChatProvider>()),
            _ => throw new ArgumentException($"Unsupported AI provider: {provider}. Use 'openai', 'openrouter', or 'claude'.")
        };
    }
}

// ═══════════════════════════════════════════════════════════════════════════════
// EN: Request/Response DTOs for the AI Chat controller
// VI: Cac DTO Request/Response cho AI Chat controller
// ═══════════════════════════════════════════════════════════════════════════════

/// <summary>
/// EN: Internal config record stored per shop (in-memory v1).
/// VI: Record cau hinh noi bo luu theo shop (in-memory v1).
/// </summary>
public record AiChatConfig(
    string Provider,
    string ApiKey,
    string Model,
    string BaseUrl,
    string? SystemPrompt,
    bool Enabled);

/// <summary>
/// EN: DTO for the chat request from the frontend.
/// VI: DTO cho chat request tu frontend.
/// </summary>
public class AiChatRequestDto
{
    public string ShopId { get; set; } = "";
    public List<AiChatMessageDto> Messages { get; set; } = [];
}

/// <summary>
/// EN: DTO for a single chat message from the frontend.
/// VI: DTO cho mot tin nhan chat tu frontend.
/// </summary>
public class AiChatMessageDto
{
    public string Role { get; set; } = "user";
    public string Content { get; set; } = "";
}

/// <summary>
/// EN: DTO for saving/updating AI chat configuration.
/// VI: DTO de luu/cap nhat cau hinh AI chat.
/// </summary>
public class AiChatConfigDto
{
    public string ShopId { get; set; } = "";
    public string Provider { get; set; } = "";
    public string ApiKey { get; set; } = "";
    public string Model { get; set; } = "";
    public string? BaseUrl { get; set; }
    public string? SystemPrompt { get; set; }
    public bool? Enabled { get; set; }
}
