using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DiunaBI.Infrastructure.Data;
using DiunaBI.Domain.Entities;
using DiunaBI.Application.DTOModels;
using DiunaBI.Application.DTOModels.Common;

namespace DiunaBI.API.Controllers;

[Authorize]
[ApiController]
[Route("[controller]")]
public class DataInboxController : Controller
{
    private readonly AppDbContext _db;
    private readonly IConfiguration _configuration;
    private readonly ILogger<DataInboxController> _logger;

    public DataInboxController(
        AppDbContext db,
        IConfiguration configuration,
        ILogger<DataInboxController> logger)
    {
        _db = db;
        _configuration = configuration;
        _logger = logger;
    }

    [HttpPut]
    [Route("Add/{apiKey}")]
    [AllowAnonymous]
    public IActionResult Add(string apiKey, [FromBody] DataInbox dataInbox)
    {
        if (apiKey != _configuration["apiKey"])
        {
            _logger.LogWarning("DataInbox: Unauthorized request - wrong apiKey for source {Source}", dataInbox.Source);
            return Unauthorized();
        }

        try
        {
            if (!Request.Headers.TryGetValue("Authorization", out var authHeader))
            {
                _logger.LogWarning("DataInbox: Unauthorized request - no authorization header for source {Source}", dataInbox.Source);
                return Unauthorized();
            }

            var credentialsArr = authHeader.ToString().Split(" ");
            if (credentialsArr.Length != 2)
            {
                _logger.LogWarning("DataInbox: Unauthorized request - wrong auth header format for source {Source}", dataInbox.Source);
                return Unauthorized();
            }

            var authValue = Encoding.UTF8.GetString(Convert.FromBase64String(credentialsArr[1]));
            var username = authValue.Split(':')[0];
            var password = authValue.Split(':')[1];
            if (username != _configuration["apiUser"] || password != _configuration["apiPass"])
            {
                _logger.LogWarning("DataInbox: Unauthorized request - bad credentials for source {Source}", dataInbox.Source);
                return Unauthorized();
            }

            // check if datainbox.data is base64 encoded value
            if (!string.IsNullOrEmpty(dataInbox.Data))
            {
                // Limit data size to 10MB to prevent DoS
                if (dataInbox.Data.Length > 10_000_000)
                {
                    _logger.LogWarning("DataInbox: Data too large for source {Source}, size {Size}", dataInbox.Source, dataInbox.Data.Length);
                    return BadRequest("Data too large (max 10MB)");
                }

                if (!IsBase64String(dataInbox.Data))
                {
                    _logger.LogWarning("DataInbox: Invalid data format - not base64 encoded for source {Source}", dataInbox.Source);
                    return BadRequest("Invalid data format - not base64 encoded");
                }
            }

            dataInbox.Id = Guid.NewGuid();
            dataInbox.CreatedAt = DateTime.UtcNow;
            _db.DataInbox.Add(dataInbox);
            _db.SaveChanges();

            _logger.LogInformation("DataInbox: Insert success for source {Source}, name {Name}", dataInbox.Source, dataInbox.Name);

            if (dataInbox.Name == "morska.d3.importer")
            {
                _logger.LogDebug("DataInbox: Detected morska.d3.importer - processing will be handled by AutoImport");
            }

            return Ok();
        }
        catch (Exception e)
        {
            _logger.LogError(e, "DataInbox: Insert error for source {Source}, name {Name}", dataInbox.Source, dataInbox.Name);
            return BadRequest("An error occurred processing your request");
        }
    }

    [HttpGet]
    [Route("GetAll")]
    public IActionResult GetAll([FromQuery] int start, [FromQuery] int limit, [FromQuery] string? search)
    {
        try
        {
            // Validate pagination parameters
            if (limit <= 0 || limit > 1000)
            {
                return BadRequest("Limit must be between 1 and 1000");
            }
            if (start < 0)
            {
                return BadRequest("Start must be non-negative");
            }

            var query = _db.DataInbox.AsQueryable();

            if (!string.IsNullOrEmpty(search))
            {
                query = query.Where(x => x.Name.Contains(search) || x.Source.Contains(search));
            }

            var totalCount = query.Count();

            var items = query
                .OrderByDescending(x => x.CreatedAt)
                .Skip(start)
                .Take(limit)
                .AsNoTracking()
                .Select(x => new DataInboxDto
                {
                    Id = x.Id,
                    Name = x.Name,
                    Source = x.Source,
                    Data = x.Data,
                    CreatedAt = x.CreatedAt
                })
                .ToList();

            var pagedResult = new PagedResult<DataInboxDto>
            {
                Items = items,
                TotalCount = totalCount,
                Page = (start / limit) + 1,
                PageSize = limit
            };

            _logger.LogDebug("GetAll: Retrieved {Count} of {TotalCount} data inbox items (page {Page}) with filter search={Search}",
                items.Count, totalCount, pagedResult.Page, search);

            return Ok(pagedResult);
        }
        catch (Exception e)
        {
            _logger.LogError(e, "GetAll: Error retrieving data inbox items");
            return BadRequest("An error occurred processing your request");
        }
    }

    [HttpGet]
    [Route("{id:guid}")]
    public IActionResult Get(Guid id)
    {
        try
        {
            var dataInbox = _db.DataInbox
                .AsNoTracking()
                .FirstOrDefault(x => x.Id == id);

            if (dataInbox == null)
            {
                _logger.LogWarning("Get: Data inbox item {Id} not found", id);
                return NotFound();
            }

            var dto = new DataInboxDto
            {
                Id = dataInbox.Id,
                Name = dataInbox.Name,
                Source = dataInbox.Source,
                Data = dataInbox.Data,
                CreatedAt = dataInbox.CreatedAt
            };

            _logger.LogDebug("Get: Retrieved data inbox item {Id} {Name}", id, dataInbox.Name);
            return Ok(dto);
        }
        catch (Exception e)
        {
            _logger.LogError(e, "Get: Error retrieving data inbox item {Id}", id);
            return BadRequest("An error occurred processing your request");
        }
    }

    // helpers
    private bool IsBase64String(string data)
    {
        if (string.IsNullOrEmpty(data))
        {
            return false;
        }
        try
        {
            var base64Bytes = Convert.FromBase64String(data);
            var utf8String = Encoding.UTF8.GetString(base64Bytes);
            var reEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(utf8String));
            return data.TrimEnd('=') == reEncoded.TrimEnd('=');
        }
        catch (FormatException)
        {
            return false;
        }
        catch (DecoderFallbackException)
        {
            return false;
        }
    }
}