using System.Collections.Concurrent;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace SilverLabs.Website.Services;

public record PendingDeployment(
    string Token,
    string Username,
    string Email,
    string FullName,
    string TicketId,
    string? Role,
    DateTime CreatedAt,
    DateTime ExpiresAt);

public class ProvisioningService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<ProvisioningService> _logger;
    private readonly IConfiguration _configuration;

    private static readonly ConcurrentDictionary<string, PendingDeployment> _pendingDeployments = new();

    public ProvisioningService(
        IHttpClientFactory httpClientFactory,
        ILogger<ProvisioningService> logger,
        IConfiguration configuration)
    {
        _httpClientFactory = httpClientFactory;
        _logger = logger;
        _configuration = configuration;
    }

    // --- Token management ---

    public PendingDeployment CreatePendingDeployment(string username, string email, string fullName, string ticketId, string? role = null)
    {
        CleanupExpiredTokens();

        var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))
            .Replace("+", "-").Replace("/", "_").TrimEnd('=');

        var deployment = new PendingDeployment(
            token, username, email, fullName, ticketId, role,
            DateTime.UtcNow, DateTime.UtcNow.AddHours(48));

        _pendingDeployments[token] = deployment;
        _logger.LogInformation("Created pending deployment for {Username} (ticket {TicketId}), token expires {ExpiresAt}",
            username, ticketId, deployment.ExpiresAt);

        return deployment;
    }

    public PendingDeployment? GetPendingDeployment(string token)
    {
        CleanupExpiredTokens();

        if (_pendingDeployments.TryGetValue(token, out var deployment))
        {
            if (deployment.ExpiresAt > DateTime.UtcNow)
                return deployment;

            _pendingDeployments.TryRemove(token, out _);
        }

        return null;
    }

    public void RemovePendingDeployment(string token)
    {
        _pendingDeployments.TryRemove(token, out _);
    }

    private void CleanupExpiredTokens()
    {
        var expired = _pendingDeployments
            .Where(kvp => kvp.Value.ExpiresAt <= DateTime.UtcNow)
            .Select(kvp => kvp.Key)
            .ToList();

        foreach (var key in expired)
            _pendingDeployments.TryRemove(key, out _);
    }

    // --- Authentication ---

    public async Task<bool> ValidateSilverDeskCredentialsAsync(string username, string password)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("SilverDesk");
            var payload = new { username, password };
            var response = await client.PostAsJsonAsync("/api/auth/login", payload);

            if (response.IsSuccessStatusCode)
            {
                _logger.LogInformation("SilverDESK credential validation succeeded for {Username}", username);
                return true;
            }

            _logger.LogWarning("SilverDESK credential validation failed for {Username}: {Status}", username, response.StatusCode);
            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error validating SilverDESK credentials for {Username}", username);
            return false;
        }
    }

    // --- Full provisioning with password ---

    public async Task<(bool Success, string Message)> ProvisionWithPasswordAsync(
        string ticketId, string username, string email, string fullName, string password, string? role = null)
    {
        var results = new List<string>();
        var allSuccess = true;

        // 1. Create Mattermost user
        var (mmOk, mmMsg) = await CreateMattermostUserAsync(username, email, fullName, password);
        results.Add($"Mattermost: {mmMsg}");
        if (!mmOk) allSuccess = false;

        // 1b. Add to SilverLABS team (only if user was created)
        if (mmOk)
        {
            var (teamOk, teamMsg) = await AddMattermostUserToTeamAsync(username);
            results.Add($"Mattermost Team: {teamMsg}");
            if (!teamOk) allSuccess = false;
        }

        // 2. Create Mailcow mailbox
        var (mailOk, mailMsg) = await CreateMailcowMailboxAsync(username, fullName, password);
        results.Add($"Mailcow: {mailMsg}");
        if (!mailOk) allSuccess = false;

        // 3. Create Gitea user (Developers only)
        var giteaOk = false;
        if (string.Equals(role, "Developer", StringComparison.OrdinalIgnoreCase))
        {
            var (gOk, giteaMsg) = await CreateGiteaUserAsync(username, email, fullName, password);
            giteaOk = gOk;
            results.Add($"Gitea: {giteaMsg}");
            if (!giteaOk) allSuccess = false;
        }
        else
        {
            results.Add("Gitea: Skipped (not required for Tester role)");
        }

        // 4. Update the DeveloperApplication record in SilverDESK
        var (updateOk, updateMsg) = await UpdateApplicationStatusAsync(ticketId, mmOk, mailOk, giteaOk);
        results.Add($"Application record: {updateMsg}");
        if (!updateOk) allSuccess = false;

        var summary = string.Join("; ", results);
        _logger.LogInformation("Provisioning for {Username} (ticket {TicketId}): {Summary}", username, ticketId, summary);

        return (allSuccess, summary);
    }

    // --- Ticket replies ---

    public async Task<(bool Success, string Message)> SendTicketReplyAsync(string ticketId, string content, string action = "waitingcustomer")
    {
        try
        {
            var client = _httpClientFactory.CreateClient("SilverDesk");
            var payload = new { content, action };
            var response = await client.PostAsJsonAsync($"/api/tickets/{ticketId}/reply", payload);

            if (response.IsSuccessStatusCode)
            {
                _logger.LogInformation("Sent ticket reply to {TicketId} with action {Action}", ticketId, action);
                return (true, "Reply sent");
            }

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogWarning("Failed to send ticket reply to {TicketId}: {Status} {Body}", ticketId, response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending ticket reply to {TicketId}", ticketId);
            return (false, $"Error: {ex.Message}");
        }
    }

    // --- Password sync ---

    public async Task<(bool Success, string Message)> SyncPasswordAsync(string username, string newPassword)
    {
        // Normalize username to lowercase - Mattermost and Gitea store usernames as lowercase
        // and their API lookups are case-sensitive
        var normalizedUsername = username.ToLowerInvariant();
        var results = new List<string>();
        var allSuccess = true;

        // 1. Mattermost - need to look up user ID first
        var (mmOk, mmMsg) = await UpdateMattermostPasswordAsync(normalizedUsername, newPassword);
        results.Add($"Mattermost: {mmMsg}");
        if (!mmOk) allSuccess = false;

        // 2. Mailcow
        var (mailOk, mailMsg) = await UpdateMailcowPasswordAsync(normalizedUsername, newPassword);
        results.Add($"Mailcow: {mailMsg}");
        if (!mailOk) allSuccess = false;

        // 3. Gitea
        var (giteaOk, giteaMsg) = await UpdateGiteaPasswordAsync(normalizedUsername, newPassword);
        results.Add($"Gitea: {giteaMsg}");
        if (!giteaOk) allSuccess = false;

        var summary = string.Join("; ", results);
        _logger.LogInformation("Password sync for {Username} (normalized: {NormalizedUsername}): {Summary}", username, normalizedUsername, summary);

        return (allSuccess, summary);
    }

    // --- Application status update ---

    private async Task<(bool Success, string Message)> UpdateApplicationStatusAsync(
        string ticketId, bool mattermostProvisioned, bool mailcowProvisioned, bool giteaProvisioned = false)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("SilverDesk");

            var lookupResponse = await client.GetAsync($"/api/developer-program/applications?ticketId={ticketId}");
            if (!lookupResponse.IsSuccessStatusCode)
            {
                var body = await lookupResponse.Content.ReadAsStringAsync();
                _logger.LogWarning("Failed to look up application by ticket {TicketId}: {Status} {Body}",
                    ticketId, lookupResponse.StatusCode, body);
                return (false, $"Lookup failed ({lookupResponse.StatusCode})");
            }

            var apps = await lookupResponse.Content.ReadFromJsonAsync<JsonElement>();
            if (apps.GetArrayLength() == 0)
            {
                _logger.LogWarning("No application found for ticket {TicketId}", ticketId);
                return (false, "No application found for ticket");
            }

            var appId = apps[0].GetProperty("id").GetString();

            var updatePayload = new
            {
                status = 1, // ApplicationStatus.Approved
                mattermostProvisioned,
                mailcowProvisioned,
                giteaProvisioned
            };

            var updateResponse = await client.PutAsJsonAsync($"/api/developer-program/applications/{appId}", updatePayload);
            if (updateResponse.IsSuccessStatusCode)
            {
                _logger.LogInformation("Application {AppId} updated to Approved for ticket {TicketId}", appId, ticketId);
                return (true, "Updated to Approved");
            }

            var updateBody = await updateResponse.Content.ReadAsStringAsync();
            _logger.LogWarning("Failed to update application {AppId}: {Status} {Body}",
                appId, updateResponse.StatusCode, updateBody);
            return (false, $"Update failed ({updateResponse.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error updating application status for ticket {TicketId}", ticketId);
            return (false, $"Error: {ex.Message}");
        }
    }

    // --- Service account creation ---

    public async Task<(bool Success, string Message)> CreateSilverDeskUserAsync(string username, string email, string fullName)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("SilverDesk");
            var payload = new
            {
                username,
                email,
                fullName,
                password = Guid.NewGuid().ToString("N")[..16] + "!A1"
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync("/api/users", content);
            if (response.IsSuccessStatusCode)
                return (true, "User created");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogError("SilverDESK user creation failed: {Status} {Body}", response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "SilverDESK user creation error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> CreateMattermostUserAsync(
        string username, string email, string fullName, string password)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Mattermost");
            var nameParts = fullName.Split(' ', 2);
            var payload = new
            {
                email,
                username,
                first_name = nameParts[0],
                last_name = nameParts.Length > 1 ? nameParts[1] : "",
                password
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync("/api/v4/users", content);
            if (response.IsSuccessStatusCode)
                return (true, "User created");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogError("Mattermost user creation failed: {Status} {Body}", response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Mattermost user creation error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> AddMattermostUserToTeamAsync(string username)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Mattermost");

            // Look up user ID by username
            var userResponse = await client.GetAsync($"/api/v4/users/username/{username}");
            if (!userResponse.IsSuccessStatusCode)
                return (false, $"User lookup failed ({userResponse.StatusCode})");

            var userData = await userResponse.Content.ReadFromJsonAsync<JsonElement>();
            var userId = userData.GetProperty("id").GetString();

            // Add to SilverLABS team
            var teamId = _configuration["Mattermost:TeamId"] ?? "ear83bc7nprzpe878ey7hxza7h";
            var payload = new { team_id = teamId, user_id = userId };
            var response = await client.PostAsJsonAsync($"/api/v4/teams/{teamId}/members", payload);

            if (response.IsSuccessStatusCode)
                return (true, "Added to team");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogError("Mattermost team join failed: {Status} {Body}", response.StatusCode, body);
            return (false, $"Team join failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Mattermost team join error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> CreateMailcowMailboxAsync(
        string username, string fullName, string password)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Mailcow");
            var payload = new
            {
                local_part = username,
                domain = "silverlabs.uk",
                name = fullName,
                password,
                password2 = password,
                quota = 1024, // 1GB
                active = 1,
                force_pw_update = 0
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync("/api/v1/add/mailbox", content);
            if (response.IsSuccessStatusCode)
                return (true, "Mailbox created");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogError("Mailcow mailbox creation failed: {Status} {Body}", response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Mailcow mailbox creation error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> CreateGiteaUserAsync(
        string username, string email, string fullName, string password)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Gitea");
            var payload = new
            {
                email,
                full_name = fullName,
                login_name = username,
                must_change_password = false,
                password,
                send_notify = false,
                username,
                visibility = "public"
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync("/api/v1/admin/users", content);
            if (response.IsSuccessStatusCode)
                return (true, "User created");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogError("Gitea user creation failed: {Status} {Body}", response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Gitea user creation error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    // --- Password update methods ---

    private async Task<(bool Success, string Message)> UpdateMattermostPasswordAsync(string username, string newPassword)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Mattermost");

            // Look up user ID by username
            var userResponse = await client.GetAsync($"/api/v4/users/username/{username}");
            if (!userResponse.IsSuccessStatusCode)
            {
                var lookupBody = await userResponse.Content.ReadAsStringAsync();
                _logger.LogWarning("Mattermost user lookup failed for {Username}: {Status} {Body}",
                    username, userResponse.StatusCode, lookupBody);
                return (false, $"User not found ({userResponse.StatusCode})");
            }

            var userData = await userResponse.Content.ReadFromJsonAsync<JsonElement>();
            var userId = userData.GetProperty("id").GetString();

            // Update password (admin reset — no old password needed with bot token)
            var payload = new { new_password = newPassword };
            var response = await client.PutAsJsonAsync($"/api/v4/users/{userId}/password", payload);

            if (response.IsSuccessStatusCode)
                return (true, "Password updated");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogWarning("Mattermost password update failed for {Username}: {Status} {Body}", username, response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Mattermost password update error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> UpdateMailcowPasswordAsync(string username, string newPassword)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Mailcow");
            var payload = new
            {
                items = new[] { $"{username}@silverlabs.uk" },
                attr = new
                {
                    password = newPassword,
                    password2 = newPassword
                }
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync("/api/v1/edit/mailbox", content);
            if (response.IsSuccessStatusCode)
                return (true, "Password updated");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogWarning("Mailcow password update failed for {Username}: {Status} {Body}", username, response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Mailcow password update error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }

    private async Task<(bool Success, string Message)> UpdateGiteaPasswordAsync(string username, string newPassword)
    {
        try
        {
            var client = _httpClientFactory.CreateClient("Gitea");
            var payload = new
            {
                login_name = username,
                password = newPassword,
                must_change_password = false,
                source_id = 0
            };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var request = new HttpRequestMessage(HttpMethod.Patch, $"/api/v1/admin/users/{username}")
            {
                Content = content
            };

            var response = await client.SendAsync(request);
            if (response.IsSuccessStatusCode)
                return (true, "Password updated");

            var body = await response.Content.ReadAsStringAsync();
            _logger.LogWarning("Gitea password update failed for {Username}: {Status} {Body}", username, response.StatusCode, body);
            return (false, $"Failed ({response.StatusCode})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Gitea password update error for {Username}", username);
            return (false, $"Error: {ex.Message}");
        }
    }
}
