using MesaFabApproval.API.Services;
using MesaFabApproval.API.Utilities;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;

namespace MesaFabApproval.API.Controllers;

[ApiController]
[Authorize]
public class PCRBController : ControllerBase {
    private readonly ILogger<MRBController> _logger;
    private readonly IPCRBService _pcrbService;
    private readonly IMonInUtils _monInUtils;

    public PCRBController(ILogger<MRBController> logger, IPCRBService pcrbService, IMonInUtils monInUtils) {
        _logger = logger ?? throw new ArgumentNullException("ILogger not injected");
        _pcrbService = pcrbService ?? throw new ArgumentNullException("IPCRBService not injected");
        _monInUtils = monInUtils ?? throw new ArgumentNullException("IMonInUtils not injected");
    }

    [HttpPost]
    [Route("pcrb")]
    public async Task<IActionResult> CreateNewPCRB(PCRB pcrb) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to generate a new PCRB");

            if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");

            await _pcrbService.CreateNewPCRB(pcrb);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot create new PCRB, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreateNewPCRB";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            
            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/all")]
    public async Task<IActionResult> GetAllPCRBs(bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to get all PCRBs");

            IEnumerable<PCRB> allPCRBs = await _pcrbService.GetAllPCRBs(bypassCache);

            return Ok(allPCRBs);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get all PCRBs, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetAllPCRBs";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            
            _monInUtils.PostMetrics(metricName, millisecondsDiff, false, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/getByTitle")]
    public async Task<IActionResult> GetPCRBByTitle(string title, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to get an PCRB by Title");

            if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");

            PCRB pcrb = await _pcrbService.GetPCRBByTitle(title, bypassCache);

            return Ok(pcrb);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get PCRB by title, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBbyTitle";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/getByPlanNumber")]
    public async Task<IActionResult> GetPCRBByPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to get an PCRB by Title");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");

            PCRB pcrb = await _pcrbService.GetPCRBByPlanNumber(planNumber, bypassCache);

            return Ok(pcrb);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get PCRB by plan number, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBbyPlanNumber";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb")]
    public async Task<IActionResult> UpdatePCRB(PCRB pcrb) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update a PCRB");

            if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");

            await _pcrbService.UpdatePCRB(pcrb);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot update PCRB, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCRB";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpDelete]
    [Route("pcrb")]
    public async Task<IActionResult> DeletePCRB(int planNumber) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to delete PCRB# {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");

            await _pcrbService.DeletePCRB(planNumber);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot delete PCRB# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "DeletePCRB";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/attachment")]
    [ApiExplorerSettings(IgnoreApi = true)]
    public async Task<IActionResult> UploadAttachment([FromForm] IFormFile file, [FromForm] PCRBAttachment attachment) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to upload PCRB attachment");

            if (file is null) throw new ArgumentNullException("File cannot be null");
            if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
            if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");

            await _pcrbService.UploadAttachment(file, attachment);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot upload PCRB attachment, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UploadPCRBAttachment";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/attachments")]
    public async Task<IActionResult> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get MRB attachments for MRB {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");

            List<PCRBAttachment> attachments = (await _pcrbService.GetAttachmentsByPlanNumber(planNumber, bypassCache)).ToList();

            return Ok(attachments);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get attachments for PCRB Plan# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBAttachments";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [AllowAnonymous]
    [HttpGet]
    [Route("pcrb/attachmentFile")]
    public async Task<IActionResult> GetMRBAttachmentFile(string path, string fileName) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to get PCRB attachment file");

            if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
            if (!System.IO.File.Exists(path)) throw new ArgumentException("No file exists at provided path");
            if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("Filename cannot be null or empty");

            byte[] fs = System.IO.File.ReadAllBytes(path);

            const string defaultContentType = "application/octet-stream";

            FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();

            if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
                contentType = defaultContentType;
            }

            return new FileContentResult(fs, contentType) {
                FileDownloadName = fileName
            };
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get PCRB attachment file, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBAttachmentFile";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            
            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb/attachment")]
    public async Task<IActionResult> UpdateAttachment(PCRBAttachment attachment) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update an attachment");

            if (attachment is null) throw new ArgumentNullException("attachment cannot be null");

            await _pcrbService.UpdateAttachment(attachment);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot update attachment, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCRBAttachment";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpDelete]
    [Route("pcrb/attachment")]
    public async Task<IActionResult> DeleteAttachment(PCRBAttachment attachment) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to delete an attachment");

            if (attachment is null) throw new ArgumentNullException("attachment cannot be null");

            await _pcrbService.DeleteAttachment(attachment);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot delete attachment, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "DeletePCRBAttachment";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/actionItem")]
    public async Task<IActionResult> CreateActionItem(PCRBActionItem actionItem) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to create PCRB action item");

            if (actionItem is null) throw new ArgumentNullException("action item cannot be null");

            await _pcrbService.CreateNewActionItem(actionItem);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot create PCRB action item, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreatePCRBActionItem";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb/actionItem")]
    public async Task<IActionResult> UpdateActionItem(PCRBActionItem actionItem) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update an action item");

            if (actionItem is null) throw new ArgumentNullException("action item cannot be null");

            await _pcrbService.UpdateActionItem(actionItem);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot update action item, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCRBActionItem";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpDelete]
    [Route("pcrb/actionItem")]
    public async Task<IActionResult> DeleteActionItem(int id) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to delete an action item");

            if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID");

            await _pcrbService.DeleteActionItem(id);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot delete action item, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "DeletePCRBActionItem";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/actionItems")]
    public async Task<IActionResult> GetActionItemsByPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get PCRB action items for plan# {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");

            List<PCRBActionItem> actionItems = (await _pcrbService.GetActionItemsForPlanNumber(planNumber, bypassCache)).ToList();

            return Ok(actionItems);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get action items for plan# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBActionItems";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/pcr3Document")]
    public async Task<IActionResult> CreatePCR3Document(PCR3Document document) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to create PCR3 document");

            if (document is null) throw new ArgumentNullException("document cannot be null");

            await _pcrbService.CreatePCR3Document(document);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot create PCR3 document, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreatePCR3Document";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb/pcr3Document")]
    public async Task<IActionResult> UpdatePCR3Document(PCR3Document document) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update a PCR3 document");

            if (document is null) throw new ArgumentNullException("document cannot be null");

            await _pcrbService.UpdatePCR3Document(document);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot update PCR3 document, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCR3Document";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/pcr3Documents")]
    public async Task<IActionResult> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get PCR3 documents for plan# {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");

            List<PCR3Document> documents = (await _pcrbService.GetPCR3DocumentsForPlanNumber(planNumber, bypassCache)).ToList();

            return Ok(documents);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get PCR3 documents for plan# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCR3Documents";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/attendee")]
    public async Task<IActionResult> CreateAttendee(PCRBAttendee attendee) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to create new attendee");

            if (attendee is null) throw new ArgumentNullException("attendee item cannot be null");

            await _pcrbService.CreateNewAttendee(attendee);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot create new attendee, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreatePCRBAttendee";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            
            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb/attendee")]
    public async Task<IActionResult> UpdateAttendee(PCRBAttendee attendee) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update an attendee");

            if (attendee is null) throw new ArgumentNullException("attendee cannot be null");

            await _pcrbService.UpdateAttendee(attendee);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot update attendee, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCRBAttendee";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpDelete]
    [Route("pcrb/attendee")]
    public async Task<IActionResult> DeleteAttendee(int id) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to delete an attendee");

            if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB attendee ID");

            await _pcrbService.DeleteAttendee(id);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot delete attendee, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "DeletePCRBAttendee";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/attendees")]
    public async Task<IActionResult> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get attendees for plan# {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");

            List<PCRBAttendee> attendees = (await _pcrbService.GetAttendeesByPlanNumber(planNumber, bypassCache)).ToList();

            return Ok(attendees);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get attendees for plan# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBAttendees";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/notify/new-approvals")]
    public async Task<IActionResult> NotifyNewApprovals(PCRB pcrb) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to notify new approvers");

            if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");

            await _pcrbService.NotifyNewApprovals(pcrb);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to notify new approvers, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "NotifyNewPCRBApprovers";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/notify/approvers")]
    public async Task<IActionResult> NotifyApprovers(PCRBNotification notification) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to notify approvers");

            if (notification is null) throw new ArgumentNullException("notification cannot be null");
            if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
            if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");

            await _pcrbService.NotifyApprovers(notification);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to notify approvers, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "NotifyPCRBApprovers";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/notify/originator")]
    public async Task<IActionResult> NotifyOriginator(PCRBNotification notification) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to notify originator");

            if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
            if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
            if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");

            await _pcrbService.NotifyOriginator(notification);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to notify originator, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "NotifyPCRBOriginator";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/notify/responsiblePerson")]
    public async Task<IActionResult> NotifyResponsiblePerson(PCRBActionItemNotification notification) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to notify originator");

            if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
            if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
            if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");

            await _pcrbService.NotifyResponsiblePerson(notification);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to notify responsible person, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "NotifyPCRBResponsiblePerson";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPost]
    [Route("pcrb/followUp")]
    public async Task<IActionResult> CreateFollowUp(PCRBFollowUp followUp) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to create follow up");

            if (followUp is null) throw new ArgumentNullException("follow up cannot be null");

            await _pcrbService.CreateFollowUp(followUp);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to create follow up, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreatePCRBFollowUp";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpGet]
    [Route("pcrb/followUps")]
    public async Task<IActionResult> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get attendees for plan# {planNumber}");

            if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");

            List<PCRBFollowUp> attendees = (await _pcrbService.GetFollowUpsByPlanNumber(planNumber, bypassCache)).ToList();

            return Ok(attendees);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get follow ups for plan# {planNumber}, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetPCRBFollowUps";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpPut]
    [Route("pcrb/followUp")]
    public async Task<IActionResult> UpdateFollowUp(PCRBFollowUp followUp) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to update follow up");

            if (followUp is null) throw new ArgumentNullException("follow up cannot be null");

            await _pcrbService.UpdateFollowUp(followUp);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to update follow up, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "UpdatePCRBFollowUp";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }

    [HttpDelete]
    [Route("pcrb/followUp")]
    public async Task<IActionResult> DeleteFollowUp(int id) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation("Attempting to delete follow up");

            if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB follow up ID");

            await _pcrbService.DeleteFollowUp(id);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Unable to delete follow up, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "DeletePCRBFollowUp";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;

            _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError);
        }
    }
}