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

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

namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class ApprovalController : ControllerBase {
    private readonly ILogger<ApprovalController> _logger;
    private readonly IApprovalService _approvalService;
    private readonly IMonInWorkerClient _monInClient;

    public ApprovalController(ILogger<ApprovalController> logger, IApprovalService approvalService,
                              IMonInWorkerClient monInClient) {
        _logger = logger ?? throw new ArgumentNullException("ILogger not injected");
        _approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
        _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
    }

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

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

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

            await _approvalService.CreateApproval(approval);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot create new approval, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "CreateApproval";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpGet]
    [Route("approval/issue")]
    public async Task<IActionResult> GetApprovalsForIssueId(int issueId, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get approvals for issue {issueId}");

            if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID");

            IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(issueId, bypassCache);

            return Ok(approvals);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get approvals, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetApprovalsForIssueId";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpGet]
    [Route("approval/user")]
    public async Task<IActionResult> GetApprovalsForUserId(int userId, bool bypassCache) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get approvals for user ID {userId}");

            if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");

            IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForUserId(userId, bypassCache);

            return Ok(approvals);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get approvals, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetApprovalsForUserId";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpGet]
    [Route("approval/members")]
    public async Task<IActionResult> GetApprovalGroupMembers(int subRoleId) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get approval group members for group {subRoleId}");

            if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID");

            IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRoleId);

            return Ok(members);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get approval group members, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetApprovalsGroupMembers";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

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

        try {
            _logger.LogInformation($"Attempting to update approval");

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

            await _approvalService.UpdateApproval(approval);

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

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpPut]
    [Route("approval/approve")]
    public async Task<IActionResult> Approve(Approval approval) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"attempting to submit approval");

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

            await _approvalService.Approve(approval);

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

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpPut]
    [Route("approval/deny")]
    public async Task<IActionResult> Deny(Approval approval) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"attempting to deny approval");

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

            await _approvalService.Deny(approval);

            return Ok();
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Approval denial failed, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "Deny";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpGet]
    [Route("approval/roleId")]
    public async Task<IActionResult> GetRoleIdForRoleName(string roleName) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get role ID by role name");

            if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("role name cannot be null or empty");

            int roleId = await _approvalService.GetRoleIdForRoleName(roleName);

            return Ok(roleId);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get role ID, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetRoleIdForRoleName";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }

    [HttpGet]
    [Route("approval/subRoles")]
    public async Task<IActionResult> GetSubRolesForSubRoleName(string subRoleName, int roleId) {
        DateTime start = DateTime.Now;
        bool isArgumentError = false;
        bool isInternalError = false;
        string errorMessage = "";

        try {
            _logger.LogInformation($"Attempting to get sub roles by sub role name");

            if (string.IsNullOrWhiteSpace(subRoleName)) throw new ArgumentException("sub role name cannot be null or empty");
            if (roleId <= 0) throw new ArgumentException($"{roleId} is not a valid role ID");

            IEnumerable<SubRole> subRoles = await _approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);

            return Ok(subRoles);
        } catch (ArgumentException ex) {
            isArgumentError = true;
            errorMessage = ex.Message;
            return BadRequest(errorMessage);
        } catch (Exception ex) {
            isInternalError = true;
            errorMessage = $"Cannot get role ID, because {ex.Message}";
            return Problem(errorMessage);
        } finally {
            string metricName = "GetSubRoleIdForSubRoleName";
            DateTime end = DateTime.Now;
            double millisecondsDiff = (end - start).TotalMilliseconds;
            _monInClient.PostAverage(metricName + "Latency", millisecondsDiff);

            if (isArgumentError) {
                _logger.LogWarning(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            } else if (isInternalError) {
                _logger.LogError(errorMessage);
                _monInClient.PostStatus(metricName, StatusValue.Critical);
            } else {
                _monInClient.PostStatus(metricName, StatusValue.Ok);
            }
        }
    }
}