using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using Marco.Pms.CacheHelper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Helpers;
using Marco.Pms.Helpers.CacheHelper;
using Marco.Pms.Helpers.Utility;
using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Middleware;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Serilog;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

#region ======================= Service Configuration (Dependency Injection) =======================

#region Logging

// Add Serilog Configuration
string? mongoConn = builder.Configuration["MongoDB:SerilogDatabaseUrl"];
string timeString = "00:00:30";
TimeSpan.TryParse(timeString, out TimeSpan timeSpan);

builder.Host.UseSerilog((context, config) =>
{
    config.ReadFrom.Configuration(context.Configuration)
    .WriteTo.MongoDB(
            databaseUrl: mongoConn ?? string.Empty,
            collectionName: "api-logs",
            batchPostingLimit: 100,
            period: timeSpan
        );
});
#endregion

#region CORS (Cross-Origin Resource Sharing)
builder.Services.AddCors(options =>
{
    // A more permissive policy for development
    options.AddPolicy("DevCorsPolicy", policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader()
              .WithExposedHeaders("Authorization", "X-Request-ID", "X-Correlation-ID");
    });

    // A stricter policy for production (loaded from config)
    var corsSettings = builder.Configuration.GetSection("Cors");
    var allowedOrigins = corsSettings.GetValue<string>("AllowedOrigins")?.Split(',') ?? Array.Empty<string>();
    options.AddPolicy("ProdCorsPolicy", policy =>
    {
        policy.WithOrigins(allowedOrigins)
              .AllowAnyMethod()
              .AllowAnyHeader()
              .WithExposedHeaders("Authorization", "X-Request-ID", "X-Correlation-ID");
    });
});
#endregion

#region Core Web & Framework Services
builder.Services.AddControllers();
builder.Services.AddSignalR();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddHttpContextAccessor();
builder.Services.AddMemoryCache();
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddHostedService<StartupUserSeeder>();
#endregion

#region Database & Identity
string? connString = builder.Configuration.GetConnectionString("DefaultConnectionString")
    ?? throw new InvalidOperationException("Database connection string 'DefaultConnectionString' not found.");

// This single call correctly registers BOTH the DbContext (scoped) AND the IDbContextFactory (singleton).
builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
    options.UseMySql(connString, ServerVersion.AutoDetect(connString)));

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseMySql(connString, ServerVersion.AutoDetect(connString)));

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
#endregion

#region Authentication (JWT)
var jwtSettings = builder.Configuration.GetSection("Jwt").Get<JwtSettings>()
    ?? throw new InvalidOperationException("JwtSettings section is missing or invalid.");
if (jwtSettings != null && jwtSettings.Key != null)
{
    builder.Services.AddSingleton(jwtSettings);
    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtSettings.Issuer,
            ValidAudience = jwtSettings.Audience,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key))
        };
        // This event allows SignalR to get the token from the query string
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                var accessToken = context.Request.Query["access_token"];
                if (!string.IsNullOrEmpty(accessToken) && context.HttpContext.Request.Path.StartsWithSegments("/hubs/marco"))
                {
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    });
}
#endregion

#region API Documentation (Swagger)
builder.Services.AddSwaggerGen(option =>
{
    option.SwaggerDoc("v1", new OpenApiInfo { Title = "Marco PMS API", Version = "v1" });
    option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please enter a valid token",
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });
    option.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
            },
            Array.Empty<string>()
        }
    });
});
#endregion

#region Application-Specific Services
// Configuration-bound services
builder.Services.Configure<SmtpSettings>(builder.Configuration.GetSection("SmtpSettings"));
builder.Services.Configure<AWSSettings>(builder.Configuration.GetSection("AWS"));

// Transient services (lightweight, created each time)
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddTransient<S3UploadService>();

// Scoped services (one instance per HTTP request)
#region Customs Services
builder.Services.AddScoped<RefreshTokenService>();
builder.Services.AddScoped<PermissionServices>();
builder.Services.AddScoped<MasterDataService>();
builder.Services.AddScoped<ISignalRService, SignalRService>();
builder.Services.AddScoped<IProjectServices, ProjectServices>();
builder.Services.AddScoped<IExpensesService, ExpensesService>();
builder.Services.AddScoped<IMasterService, MasterService>();
builder.Services.AddScoped<IDirectoryService, DirectoryService>();
builder.Services.AddScoped<IFirebaseService, FirebaseService>();
builder.Services.AddScoped<IRazorpayService, RazorpayService>();
builder.Services.AddScoped<IAesEncryption, AesEncryption>();
builder.Services.AddScoped<ITenantService, TenantService>();
builder.Services.AddScoped<IServiceProject, ServiceProjectService>();
#endregion

#region Helpers
builder.Services.AddScoped<GeneralHelper>();
builder.Services.AddScoped<UserHelper>();
builder.Services.AddScoped<RolesHelper>();
builder.Services.AddScoped<EmployeeHelper>();
builder.Services.AddScoped<ReportHelper>();
builder.Services.AddScoped<CacheUpdateHelper>();
builder.Services.AddScoped<FeatureDetailsHelper>();
builder.Services.AddScoped<UtilityMongoDBHelper>();
#endregion

#region Cache Services
builder.Services.AddScoped<ProjectCache>();
builder.Services.AddScoped<EmployeeCache>();
builder.Services.AddScoped<ReportCache>();
builder.Services.AddScoped<ExpenseCache>();
builder.Services.AddScoped<SidebarMenuHelper>();
#endregion

// Singleton services (one instance for the app's lifetime)
builder.Services.AddSingleton<ILoggingService, LoggingService>();

string path = Path.Combine(builder.Environment.ContentRootPath, "FireBase", "service-account.json");

FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.FromFile(path),
});
#endregion

#region Web Server (Kestrel)
builder.WebHost.ConfigureKestrel(options =>
{
    options.AddServerHeader = false; // Disable the "Server" header for security
});
#endregion

#endregion

var app = builder.Build();

#region ===================== HTTP Request Pipeline Configuration =====================

// The order of middleware registration is critical for correct application behavior.

#region Global Middleware (Run First)
// These custom middleware components run at the beginning of the pipeline to handle cross-cutting concerns.
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseMiddleware<TenantMiddleware>();
app.UseMiddleware<LoggingMiddleware>();
#endregion

#region Development Environment Configuration
// These tools are only enabled in the Development environment for debugging and API testing.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
#endregion

#region Standard Middleware
// Common middleware for handling static content, security, and routing.
app.UseStaticFiles(); // Enables serving static files (e.g., from wwwroot)
app.UseHttpsRedirection(); // Redirects HTTP requests to HTTPS
#endregion

#region Security (CORS, Authentication & Authorization)
// Security-related middleware must be in the correct order.
var corsPolicy = app.Environment.IsDevelopment() ? "DevCorsPolicy" : "ProdCorsPolicy";
app.UseCors(corsPolicy); // CORS must be applied before Authentication/Authorization.

app.UseAuthentication(); // 1. Identifies who the user is.
app.UseAuthorization();  // 2. Determines what the identified user is allowed to do.
#endregion

#region Endpoint Routing (Run Last)
// These map incoming requests to the correct controller actions or SignalR hubs.
app.MapControllers();
app.MapHub<MarcoHub>("/hubs/marco");
#endregion

#endregion

app.Run();