using Infineon.Monitoring.MonA;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using OI.Metrology.Server.Models;
using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Models.Stateless;
using OI.Metrology.Shared.ViewModels;
using System.Text;

namespace OI.Metrology.Server.Controllers;

public class ExportController : Controller
{

    private readonly ILogger _Logger;
    private readonly AppSettings _AppSettings;
    private readonly bool _IsTestDatabase;
    private readonly IMetrologyRepository _MetrologyRepository;

    public ExportController(AppSettings appSettings, ILogger<ExportController> logger, IMetrologyRepository metrologyRepository)
    {
        _Logger = logger;
        _AppSettings = appSettings;
        _MetrologyRepository = metrologyRepository;
        _IsTestDatabase = appSettings.ConnectionString.Contains("test", StringComparison.InvariantCultureIgnoreCase);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        base.OnActionExecuted(context);
        ViewBag.IsTestDatabase = _IsTestDatabase;
    }

    private string GetApiUrl() => string.IsNullOrEmpty(_AppSettings.ApiUrl) ? Url.Content("~/") : _AppSettings.ApiUrl[0] == '~' ? Url.Content(_AppSettings.ApiUrl) : _AppSettings.ApiUrl;

    [HttpGet]
    [Route("/Export")]
    public ActionResult Index()
    {
        Export model = new()
        {
            StartTime = DateTime.Now.AddMonths(-1),
            EndTime = DateTime.Now
        };
        MonIn monIn = MonIn.GetInstance();
        _ = monIn.SendStatus(_AppSettings.MonASite, _AppSettings.MonAResource, "Heartbeat", State.Up);
        ViewBag.ApiUrl = GetApiUrl();
        return View(model);
    }

    [HttpPost]
    [Route("/ExportData")]
    public ActionResult ExportData(Export model)
    {
        ToolType? toolType = null;
        if (string.IsNullOrEmpty(model.ToolType))
            ModelState.AddModelError("Exception", "Invalid selection");
        else
        {
            if (model.StartTime > model.EndTime)
                ModelState.AddModelError("EndTime", "End time must be after start time");
            IEnumerable<ToolType> toolTypes = _MetrologyRepository.GetToolTypes();
            toolType = toolTypes.Where(tt => tt.ID.ToString() == model.ToolType).SingleOrDefault();
            if (toolType is null)
                ModelState.AddModelError("ToolType", "Invalid selection");
            else if (string.IsNullOrWhiteSpace(toolType.ExportSPName))
                ModelState.AddModelError("ToolType", "Tool type is not exportable");
        }
        if (ModelState.IsValid)
        {
            try
            {
                DateTime startDT = model.StartDate.Date.Add(model.StartTime.TimeOfDay);
                DateTime endDT = model.EndDate.Date.Add(model.EndTime.TimeOfDay);

                return DoCSVExport(toolType?.ToolTypeName, toolType?.ExportSPName, startDT, endDT);
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("Exception", "Error exporting data");
                ModelState.AddModelError("Exception", ex.Message);
                string errorMessage = $"Error exporting: {ex}";
                _Logger.LogError(message: errorMessage);
                MonIn monIn = MonIn.GetInstance();
                _ = monIn.SendStatus(_AppSettings.MonASite, _AppSettings.MonAResource, "Heartbeat", State.Warning);
            }
        }
        ViewBag.ApiUrl = GetApiUrl();
        return View("Index", model);
    }

    protected ActionResult DoCSVExport(string? toolTypeName, string? spName, DateTime startTime, DateTime endTime)
    {
        string fileName = string.Format("Export_{0}_{1:yyyyMMddHHmm}_to_{2:yyyyMMddHHmm}.csv", toolTypeName, startTime, endTime);
        StringBuilder sb = new();
        if (spName is null)
            throw new NullReferenceException(nameof(spName));
        System.Data.DataTable dt = _MetrologyRepository.ExportData(spName, startTime, endTime);
        _ = sb.AppendLine(GetColumnHeaders(dt));
        foreach (System.Data.DataRow dr in dt.Rows)
            _ = sb.AppendLine(GetRowData(dr));
        byte[] contents = Encoding.UTF8.GetBytes(sb.ToString());
        return File(contents, "application/octet-stream", fileName);
    }

    protected static string GetRowData(System.Data.DataRow dr)
    {
        StringBuilder r = new();
        for (int i = 0; i < dr.Table.Columns.Count; i++)
        {
            if (i > 0)
                _ = r.Append(',');
            object v = dr[i];
            if (!Convert.IsDBNull(v))
            {
                string? c = Convert.ToString(v);
                if (c is not null)
                    _ = r.Append(FormatForCSV(c));
            }
        }
        return r.ToString();
    }

    protected static string GetColumnHeaders(System.Data.DataTable dt)
    {
        StringBuilder r = new();
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            if (i > 0)
                _ = r.Append(',');

            _ = r.Append(FormatForCSV(dt.Columns[i].ColumnName.TrimEnd('_')));
        }
        return r.ToString();
    }

    protected static string FormatForCSV(string v)
    {
        bool doubleQuoted = false;
        StringBuilder r = new(v.Length + 2);
        if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"'))
        {
            _ = r.Append('"');
            doubleQuoted = true;
        }
        foreach (char c in v)
        {
            _ = c switch
            {
                '\r' or '\n' => r.Append(' '),
                '"' => r.Append("\"\""),
                _ => r.Append(c),
            };
        }
        if (doubleQuoted)
            _ = r.Append('"');
        return r.ToString();
    }

}