using System.Net;
using Bit.Api.Billing.Public.Models;
using Bit.Api.Models.Public.Response;
using Bit.Core.Billing.Pricing;
using Bit.Core.Context;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OrganizationSubscriptionUpdateRequestModel = Bit.Api.Billing.Public.Models.OrganizationSubscriptionUpdateRequestModel;

namespace Bit.Api.Billing.Public.Controllers;

[Route("public/organization")]
[Authorize("Organization")]
public class OrganizationController : Controller
{
    private readonly IOrganizationService _organizationService;
    private readonly ICurrentContext _currentContext;
    private readonly IOrganizationRepository _organizationRepository;
    private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
    private readonly ILogger<OrganizationController> _logger;
    private readonly IPricingClient _pricingClient;

    public OrganizationController(
        IOrganizationService organizationService,
        ICurrentContext currentContext,
        IOrganizationRepository organizationRepository,
        IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
        ILogger<OrganizationController> logger,
        IPricingClient pricingClient)
    {
        _organizationService = organizationService;
        _currentContext = currentContext;
        _organizationRepository = organizationRepository;
        _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
        _logger = logger;
        _pricingClient = pricingClient;
    }

    /// <summary>
    /// Retrieves the subscription details for the current organization.
    /// </summary>
    /// <returns>
    /// Returns an object containing the subscription details if successful.
    /// </returns>
    [HttpGet("subscription")]
    [SelfHosted(NotSelfHostedOnly = true)]
    [ProducesResponseType(typeof(OrganizationSubscriptionDetailsResponseModel), (int)HttpStatusCode.OK)]
    [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.NotFound)]
    public async Task<IActionResult> GetSubscriptionAsync()
    {
        try
        {
            var organizationId = _currentContext.OrganizationId.Value;
            var organization = await _organizationRepository.GetByIdAsync(organizationId);

            var subscriptionDetails = new OrganizationSubscriptionDetailsResponseModel
            {
                PasswordManager = new PasswordManagerSubscriptionDetails
                {
                    Seats = organization.Seats,
                    MaxAutoScaleSeats = organization.MaxAutoscaleSeats,
                    Storage = organization.MaxStorageGb
                },
                SecretsManager = new SecretsManagerSubscriptionDetails
                {
                    Seats = organization.SmSeats,
                    MaxAutoScaleSeats = organization.MaxAutoscaleSmSeats,
                    ServiceAccounts = organization.SmServiceAccounts,
                    MaxAutoScaleServiceAccounts = organization.MaxAutoscaleSmServiceAccounts
                }
            };

            return Ok(subscriptionDetails);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled error while retrieving the subscription details");
            return StatusCode(500, new { Message = "An error occurred while retrieving the subscription details." });
        }
    }

    /// <summary>
    /// Update the organization's current subscription for Password Manager and/or Secrets Manager.
    /// </summary>
    /// <param name="model">The request model containing the updated subscription information.</param>
    [HttpPut("subscription")]
    [SelfHosted(NotSelfHostedOnly = true)]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.NotFound)]
    public async Task<IActionResult> PostSubscriptionAsync([FromBody] OrganizationSubscriptionUpdateRequestModel model)
    {
        try
        {
            await UpdatePasswordManagerAsync(model, _currentContext.OrganizationId.Value);

            var secretsManagerResult = await UpdateSecretsManagerAsync(model, _currentContext.OrganizationId.Value);

            if (!string.IsNullOrEmpty(secretsManagerResult))
            {
                return Ok(new { Message = secretsManagerResult });
            }

            return Ok(new { Message = "Subscription updated successfully." });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled error while updating the subscription");
            return StatusCode(500, new { Message = "An error occurred while updating the subscription." });
        }
    }

    private async Task UpdatePasswordManagerAsync(OrganizationSubscriptionUpdateRequestModel model, Guid organizationId)
    {
        if (model.PasswordManager != null)
        {
            var organization = await _organizationRepository.GetByIdAsync(organizationId);

            model.PasswordManager.ToPasswordManagerSubscriptionUpdate(organization);
            await _organizationService.UpdateSubscription(organization.Id, (int)model.PasswordManager.Seats,
                model.PasswordManager.MaxAutoScaleSeats);
            if (model.PasswordManager.Storage.HasValue)
            {
                await _organizationService.AdjustStorageAsync(organization.Id, (short)model.PasswordManager.Storage);
            }
        }
    }

    private async Task<string> UpdateSecretsManagerAsync(OrganizationSubscriptionUpdateRequestModel model, Guid organizationId)
    {
        if (model.SecretsManager == null)
        {
            return string.Empty;
        }

        var organization = await _organizationRepository.GetByIdAsync(organizationId);

        if (!organization.UseSecretsManager)
        {
            return "Organization has no access to Secrets Manager.";
        }

        var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
        var secretsManagerUpdate = model.SecretsManager.ToSecretsManagerSubscriptionUpdate(organization, plan);
        await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(secretsManagerUpdate);

        return string.Empty;
    }
}
