using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;

namespace ClaudeDo.Worker.Tickets;

/// <summary>
/// Der eine HTTP-Zugang zum Bandel-Ticketsystem. Bewusst kein Interface: es gibt genau eine
/// Implementierung. Kommt ein zweites Ticketsystem (Jira), ist der Extract ein Refactoring.
/// </summary>
public sealed class BandelTicketClient
{
    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        PropertyNameCaseInsensitive = true,
    };

    private readonly HttpClient _http;
    private readonly string _baseUrl;
    private readonly string _token;
    private readonly ILogger<BandelTicketClient> _logger;

    public BandelTicketClient(HttpClient http, string baseUrl, string token, ILogger<BandelTicketClient> logger)
    {
        _http = http;
        _baseUrl = baseUrl.TrimEnd('/');
        _token = token;
        _logger = logger;
        if (_http.Timeout == Timeout.InfiniteTimeSpan || _http.Timeout > TimeSpan.FromSeconds(10))
            _http.Timeout = TimeSpan.FromSeconds(10);
    }

    public Task<TicketIdentity> GetIdentityAsync(CancellationToken ct)
        => SendAsync<TicketIdentity>(HttpMethod.Get, "/api/ticketsystem/pat/me", null, ct);

    public async Task<IReadOnlyList<TicketProject>> GetProjectsAsync(CancellationToken ct)
    {
        var departments = await SendAsync<List<SidebarDepartment>>(
            HttpMethod.Get, "/api/Navigation/sidebar", null, ct);

        return departments
            .SelectMany(d => d.Projects.Select(p => new TicketProject(p.Id, p.Title, d.DepartmentName)))
            .ToList();
    }

    public async Task<IReadOnlyList<TicketBoardItem>> GetProjectBoardAsync(int projectId, CancellationToken ct)
        => await SendAsync<List<TicketBoardItem>>(
            HttpMethod.Get, $"/api/Board/project/{projectId}", null, ct);

    public async Task SetStatusAsync(int ticketId, int statusId, CancellationToken ct)
        => await SendAsync<JsonElement>(
            HttpMethod.Patch, $"/api/Ticket/{ticketId}/status",
            new StringContent($"{{\"statusId\":{statusId}}}", Encoding.UTF8, "application/json"), ct);

    private async Task<T> SendAsync<T>(HttpMethod method, string path, HttpContent? body, CancellationToken ct)
    {
        using var request = new HttpRequestMessage(method, _baseUrl + path) { Content = body };
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);

        HttpResponseMessage response;
        try
        {
            response = await _http.SendAsync(request, ct);
        }
        // Bewusst JEDE Exception, nicht nur HttpRequestException/TaskCanceledException: eine
        // fehlerhafte Base-URL wirft InvalidOperationException oder UriFormatException, und die
        // flogen vorher roh aus dem Hub. Der Aufrufer bekam dadurch statt eines Grundes nur ein
        // verschlucktes null — die UI meldete "Worker not reachable", obwohl der Worker lief.
        // Eine echte Abbruchanforderung wird davon nicht angefasst.
        catch (Exception ex) when (!ct.IsCancellationRequested)
        {
            throw new TicketApiException($"Ticketsystem nicht erreichbar ({_baseUrl}): {ex.Message}");
        }

        using (response)
        {
            var payload = await response.Content.ReadAsStringAsync(ct);

            if (!response.IsSuccessStatusCode)
            {
                var hint = response.StatusCode switch
                {
                    HttpStatusCode.Unauthorized => "Token fehlt oder ist ungültig",
                    HttpStatusCode.Forbidden => "Token fehlt der nötige Scope",
                    HttpStatusCode.NotFound => "Nicht gefunden",
                    _ => "Fehler",
                };
                throw new TicketApiException($"{hint} ({(int)response.StatusCode} bei {method} {path}).");
            }

            BandelEnvelope<T>? envelope;
            try
            {
                envelope = JsonSerializer.Deserialize<BandelEnvelope<T>>(payload, JsonOpts);
            }
            catch (JsonException ex)
            {
                throw new TicketApiException($"Unerwartete Antwort von {method} {path}: {ex.Message}");
            }

            if (envelope is null || !envelope.Success)
                throw new TicketApiException(envelope?.Message ?? $"Ticketsystem meldete einen Fehler bei {method} {path}.");

            if (envelope.Data is null)
                throw new TicketApiException($"Ticketsystem lieferte keine Daten bei {method} {path}.");

            return envelope.Data;
        }
    }
}
