using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text; using System.Text.Json; using SilverLabs.Website.Models; namespace SilverLabs.Website.Services; public class DeveloperApplicationService { private readonly HttpClient _httpClient; private readonly ILogger _logger; public DeveloperApplicationService(HttpClient httpClient, ILogger logger) { _httpClient = httpClient; _logger = logger; } /// /// Checks username availability. Returns: true = available, false = taken, null = error/unknown. /// public async Task CheckUsernameAsync(string username) { try { var response = await _httpClient.GetAsync($"/api/auth/check-username/{Uri.EscapeDataString(username)}"); if (!response.IsSuccessStatusCode) { _logger.LogWarning("Username check returned {StatusCode} for {Username}", response.StatusCode, username); return null; } var result = await response.Content.ReadFromJsonAsync(); if (result.TryGetProperty("available", out var available)) return available.GetBoolean(); return null; } catch (Exception ex) { _logger.LogError(ex, "Error checking username availability for {Username}", username); return null; } } public async Task<(bool Success, string Message, string? Token)> SubmitApplicationAsync(DeveloperApplication application) { try { // 1. Register user on SilverDESK var registerPayload = new { username = application.DesiredUsername, email = application.Email, password = application.Password, fullName = application.FullName }; var registerResponse = await _httpClient.PostAsJsonAsync("/api/auth/register", registerPayload); if (!registerResponse.IsSuccessStatusCode) { var errorBody = await registerResponse.Content.ReadAsStringAsync(); _logger.LogError("SilverDESK registration failed: {StatusCode} - {Body}", registerResponse.StatusCode, errorBody); var friendlyMessage = ParseRegistrationError(errorBody); return (false, friendlyMessage, null); } var authResult = await registerResponse.Content.ReadFromJsonAsync(); var token = authResult.GetProperty("token").GetString(); // 2. Create ticket using the user's own JWT var ticketBody = FormatTicketBody(application); var ticketPayload = new { Subject = $"[Developer Program] {application.Role} Application - {application.FullName}", Description = ticketBody, Priority = "Medium", Category = "Developer Program" }; // Use a fresh HttpClient without the X-API-Key default header so that // SilverDESK's MultiAuth policy routes to Bearer/JWT auth (the new user's token) // instead of ApiKey auth (which resolves to the MCP system user). using var userClient = new HttpClient { BaseAddress = _httpClient.BaseAddress }; var ticketRequest = new HttpRequestMessage(HttpMethod.Post, "/api/tickets"); ticketRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); ticketRequest.Content = JsonContent.Create(ticketPayload); var ticketResponse = await userClient.SendAsync(ticketRequest); if (!ticketResponse.IsSuccessStatusCode) { var errorBody = await ticketResponse.Content.ReadAsStringAsync(); _logger.LogError("Failed to create ticket: {StatusCode} - {Body}", ticketResponse.StatusCode, errorBody); // User was created but ticket failed — still return success with a note return (true, "Your account has been created, but we had trouble submitting your application ticket. Please log in to SilverDESK and create a support ticket.", token); } // 3. Create DeveloperApplication record linking user + ticket try { var userId = authResult.GetProperty("user").GetProperty("id").GetString(); var ticketResult = await ticketResponse.Content.ReadFromJsonAsync(); var ticketId = ticketResult.GetProperty("id").GetString(); var applicationPayload = new { userId, ticketId, fullName = application.FullName, email = application.Email, desiredUsername = application.DesiredUsername, timezone = application.Timezone, appliedRole = application.Role.ToString(), platforms = application.Platforms, skills = application.Skills ?? "", motivation = application.Motivation, status = 0, // Pending silverDeskProvisioned = true }; var appResponse = await _httpClient.PostAsJsonAsync("/api/developer-program/applications", applicationPayload); if (appResponse.IsSuccessStatusCode) { _logger.LogInformation("DeveloperApplication record created for {Email}", application.Email); } else { var appError = await appResponse.Content.ReadAsStringAsync(); _logger.LogWarning("Failed to create DeveloperApplication record for {Email}: {StatusCode} - {Body}", application.Email, appResponse.StatusCode, appError); } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to create DeveloperApplication record for {Email} — user and ticket were created successfully", application.Email); } _logger.LogInformation("Developer application submitted for {Email} as {Role} — user registered and ticket created", application.Email, application.Role); return (true, "Application submitted successfully! Your SilverDESK account has been created.", token); } catch (Exception ex) { _logger.LogError(ex, "Error submitting developer application for {Email}", application.Email); return (false, "Unable to connect to the application service. Please try again later.", null); } } private static string ParseRegistrationError(string errorBody) { try { var error = JsonSerializer.Deserialize(errorBody); if (error.TryGetProperty("message", out var message)) { var msg = message.GetString() ?? ""; if (msg.Contains("Username already exists", StringComparison.OrdinalIgnoreCase)) return "That username is already taken. Please choose a different one."; if (msg.Contains("Email already exists", StringComparison.OrdinalIgnoreCase)) return "An account with that email already exists."; if (msg.Contains("Password", StringComparison.OrdinalIgnoreCase)) return msg; return msg; } } catch { } return "Something went wrong creating your account. Please try again later."; } private static string FormatTicketBody(DeveloperApplication app) { var sb = new StringBuilder(); sb.AppendLine("## Developer Program Application"); sb.AppendLine(); sb.AppendLine($"**Role:** {app.Role}"); sb.AppendLine($"**Full Name:** {app.FullName}"); sb.AppendLine($"**Email:** {app.Email}"); sb.AppendLine($"**Desired Username:** {app.DesiredUsername}"); sb.AppendLine($"**Timezone:** {app.Timezone}"); sb.AppendLine(); sb.AppendLine($"**Platforms:** {string.Join(", ", app.Platforms)}"); sb.AppendLine(); if (app.Role == ApplicationRole.Developer && !string.IsNullOrWhiteSpace(app.Skills)) { sb.AppendLine("**Skills & Experience:**"); sb.AppendLine(app.Skills); sb.AppendLine(); } sb.AppendLine("**Motivation:**"); sb.AppendLine(app.Motivation); return sb.ToString(); } }