using Marco.Pms.Model.Logs;
using Marco.Pms.Model.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Serilog.Context;
using System.Text.Json;

namespace Marco.Pms.Services.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class LogController : ControllerBase
    {
        private readonly ILogger<LogController> _logger;
        public LogController(ILogger<LogController> logger)
        {
            _logger = logger;
        }

        [HttpPost]
        /// <summary>
        /// Receives a batch of log entries from mobile applications and processes them.
        /// Logs are enriched with context properties like UserAgent, Timestamp, and IpAddress.
        /// </summary>
        /// <param name="logs">A list of LogStructure objects containing log details.</param>
        /// <returns>An <see cref="IActionResult"/> indicating the success of the operation.</returns>
        public IActionResult MobileLogging([FromBody] List<LogStructure> logs)
        {
            // Check if logs are provided to avoid processing empty requests
            if (logs == null || !logs.Any())
            {
                _logger.LogWarning("MobileLogging endpoint received an empty or null log list.");
                return BadRequest("No log entries provided.");
            }

            // Iterate through each log entry received from the client
            foreach (var log in logs)
            {
                object? detailsObject = null;

                // Attempt to deserialize the 'Details' string into a dynamic object
                // This allows structured logging of the details payload.
                if (!string.IsNullOrWhiteSpace(log.Details))
                {
                    try
                    {
                        // Use JsonDocument for potentially more efficient parsing if you just need the raw JSON or a dynamic object
                        // For simply logging it as a structured property, object works.
                        detailsObject = JsonSerializer.Deserialize<Dictionary<string, string>>(log.Details);

                    }
                    catch (JsonException ex)
                    {
                        // Log a warning if deserialization fails, but continue processing other logs
                        _logger.LogWarning("Failed to deserialize 'Details' for a log entry. Raw details: '{RawDetails}'. Error: {ErrorMessage}", log.Details, ex.Message);
                    }
                }

                // Push common properties to the Serilog LogContext for the current log operation.
                // These properties will be attached to the log event itself.
                using (LogContext.PushProperty("UserAgent", log.UserAgent))
                using (LogContext.PushProperty("Timestamp", log.TimeStamp))
                using (LogContext.PushProperty("IpAddress", log.IpAddress))
                using (LogContext.PushProperty("LogOrigin", "Mobile Application"))
                {
                    // If details were successfully parsed, push them as a structured property.
                    if (detailsObject != null)
                    {
                        using (LogContext.PushProperty("Details", detailsObject, true)) // 'true' for destructuring
                        {
                            LogByLevel(log.LogLevel, log.Message);
                        }
                    }
                    else
                    {
                        // If details were null or failed to parse, log without the details property
                        LogByLevel(log.LogLevel, log.Message);
                    }
                }
            }

            // Return 200 OK indicating successful receipt of logs.
            // As logging is typically a "fire and forget" operation from the client's perspective,
            // we don't need to await individual log writes here.
            // The logger provider handles the actual writing asynchronously in the background.
            _logger.LogInformation("Successfully processed {LogCount} mobile log entries.", logs.Count);
            return Ok(ApiResponse<object>.SuccessResponse(new { }, "Logs received successfully.", 200));
        }

        /// <summary>
        /// Logs a message at the specified log level using the injected ILogger.
        /// </summary>
        /// <param name="logLevel">The string representation of the log level (e.g., "info", "warning").</param>
        /// <param name="message">The log message.</param>
        private void LogByLevel(string? logLevel, string? message)
        {
            // Default message if null or empty
            message = string.IsNullOrWhiteSpace(message) ? "No message provided." : message;

            // Use a switch expression or simple if/else for clarity based on log level
            switch (logLevel?.ToLowerInvariant()) // Use ToLowerInvariant for case-insensitive comparison
            {
                case "debug":
                    _logger.LogDebug(message);
                    break;
                case "info":
                case "information": // Common alias for info
                    _logger.LogInformation(message);
                    break;
                case "warn":
                case "warning":
                    _logger.LogWarning(message);
                    break;
                case "error":
                    _logger.LogError(message);
                    break;
                case "critical":
                    _logger.LogCritical(message);
                    break;
                default:
                    // Log unknown levels as information to ensure they are captured
                    _logger.LogInformation("[Unknown Level] {Message}", message);
                    break;
            }
        }
    }
}
