using System.Net.Http.Json;
using System.Net;
using System.Net.Http.Headers;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using UniVerse.Application.DTOs.Sync;
using UniVerse.Application.Interfaces;

namespace UniVerse.Infrastructure.ExternalServices;

public class ModeusApiClient : IModeusApiClient
{
    private readonly HttpClient _http;
    private readonly ILogger<ModeusApiClient> _logger;

    public ModeusApiClient(HttpClient http, IConfiguration config, ILogger<ModeusApiClient> logger)
    {
        _http = http; _logger = logger;
        var apiKey = config["ModeusApi:ApiKey"];
        if (!string.IsNullOrEmpty(apiKey))
            _http.DefaultRequestHeaders.Add("X-API-Key", apiKey);
    }

    public async Task<ModeusEventsResponse> SearchEventsAsync(SyncScheduleRequest request)
    {
        var pageSize = request.Size is > 0 ? request.Size.Value : 900;
        var body = new Dictionary<string, object?>
        {
            ["size"] = pageSize,
            ["timeMin"] = request.TimeMin,
            ["timeMax"] = request.TimeMax
        };

        AddNonEmpty(body, "roomId", request.RoomId);
        AddNonEmpty(body, "attendeePersonId", request.AttendeePersonId);
        AddNonEmpty(body, "courseUnitRealizationId", request.CourseUnitRealizationId);
        AddNonEmpty(body, "cycleRealizationId", request.CycleRealizationId);
        AddNonEmpty(body, "specialtyCode", request.SpecialtyCode);
        AddNonEmpty(body, "learningStartYear", request.LearningStartYear);
        AddNonEmpty(body, "profileName", request.ProfileName);
        AddNonEmpty(body, "curriculumId", request.CurriculumId);
        AddNonEmpty(body, "typeId", request.TypeId);

        var requestJson = JsonSerializer.Serialize(body);
        var requestSummary = $"POST /api/ictis?includeCounts=true. Request JSON: {requestJson}";
        var response = await _http.PostAsJsonAsync("/api/ictis?includeCounts=true", body);
        await EnsureSuccessAsync(response, "ICTIS events search", requestSummary);
        return await ReadJsonAsync<ModeusEventsResponse>(response, "ICTIS events search", requestSummary)
            ?? new ModeusEventsResponse();
    }

    public async Task<ModeusRoomsResponse> SearchRoomsAsync()
    {
        const int pageSize = 100;
        var allRooms = new List<ModeusRoom>();
        var page = 0;
        var totalPages = 1;

        do
        {
            var body = new
            {
                name = "",
                sort = "+building.name,+name",
                size = pageSize,
                page,
                deleted = false
            };

            var response = await _http.PostAsJsonAsync("/api/proxy/rooms/search", body);
            await EnsureSuccessAsync(response, "Modeus rooms search",
                $"name=<empty>, sort=+building.name,+name, size={pageSize}, page={page}, deleted=false");

            var payload = await ReadJsonAsync<ModeusRoomsResponse>(response, "Modeus rooms search",
                $"name=<empty>, sort=+building.name,+name, size={pageSize}, page={page}, deleted=false")
                ?? new ModeusRoomsResponse();
            allRooms.AddRange(payload.RoomItems);

            totalPages = payload.Page?.TotalPages ?? page + 1;
            page++;
        }
        while (page < totalPages);

        return new ModeusRoomsResponse { Rooms = allRooms };
    }

    private static async Task EnsureSuccessAsync(HttpResponseMessage response, string operation, string requestSummary)
    {
        if (response.IsSuccessStatusCode) return;

        var responseBody = await response.Content.ReadAsStringAsync();

        throw new HttpRequestException(
            $"{operation} failed with HTTP {(int)response.StatusCode} {response.ReasonPhrase}. Request: {requestSummary}. Response body: {Truncate(responseBody)}",
            null,
            response.StatusCode);
    }

    private static void AddNonEmpty<T>(
        IDictionary<string, object?> body,
        string key,
        IReadOnlyList<T>? values)
    {
        if (values is { Count: > 0 })
            body[key] = values;
    }

    private static async Task<T?> ReadJsonAsync<T>(HttpResponseMessage response, string operation, string requestSummary)
    {
        var responseBody = await response.Content.ReadAsStringAsync();
        var contentType = response.Content.Headers.ContentType?.ToString() ?? "<empty>";
        var contentLength = response.Content.Headers.ContentLength?.ToString() ?? "<unknown>";

        if (string.IsNullOrWhiteSpace(responseBody))
        {
            throw new HttpRequestException(
                $"{operation} returned HTTP {(int)response.StatusCode} {response.ReasonPhrase} with an empty response body. Request: {requestSummary}. Content-Type: {contentType}. Content-Length: {contentLength}.",
                null,
                response.StatusCode);
        }

        try
        {
            return JsonSerializer.Deserialize<T>(responseBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        }
        catch (JsonException ex)
        {
            throw new InvalidOperationException(
                $"{operation} returned invalid JSON. Request: {requestSummary}. Content-Type: {contentType}. Response body: {Truncate(responseBody)}",
                ex);
        }
    }

    private static string Truncate(string value) =>
        value.Length > 2000 ? string.Concat(value.AsSpan(0, 2000), "...<truncated>") : value;

    public async Task<List<ModeusEmployee>> SearchEmployeeAsync(string fullname)
    {
        var response = await _http.GetFromJsonAsync<List<ModeusEmployee>>(
            $"/api/schedule/searchemployee?fullname={Uri.EscapeDataString(fullname)}");
        return response ?? new();
    }

    public async Task<string?> GetSubIdByFullNameAsync(string fullname, CancellationToken cancellationToken = default)
    {
        using var request = new HttpRequestMessage(
            HttpMethod.Get,
            $"/api/universe/subid?fullname={Uri.EscapeDataString(fullname)}");
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));

        using var response = await _http.SendAsync(request, cancellationToken);
        if (response.StatusCode == HttpStatusCode.NotFound)
            return null;

        await EnsureSuccessAsync(response, "Universe user sub lookup", $"fullname={fullname}");
        var body = await response.Content.ReadAsStringAsync(cancellationToken);
        return string.IsNullOrWhiteSpace(body) ? null : body.Trim();
    }
}
