using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NexusIdentity.Api.Data;
using NexusIdentity.Api.Services;

var builder = WebApplication.CreateBuilder(args);
ApplyEnvironmentOverrides(builder);

builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection(JwtOptions.SectionName));
var jwtOptions = builder.Configuration.GetSection(JwtOptions.SectionName).Get<JwtOptions>() ?? new JwtOptions();

var connectionString = ResolveConnectionString(builder.Configuration.GetConnectionString("Identity"));
builder.Services.AddDbContext<IdentityDbContext>(options =>
{
    if (IsPostgresConnection(connectionString))
    {
        options.UseNpgsql(connectionString);
    }
    else
    {
        options.UseSqlite(connectionString);
    }
});

builder.Services.AddHttpClient<DownloadManifestService>();
builder.Services.AddScoped<TokenService>();
builder.Services.AddScoped<PolicyService>();
builder.Services.Configure<DevicePresenceOptions>(builder.Configuration.GetSection(DevicePresenceOptions.SectionName));
builder.Services.AddSingleton<DevicePresenceService>();

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtOptions.Issuer,
            ValidAudience = jwtOptions.Audience,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey)),
            NameClaimType = "sub",
        };
    });

builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Nexus Identity POC", Version = "0.1.0-poc" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme.",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = "bearer",
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" },
            },
            Array.Empty<string>()
        },
    });
});

var allowedOrigins = builder.Configuration["Nexus:AllowedOrigins"];
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        if (!string.IsNullOrWhiteSpace(allowedOrigins))
        {
            policy.WithOrigins(allowedOrigins.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
                .AllowAnyHeader()
                .AllowAnyMethod();
        }
        else
        {
            policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
        }
    });
});

var app = builder.Build();

// POC laptop: seed test users unless explicitly disabled (Nexus:SeedPocUsers=false).
var seedPocUsers = !string.Equals(
    builder.Configuration["Nexus:SeedPocUsers"],
    "false",
    StringComparison.OrdinalIgnoreCase);

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<IdentityDbContext>();
    await SeedData.InitializeAsync(db, seedPocUsers);
}

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

static void ApplyEnvironmentOverrides(WebApplicationBuilder builder)
{
    var overrides = new Dictionary<string, string?>();

    var dbConnection = Environment.GetEnvironmentVariable("NEXUS_DB_CONNECTION");
    if (!string.IsNullOrWhiteSpace(dbConnection))
    {
        overrides["ConnectionStrings:Identity"] = dbConnection;
    }

    var jwtKey = Environment.GetEnvironmentVariable("NEXUS_JWT_SIGNING_KEY");
    if (!string.IsNullOrWhiteSpace(jwtKey))
    {
        overrides["Jwt:SigningKey"] = jwtKey;
    }

    var allowedOrigins = Environment.GetEnvironmentVariable("NEXUS_ALLOWED_ORIGINS");
    if (!string.IsNullOrWhiteSpace(allowedOrigins))
    {
        overrides["Nexus:AllowedOrigins"] = allowedOrigins;
    }

    var nexusEnvironment = Environment.GetEnvironmentVariable("NEXUS_ENVIRONMENT");
    if (!string.IsNullOrWhiteSpace(nexusEnvironment))
    {
        overrides["Nexus:Environment"] = nexusEnvironment;
        builder.Environment.EnvironmentName = nexusEnvironment;
    }

    var seedPocUsers = Environment.GetEnvironmentVariable("NEXUS_SEED_POC_USERS");
    if (!string.IsNullOrWhiteSpace(seedPocUsers))
    {
        overrides["Nexus:SeedPocUsers"] = seedPocUsers;
    }

    var downloadsBase = Environment.GetEnvironmentVariable("NEXUS_DOWNLOADS_BASE_URL");
    if (!string.IsNullOrWhiteSpace(downloadsBase))
    {
        overrides["Nexus:DownloadsBaseUrl"] = downloadsBase.TrimEnd('/');
    }

    var domain = Environment.GetEnvironmentVariable("NEXUS_DOMAIN");
    if (!string.IsNullOrWhiteSpace(domain))
    {
        var issuer = domain.StartsWith("http", StringComparison.OrdinalIgnoreCase)
            ? domain.TrimEnd('/')
            : $"https://{domain.TrimEnd('/')}";
        overrides["Jwt:Issuer"] = issuer;
    }

    if (overrides.Count > 0)
    {
        builder.Configuration.AddInMemoryCollection(overrides);
    }
}

static string ResolveConnectionString(string? configured)
{
    var connectionString = string.IsNullOrWhiteSpace(configured)
        ? BuildDefaultSqliteConnectionString()
        : configured.Trim();

    if (IsPostgresConnection(connectionString))
    {
        return connectionString;
    }

    var dataSource = ExtractSqliteDataSource(connectionString);
    if (string.IsNullOrWhiteSpace(dataSource) || !Path.IsPathRooted(dataSource))
    {
        return BuildDefaultSqliteConnectionString();
    }

    EnsureSqliteDirectory(dataSource);
    return connectionString;
}

static string BuildDefaultSqliteConnectionString()
{
    var dbPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        "NinjaNexus",
        "identity",
        "nexus-identity.db");
    EnsureSqliteDirectory(dbPath);
    return $"Data Source={dbPath}";
}

static string? ExtractSqliteDataSource(string connectionString)
{
    foreach (var segment in connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
    {
        var split = segment.Split('=', 2);
        if (split.Length == 2
            && split[0].Trim().Equals("Data Source", StringComparison.OrdinalIgnoreCase))
        {
            return split[1].Trim();
        }
    }

    return null;
}

static void EnsureSqliteDirectory(string dbPath)
{
    var directory = Path.GetDirectoryName(dbPath);
    if (!string.IsNullOrWhiteSpace(directory))
    {
        Directory.CreateDirectory(directory);
    }
}

static bool IsPostgresConnection(string connectionString) =>
    connectionString.Contains("Host=", StringComparison.OrdinalIgnoreCase)
    || connectionString.Contains("postgres", StringComparison.OrdinalIgnoreCase);
