using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using DigitalData.Modules.Interfaces;
using DigitalData.Modules.Logging;
using static DigitalData.Modules.Interfaces.Exceptions;
using static DigitalData.Modules.Interfaces.ZUGFeRDInterface;

namespace ZUGFeRDRESTService.Controllers
{    
    [Route("api/[controller]")]
    [ApiController]
    public class ValidationController : ControllerBase
    {
        public static string RESPONSE_OK = "OK";
        public static string RESPONSE_ERROR = "ERROR";

        private readonly ZUGFeRDInterface _zugferd;
        private readonly IDatabase _database;

        private readonly DigitalData.Modules.Logging.LogConfig _logConfig;
        private readonly DigitalData.Modules.Logging.Logger _logger;

        private readonly PropertyValues _props;
        private readonly Dictionary<String, XmlItemProperty> _propertyMap;

        public class ValidationResponse
        {
            public string status;
            public string message;
            public List<string> errors;
        
            public ValidationResponse()
            {
                status = RESPONSE_OK;
                message = string.Empty;
                errors = new List<string>();
            }

            public ValidationResponse(string Status, string Message)
            {
                status = Status;
                message = Message;
                errors = new List<string>();
            }

            public ValidationResponse(string Status, string Message, List<String> Errors)
            {
                status = Status;
                message = Message;
                errors = Errors;
            }
        }

        public ValidationController(ILogging logging, IDatabase database)
        {
            _logConfig = logging.LogConfig;
            _logger = _logConfig.GetLogger();

            _logger.Debug("Validation Controller initializing");

            _database = database;
            var oGDPictureKey = database.GetGDPictureKey();
            var oPropertyMap = database.GetPropertyMap();

            _propertyMap = oPropertyMap;

            _zugferd = new ZUGFeRDInterface(_logConfig, oGDPictureKey);
            _props = new PropertyValues(_logConfig);

            _logger.Debug("Validation Controller initialized!");
        }

        /// <summary>
        /// POST: /api/validation
        /// </summary>
        /// <param name="files">This parameter's name needs to correspond to the html form's file-input name</param>
        [HttpPost]
        public async Task<ValidationResponse> Post(IFormFile file, string user_id)
        {
            _logger.Debug("Start processing request to ValidationController");      

            object oDocument;
            PropertyValues.CheckPropertyValuesResult oResult = new PropertyValues.CheckPropertyValuesResult();

            try
            {               
                using Stream oStream = file.OpenReadStream();
                {
                    _logger.Debug("Extracting ZUGFeRD Data from file [{0}]", file.FileName);

                    oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oStream);

                    _logger.Debug("Checking ZUGFeRD Data against the database");

                    oResult = _props.CheckPropertyValues(oDocument, _propertyMap, "MESSAGEID");

                    var oRequiredProperties = _propertyMap.
                        Where(prop => {return prop.Value.IsRequired == true;}).
                        Select(prop => { return prop.Value.Description; })
                        .ToList();

                    _logger.Debug("Found [{0}] required properties", oRequiredProperties.Count);
                    _logger.Debug(String.Join(",", oRequiredProperties.ToArray()));

                    _logger.Debug("Result of checking against the database: {0} valid properties, {1} missing properties",
                        oResult.ValidProperties.Count, oResult.MissingProperties.Count);

                    if (oResult.MissingProperties.Count > 0)
                    {
                        throw new ZUGFeRDExecption(ZUGFeRDInterface.ErrorType.MissingProperties,
                            "Die hochgeladene Datei ist eine gültige ZUGFeRD-Rechnung, allerdings fehlen benötigte Daten.");
                    }

                    string oMessage = "Die hochgeladene Datei ist eine gültige-ZUGFeRD Rechnung";

                    _logger.Debug($"Replying with: [{oMessage}]");

                    return new ValidationResponse()
                    {
                        status = RESPONSE_OK,
                        message = oMessage
                    };
                };
            }
            catch (ZUGFeRDExecption ex)
            {              
                _logger.Error(ex);

                // Determine which message should be sent in the response
                string oMessage = ex.ErrorType switch
                {
                    ErrorType.NoValidFile => "Die hochgeladene Datei ist keine gültige Datei.",
                    ErrorType.NoZugferd => "Die hochgeladene Datei ist keine ZUGFeRD-Rechnung.",
                    ErrorType.NoValidZugferd => "Die hochgeladene Datei ist keine gültige ZUGFeRD-Rechnung.",
                    ErrorType.MissingProperties => "Die hochgeladene Datei ist keine gültige ZUGFeRD-Rechnung, es fehlen einige Metadaten",
                    _ => "Die hochgeladene Datei kann nicht validiert werden.",
                };

                // Determine if any errors should be sent in the response
                List<string> oErrors = ex.ErrorType switch
                {
                    // Errors contains the list of missing fields
                    ErrorType.MissingProperties => oResult.MissingProperties,
                    _ => new List<string>()
                };

                _logger.Debug($"Replying with: [{oMessage}]");

                return new ValidationResponse()
                {
                    status = RESPONSE_ERROR,
                    message = oMessage,
                    errors = oErrors
                };
            }
            catch (Exception ex)
            {
                _logger.Error(ex);

                string oMessage = "Die hochgeladene Datei kann nicht validiert werden, weil ein unbekannter Fehler aufgetreten ist.";

                _logger.Debug($"Replying with: [{oMessage}]");

                return new ValidationResponse()
                {
                    status = RESPONSE_ERROR,
                    message = oMessage
                };
            }           
        }
    }
}
