using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Configuration;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using System.Web.Routing;
using minimalist_oauth_rp.Clases;
using System.IO;
using System.Runtime.Serialization.Json;
using minimalist_oauth_rp.Models;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Drawing;

namespace minimalist_oauth_rp.Controllers
{
    public class SignatureController : Controller
    {
        Uri hostname;
        //
        // GET: /Signature/
        protected override void Initialize(RequestContext requestContext)
        {
            hostname = requestContext.HttpContext.Request.Url;
            base.Initialize(requestContext);
        }


        public ActionResult DocumentSignatureInit()
        {

            String accessToken = string.Empty;
            try
            {
                accessToken = getAccessToken();
            }
            catch (WebException we)
            {
                return RedirectToAction("Http400", "Errors", new RouteValueDictionary(new { error = we.Message, error_description = we.InnerException }));
            }

            // Base URL of TrustedX Resources.
            String trustedxResourcesBaseUri = WebConfigurationManager.AppSettings["trustedxResourcesBaseUri"];
            String signUrl = trustedxResourcesBaseUri + "/esignsp/v2/signer_processes";
            // Se sube el archivo a firmar
            Stream stream = this.Upload(signUrl, accessToken);
            SignatureResponse signatureResponse = null;
            if (stream != null)
            {
                signatureResponse = tratarRespuesta(stream);
            }
            else
            {
                return RedirectToAction("Http400", "Errors");
            }
            String urlSignatureProcess = signatureResponse.self;
            String urlUserRedirectAuthorization = signatureResponse.tasks.pending[0].url;
            String urlSignedDocument = signatureResponse.documents[0].url + "/content";
            Session["applicationAccessToken"] = accessToken;
            Session["urlSignatureProcess"] = urlSignatureProcess;
            Session["urlSignedDocument"] = urlSignedDocument;
            return Redirect(urlUserRedirectAuthorization);
        }

        public ActionResult DocumentSignatureEnd()
        {
            String applicationAccessToken = (string)(Session["applicationAccessToken"]);
            String urlSignedDocument = (string)(Session["urlSignedDocument"]);
            String urlSignatureProcess = (string)(Session["urlSignatureProcess"]);
            String status = Request.QueryString.Get("status");
            WebClient client = new WebClient();
            client.Headers.Set("Authorization", "Bearer " + applicationAccessToken);
            string pdfFileName = WebConfigurationManager.AppSettings["pdfFileName"];
            var pathPDFFirmado = Server.MapPath("~/Files/signed_" + pdfFileName);
            try
            {
                client.DownloadFile(urlSignedDocument, pathPDFFirmado);
            }
            catch (WebException we)
            {
                return RedirectToAction("Http400", "Errors", new RouteValueDictionary(new { error = we.Message, error_description = we.InnerException }));
            }
            // Once the document has been generated you have to sent a DELETE request to eliminate
            // the document on the server.
            //WebRequest request = WebRequest.Create(urlSignatureProcess);
            //request.Headers.Set("Authorization", "Bearer " + applicationAccessToken);
            //request.Method = "DELETE";
            //HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            //// You should get an HTTP 204 response from TrustedX. Other response codes indicate that an
            //// error has occurred. For example, HTTP 401 (unauthorized) means that the request could not
            //// be authenticated by TrustedX. HTTP 400 (bad request) or HTTP 500 (internal server error)
            //// can also be returned. In these cases, processing stops, and the error must be handled in
            //// some way.
            //if (response.StatusCode != HttpStatusCode.NoContent)
            //{
            //    return RedirectToAction("Http400", "Errors");
            //}
            ViewData["pdfFileName"] = pathPDFFirmado;
            return View("Signature");
        }

        private Stream Upload(string url, string accessToken)
        {
            string pdfFileName = WebConfigurationManager.AppSettings["pdfFileName"];

            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
                using (var multipartFormDataContent = new MultipartFormDataContent())
                {
                    var boundaryValue = multipartFormDataContent.Headers.ContentType.Parameters.FirstOrDefault(p => p.Name == "boundary");
                    boundaryValue.Value = boundaryValue.Value.Replace("\"", String.Empty);
                    var pathPDF = Server.MapPath("~/Files/" + pdfFileName);
                    SignatureRequest signatureRequest = this.fillSignatureRequest();


                    MemoryStream memoryStream = new MemoryStream();
                    DataContractJsonSerializer signatureJsonSerializer = new DataContractJsonSerializer(typeof(SignatureRequest));
                    signatureJsonSerializer.WriteObject(memoryStream, signatureRequest);
                    memoryStream.Position = 0;
                    StreamReader sr = new StreamReader(memoryStream);
                    StringContent stringContent = new StringContent(sr.ReadToEnd().Replace("\\",""), Encoding.UTF8, "application/json");
                    multipartFormDataContent.Add(stringContent, "\"process\"");

                    FileStream fs = System.IO.File.OpenRead(pathPDF);
                    StreamContent streamContent = new StreamContent(fs);
                    streamContent.Headers.Add("Content-Disposition", "form-data; name=\"document\"; filename=\"" + pdfFileName + "\"");
                    streamContent.Headers.Add("Content-Type", "application/pdf");
                    streamContent.Headers.Add("Content-Transfer-Encoding", "binary");
                    multipartFormDataContent.Add(streamContent, "file", Path.GetFileName(pathPDF));

                    var response = httpClient.PostAsync(url, multipartFormDataContent).Result;

                    if (!response.IsSuccessStatusCode)
                    {
                        return null;
                    }
                    return response.Content.ReadAsStreamAsync().Result;
                }
            }
        }
        
        private SignatureRequest fillSignatureRequest()
        {
            string appPdfRedirectUri = hostname.Scheme + "://" + hostname.Authority + WebConfigurationManager.AppSettings["appPdfRedirectUri"];

            string labels = WebConfigurationManager.AppSettings["labels"];
            string[] labelsArray = labels.Split(',');
            List<string> labelsList = new List<string>(labelsArray);
            List<List<string>> labelsListList = new List<List<string>>();
            labelsListList.Add(labelsList);

            SignatureRequest signatureRequest = new SignatureRequest();
            signatureRequest.process_type = "urn:safelayer:eidas:processes:document:sign";
            signatureRequest.signer = new Signer();
            signatureRequest.signer.signature_policy_id = "urn:safelayer:eidas:policies:sign:document:pdf";
            signatureRequest.signer.parameters = new Parameters();
            signatureRequest.signer.parameters.type = "pades-bes";
            signatureRequest.signer.parameters.default_digest_algorithm = "sha256";
            signatureRequest.signer.parameters.location = "Vitoria, Spain";
            signatureRequest.signer.parameters.signature_field = new SignatureField();
            signatureRequest.signer.parameters.signature_field.name = "user_signature";
            signatureRequest.signer.parameters.signature_field.location = new Location();
            signatureRequest.signer.parameters.signature_field.location.page = new Page();
            signatureRequest.signer.parameters.signature_field.location.page.number = "last";
            signatureRequest.signer.parameters.signature_field.location.rectangle = new Models.Rectangle();
            signatureRequest.signer.parameters.signature_field.location.rectangle.x = 160;
            signatureRequest.signer.parameters.signature_field.location.rectangle.y = 290;
            signatureRequest.signer.parameters.signature_field.location.rectangle.height = 30;
            signatureRequest.signer.parameters.signature_field.location.rectangle.width = 90;
            signatureRequest.signer.parameters.signature_field.appearance = new Appearance();
            signatureRequest.signer.parameters.signature_field.appearance.signature_details = new SignatureDetails();
            signatureRequest.signer.parameters.signature_field.appearance.signature_details.details = new List<Detail>();
            Detail detalleText = new Detail();
            detalleText.type = "text";
            detalleText.value = "Detalle de la firma";
            signatureRequest.signer.parameters.signature_field.appearance.signature_details.details.Add(detalleText);
            Detail detalleSubject = new Detail();
            detalleSubject.type = "subject";
            detalleSubject.title = "Firmado por: ";
            signatureRequest.signer.parameters.signature_field.appearance.signature_details.details.Add(detalleSubject);
            Detail detalleLocation = new Detail();
            detalleLocation.type = "location";
            detalleLocation.title = "Firmado en: ";
            signatureRequest.signer.parameters.signature_field.appearance.signature_details.details.Add(detalleLocation);
            Detail detalleDate = new Detail();
            detalleDate.type = "date";
            detalleDate.title = "Fecha: ";
            detalleDate.timezone = "Europe/Madrid";
            signatureRequest.signer.parameters.signature_field.appearance.signature_details.details.Add(detalleDate);
            signatureRequest.signer.parameters.signature_field.appearance.background_image = new BackgroundImage();
            signatureRequest.signer.parameters.signature_field.appearance.background_image.binary = " ";
            signatureRequest.ui_locales = new List<string>();
            signatureRequest.ui_locales.Add("es_ES");
            signatureRequest.finish_callback_url = appPdfRedirectUri;
            //signatureRequest.labels = labelsListList;

            return signatureRequest;
        }

        private SignatureResponse tratarRespuesta(Stream respuesta)
        {
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(SignatureResponse));
            SignatureResponse signatureResponse = (SignatureResponse)ser.ReadObject(respuesta);
            return signatureResponse;
        }
        
        private string getAccessToken()
        {
            // Get the necessary scopes to sign documents. 
            String clientScopes = WebConfigurationManager.AppSettings["appSignScopes"];
            // Base URL of TrustedX Signature API.
            String trustedxSignServerBaseUri = WebConfigurationManager.AppSettings["trustedxSignServerBaseUri"];

            CustomWebClient clientToken = new CustomWebClient();
            // To authenticate the request, use your API_KEY as an HTTP header
            String apiKey = WebConfigurationManager.AppSettings["apiKey"];
            clientToken.Headers.Add("Authorization", "Basic " + apiKey);

            // The other parameters must be sent in the request body.
            NameValueCollection values = new NameValueCollection();
            values["grant_type"] = "client_credentials";
            values["scope"] = clientScopes;
            byte[] responsePost;
            String responsePostString;
           
            responsePost = clientToken.UploadValues(trustedxSignServerBaseUri + "/token", values);
            clientToken.StatusCode();
            responsePostString = Encoding.Default.GetString(responsePost);

            MemoryStream msPost = new MemoryStream(Encoding.Unicode.GetBytes(responsePostString));
            DataContractJsonSerializer serOuathAccessToken = new DataContractJsonSerializer(typeof(OauthAccessToken));
            OauthAccessToken oauthAccessToken = (OauthAccessToken)serOuathAccessToken.ReadObject(msPost);
            return oauthAccessToken.access_token;
        }


        public ActionResult BatchDocumentSignatureInit()
        {
            String accessToken = string.Empty;
            try
            { 
                accessToken = getAccessToken();
            }
            catch (WebException we)
            {
                return RedirectToAction("Http400", "Errors", new RouteValueDictionary(new { error = we.Message, error_description = we.InnerException}));
            }

            // Se suben los archivos a firmar
            List<UploadFile> filesToSign = new List<UploadFile>();
            string pdfFileName = WebConfigurationManager.AppSettings["pdfFileName"];
            string pdfFileName2 = WebConfigurationManager.AppSettings["pdfFileName2"];
           
            filesToSign.Add(new UploadFile(System.IO.Path.GetFileName(pdfFileName), System.IO.File.ReadAllBytes(Server.MapPath("~/Files/" + pdfFileName))));
            filesToSign.Add(new UploadFile(System.IO.Path.GetFileName(pdfFileName2), System.IO.File.ReadAllBytes(Server.MapPath("~/Files/" + pdfFileName2))));

            List<KeyValuePair<string, string>> idNameList;

            String trustedxResourcesBaseUri = WebConfigurationManager.AppSettings["trustedxResourcesBaseUri"];
            var signManager = new SignManager(hostname, trustedxResourcesBaseUri);

            try
            {
                 idNameList = signManager.UploadFilesToSign(filesToSign, "application/pdf", accessToken);
            }
            catch (Exception)
            {

                return RedirectToAction("Http400", "Errors");
            }
                    
            string appDataFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data");
            string jsonFilePath = Path.Combine(appDataFolderPath, "pdfSignature.json");
            string signatureJson = System.IO.File.ReadAllText(jsonFilePath);
            signatureJson = signManager.ActualizarDocumentos(idNameList.Select(kv => kv.Key).ToList(), signatureJson);
            signatureJson = signManager.ActualizarRedirectUri(signatureJson);
            string jsonFirma = signManager.IniciarProcesoFirma(signatureJson, accessToken);
            InicioProcesoFirmaResponse inicioProcesoFirmaResponse = signManager.ObtenerUrlsFirma(jsonFirma);
            Session["applicationAccessToken"] = accessToken;
            Session["idNameList"] = idNameList;
            Session["inicioProcesoFirmaResponse"] = inicioProcesoFirmaResponse;

            return Redirect(inicioProcesoFirmaResponse.UrlUserRedirectAuthorization);
        }

        public ActionResult DocumentsSignatureEnd()
        {
            String status = Request.QueryString.Get("status");
            WebClient client = new WebClient();
            client.Headers.Set("Authorization", "Bearer " + (string)(Session["applicationAccessToken"]));
            List<KeyValuePair<string, string>> idNameList = Session["idNameList"] as List<KeyValuePair<string, string>>;
            InicioProcesoFirmaResponse inicioProcesoFirmaResponse = Session["inicioProcesoFirmaResponse"] as InicioProcesoFirmaResponse;
            
            foreach (string content in inicioProcesoFirmaResponse.DocumentsContent)
            {
                try
                {
                    var pdfFileName = idNameList.Where(x => content.Contains(x.Key)).Select(x => x.Value).First();
                    var pathPDFFirmado = Server.MapPath("~/Files/signed_" + pdfFileName);
                    client.DownloadFile(content, pathPDFFirmado);
                    ViewData["pdfFileName"] += "<br>" + pathPDFFirmado ;
                }
                catch (WebException we)
                {
                    return RedirectToAction("Http400", "Errors", new RouteValueDictionary(new { error = we.Message, error_description = we.InnerException }));
                }
            } 
            return View("Signature");
        }

    }
}
