using Infineon.Monitoring.MonA;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using OI.Metrology.Archive.Models;
using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Repositories;
using OI.Metrology.Shared.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OI.Metrology.Archive.Controllers;

public class ExportController : Controller
{
    private readonly ILogger _Logger;
    private readonly bool _IsTestDatabase;
    private readonly IMetrologyRepo _Repo;
    private readonly AppSettings _AppSettings;

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

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

    [HttpGet]
    [Route("/Export")]
    public ActionResult Index()
    {
        Export model = new()
        {
            ToolType = "",
            StartTime = DateTime.Now.AddMonths(-1),
            EndTime = DateTime.Now
        };
        MonIn monIn = MonIn.GetInstance();
        _ = monIn.SendStatus(_AppSettings.MonASite, _AppSettings.MonAResource, "Heartbeat", State.Up);
        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 = _Repo.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);
            }
        }
        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();

        System.Data.DataTable dt = _Repo.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))
                _ = r.Append(FormatForCSV(Convert.ToString(v)));
        }
        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)
    {
        StringBuilder r = new(v.Length + 2);

        bool doubleQuoted = false;

        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();
    }

}