public async Task <SimsAttachmentLibraryInfo> EnsureLibrary(string incidentId)
        {
            var accessToken = await fetchAccessToken();

            using (var ctx = SpContextHelper.GetClientContextWithAccessToken(this.siteUrl, accessToken))
            {
                var list = await this.EnsureList(incidentId, ctx);

                ctx.Load(list.RootFolder);
                ctx.Load(ctx.Web);
                await ctx.ExecuteQueryAsync();

                // need the full site collection url
                //https blah/sites/blah
                var webUrl = ctx.Web.Url;
                // /sites/blah/folder
                var rootFolderUrl = list.RootFolder.ServerRelativeUrl;
                //var startOverlap webUrl.Contains()
                return(new SimsAttachmentLibraryInfo
                {
                    WebUrl = webUrl,
                    ServerRelativeFolderUrl = rootFolderUrl
                });
            }
        }
        public SPAttachments(string clientId, string tenantId, X509Certificate2 cert, string hostUrl, string siteUrl, string contentTypeId)
        {
            //this.clientId = clientId;
            this.cert          = cert;
            this.siteUrl       = siteUrl;
            this.contentTypeId = contentTypeId;

            //this.tenantId = tenantId;
            this.scopes           = new string[] { $"https://{hostUrl}/.default" }; // "https://{hostUrl}.com/TermStore.ReadWrite.All" };
            this.fetchAccessToken = () => SpContextHelper.GetApplicationAuthenticatedClient(clientId, this.cert, this.scopes, tenantId);
        }
        public async Task <(string filename, string url)> AddAttachment(string filePath, string fileName, string hostIdentifier)
        {
            var accessToken = await fetchAccessToken();

            using (var ctx = SpContextHelper.GetClientContextWithAccessToken(this.siteUrl, accessToken))
            {
                try
                {
                    // Ensure we have a library related to the incident.(It is created if not)
                    var uploadLib = await EnsureList(hostIdentifier, ctx);

                    var fileSize = new FileInfo(filePath).Length;
                    var fileMb   = (fileSize / 1024) / 1024;

                    Microsoft.SharePoint.Client.File uploadedFile = null;

                    if (fileMb < 10)
                    {
                        uploadedFile = await UploadMidSizeFile(ctx, uploadLib, filePath, Path.GetFileName(fileName));
                    }
                    else
                    {
                        uploadedFile = await UploadLargeSizeFile(ctx, uploadLib, filePath, Path.GetFileName(fileName), fileSize);
                    }
                    // set the content type correctly.
                    if (uploadedFile != null)
                    {
                        var fileItem = uploadLib.GetItemByUniqueId(uploadedFile.UniqueId);
                        ctx.Load(fileItem);
                        var fileFields = ctx.Web.GetFileByServerRelativeUrl(uploadedFile.ServerRelativeUrl);
                        ctx.Load(fileFields, f => f.ListItemAllFields["EncodedAbsUrl"]);
                        fileItem["ContentTypeId"]  = this.contentTypeId;
                        fileItem["SIMSIncidentId"] = hostIdentifier; // Custom Column
                        fileItem.Update();
                        await ctx.ExecuteQueryAsync();

                        return(uploadedFile.Name, String.IsNullOrEmpty(uploadedFile.LinkingUrl) ? Uri.EscapeUriString(fileFields.ListItemAllFields["EncodedAbsUrl"] as string) : Uri.EscapeUriString(uploadedFile.LinkingUrl));
                    }

                    throw new ArgumentNullException("No file has been uploaded");
                }
                //may be manage the duplicates here
                catch (ServerException ex) when(ex.ServerErrorCode == -2130575257)
                {
                    throw ex;
                }
                catch (ServerException ex)
                {
                    throw new ArgumentException($"Microsoft 365 error : {ex.ServerErrorValue}");;
                }
            }
        }
        public async Task <IEnumerable <SimsAttachmentFileInfo> > FetchAllAttchmentsLinks(string listName)
        {
            var accessToken = await fetchAccessToken();

            using (var ctx = SpContextHelper.GetClientContextWithAccessToken(this.siteUrl, accessToken))
            {
                var theLib = await EnsureList(listName, ctx);

                var files = theLib.RootFolder.Files;
                ctx.Load(files, o => o.Include(p => p.ListItemAllFields["EncodedAbsUrl"], p => p.Name));
                await ctx.ExecuteQueryAsync();

                return(files.Select(p => new SimsAttachmentFileInfo {
                    FileName = p.Name, Url = p.ListItemAllFields["EncodedAbsUrl"] as string
                }).ToList());
            }
        }
        public async Task <(string fileName, string url)> RenameAttachment(string fileName, string url)
        {
            var accessToken = await fetchAccessToken();

            using (var ctx = SpContextHelper.GetClientContextWithAccessToken(this.siteUrl, accessToken))
            {
                // Load the file, but then we need to check to make sure the new file name does not already exist.
                var file = ctx.Web.GetFileByUrl(url);
                ctx.Load(file, f => f.ListItemAllFields["FileDirRef"], f => f.ListItemAllFields["EncodedAbsUrl"]);
                await ctx.ExecuteQueryAsync();

                if (file != null)
                {
                    // Check to see if there is already a file with the new file file.
                    var newFilename = file.ListItemAllFields["FileDirRef"] + "/" + fileName;
                    try
                    {
                        var existingFile = ctx.Web.GetFileByServerRelativeUrl(newFilename);
                        ctx.Load(existingFile);
                        await ctx.ExecuteQueryAsync();

                        if (existingFile.ServerObjectIsNull.HasValue ? !existingFile.ServerObjectIsNull.Value : false)
                        {
                            throw new ArgumentOutOfRangeException("File already exists");
                        }
                        ;
                    }
                    catch (ServerException e) when(e.Message.Contains("File Not Found."))
                    {
                        file.MoveTo(newFilename, MoveOperations.RetainEditorAndModifiedOnMove);
                        ctx.Load(file, f => f.ListItemAllFields["EncodedAbsUrl"]);
                        await ctx.ExecuteQueryAsync();

                        return(fileName, file.ListItemAllFields["EncodedAbsUrl"] as string);
                    }
                }
            }
            return("", "");
        }
        public async Task <IEnumerable <SimsSignalIncidentMigratedFile> > MigrateToLibrary(string incidentId, string signalId)
        {
            var accessToken = await fetchAccessToken();

            using (var ctx = SpContextHelper.GetClientContextWithAccessToken(this.siteUrl, accessToken))
            {
                var hostWeb = ctx.Web;
                var rootWeb = ctx.Site.RootWeb;
                var site    = ctx.Site;
                ctx.Load(hostWeb);
                ctx.Load(rootWeb);
                ctx.Load(site);
                // ensure we actually have a signals library
                List signalLib = null;
                try
                {
                    signalLib = ctx.Web.Lists.GetByTitle(signalId);
                    ctx.Load(signalLib, a => a.RootFolder, a => a.RootFolder.Files);
                    await ctx.ExecuteQueryAsync();
                }
                catch (ServerException ex)
                {
                    return(Enumerable.Empty <SimsSignalIncidentMigratedFile>());
                }

                // If we actually have no files, then we can return
                if (signalLib.RootFolder.Files.Count == 0)
                {
                    return(Enumerable.Empty <SimsSignalIncidentMigratedFile>());
                }

                // othewise continue with the incident
                var incidentLib = ctx.Web.Lists.GetByTitle(incidentId);
                ctx.Load(incidentLib, a => a.RootFolder);
                await ctx.ExecuteQueryAsync();

                // Get all the signal files and their names
                var signalDictionary = new Dictionary <string, string>();
                foreach (var file in signalLib.RootFolder.Files)
                {
                    signalDictionary.Add(file.Name, file.ServerRelativeUrl);
                }

                // Moves the files over to incidents
                foreach (var file in signalLib.RootFolder.Files)
                {
                    file.CopyTo(Path.Combine(incidentLib.RootFolder.ServerRelativeUrl, file.Name), true);
                }

                await ctx.ExecuteQueryAsync();

                // Once all the files have been moved, we need to update all the files to have the incident id
                ctx.Load(incidentLib, a => a.RootFolder, a => a.RootFolder.Files, a => a.RootFolder.Files.Include(o => o.ListItemAllFields));
                await ctx.ExecuteQueryAsync();

                var incidentDictionary = new Dictionary <string, string>();
                foreach (var file in incidentLib.RootFolder.Files)
                {
                    ctx.Load(file, a => a.ListItemAllFields);
                    var fileItem = file.ListItemAllFields;
                    fileItem["ContentTypeId"]  = this.contentTypeId;
                    fileItem["SIMSIncidentId"] = incidentId;
                    fileItem.Update();

                    // Create a complete  uri
                    Uri outUri = null;
                    if (Uri.TryCreate(new Uri(site.Url), file.ServerRelativeUrl, out outUri))
                    {
                        incidentDictionary.Add(file.Name, outUri.ToString());
                    }
                }

                await ctx.ExecuteQueryAsync();

                // Values are UrlPath encoded so we don't have to futz about converting values
                // At Rest(in db) all paths MUST be URL encoded.
                return(incidentDictionary.Select(a => new SimsSignalIncidentMigratedFile
                {
                    SignalUrl = signalDictionary[a.Key],
                    IncidentUrl = HttpUtility.UrlPathEncode(a.Value),
                    FileName = HttpUtility.UrlPathEncode(a.Key)
                }).ToList());
            }
        }