using DeepDrftModels.DTOs;
using Models.Common;
using NetBlocks.Models;
using System.Text.Json;
using System.Web;
using Microsoft.AspNetCore.Http;

namespace DeepDrftPublic.Client.Clients;

public class TrackClient
{
    private readonly HttpClient _http;

    public TrackClient(IHttpClientFactory httpClientFactory)
    {
        _http = httpClientFactory.CreateClient("DeepDrft.API");
    }

    public async Task<ApiResult<PagedResult<TrackDto>>> GetPage(
        int pageNumber,
        int pageSize,
        string? sortColumn = null,
        bool sortDescending = false,
        string? searchText = null,
        string? album = null,
        string? genre = null,
        long? releaseId = null)
    {
        var queryArgs = new Dictionary<string, string?>(){
            ["page"] = pageNumber.ToString(),
            ["pageSize"] = pageSize.ToString()
        };

        if (!string.IsNullOrEmpty(sortColumn))
            queryArgs["sortColumn"] = sortColumn;

        if (sortDescending)
            queryArgs["sortDescending"] = "true";

        if (!string.IsNullOrEmpty(searchText))
            queryArgs["q"] = searchText;

        if (!string.IsNullOrEmpty(album))
            queryArgs["album"] = album;

        if (!string.IsNullOrEmpty(genre))
            queryArgs["genre"] = genre;

        if (releaseId is { } id)
            queryArgs["releaseId"] = id.ToString();

        string query = QueryString.Create(queryArgs).ToString();

        var response = await _http.GetAsync($"api/track/page{query}");

        if (!response.IsSuccessStatusCode)
            return ApiResult<PagedResult<TrackDto>>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var paged = JsonSerializer.Deserialize<PagedResult<TrackDto>>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return paged is not null
            ? ApiResult<PagedResult<TrackDto>>.CreatePassResult(paged)
            : ApiResult<PagedResult<TrackDto>>.CreateFailResult("Failed to deserialize response");
    }

    /// <summary>
    /// Fetches a random track from the public library. A 404 means the library is empty — a valid
    /// state, not an error — so it returns a pass result with a null value. Any other non-success
    /// status is a genuine failure.
    /// </summary>
    public async Task<ApiResult<TrackDto?>> GetRandom()
    {
        var response = await _http.GetAsync("api/track/random");

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return ApiResult<TrackDto?>.CreatePassResult(null);

        if (!response.IsSuccessStatusCode)
            return ApiResult<TrackDto?>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var track = JsonSerializer.Deserialize<TrackDto>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return track is not null
            ? ApiResult<TrackDto?>.CreatePassResult(track)
            : ApiResult<TrackDto?>.CreateFailResult("Failed to deserialize response");
    }

    public async Task<ApiResult<List<ReleaseDto>>> GetAlbums()
    {
        var response = await _http.GetAsync("api/track/albums");

        if (!response.IsSuccessStatusCode)
            return ApiResult<List<ReleaseDto>>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var releases = JsonSerializer.Deserialize<List<ReleaseDto>>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return releases is not null
            ? ApiResult<List<ReleaseDto>>.CreatePassResult(releases)
            : ApiResult<List<ReleaseDto>>.CreateFailResult("Failed to deserialize response");
    }

    public async Task<ApiResult<List<GenreSummaryDto>>> GetGenres()
    {
        var response = await _http.GetAsync("api/track/genres");

        if (!response.IsSuccessStatusCode)
            return ApiResult<List<GenreSummaryDto>>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var genres = JsonSerializer.Deserialize<List<GenreSummaryDto>>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return genres is not null
            ? ApiResult<List<GenreSummaryDto>>.CreatePassResult(genres)
            : ApiResult<List<GenreSummaryDto>>.CreateFailResult("Failed to deserialize response");
    }

    /// <summary>
    /// Fetches the per-track high-res waveform datum, addressed by the track's EntryKey (phase-12 §5b).
    /// A 404 means no high-res datum is stored (a track not yet backfilled) — a valid state, so it
    /// returns a pass result with a null value and the visualizer blanks gracefully. Any other
    /// non-success status is a genuine failure.
    /// </summary>
    public async Task<ApiResult<WaveformProfileDto?>> GetTrackWaveform(string trackEntryKey)
    {
        var response = await _http.GetAsync($"api/track/{Uri.EscapeDataString(trackEntryKey)}/waveform/high-res");

        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return ApiResult<WaveformProfileDto?>.CreatePassResult(null);

        if (!response.IsSuccessStatusCode)
            return ApiResult<WaveformProfileDto?>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var profile = JsonSerializer.Deserialize<WaveformProfileDto>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return profile is not null
            ? ApiResult<WaveformProfileDto?>.CreatePassResult(profile)
            : ApiResult<WaveformProfileDto?>.CreateFailResult("Failed to deserialize response");
    }

    public async Task<ApiResult<TrackDto>> GetTrack(string entryKey)
    {
        var response = await _http.GetAsync($"api/track/meta/by-key/{Uri.EscapeDataString(entryKey)}");

        if (!response.IsSuccessStatusCode)
            return ApiResult<TrackDto>.CreateFailResult($"HTTP {(int)response.StatusCode}");

        var json = await response.Content.ReadAsStringAsync();
        var track = JsonSerializer.Deserialize<TrackDto>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        return track is not null
            ? ApiResult<TrackDto>.CreatePassResult(track)
            : ApiResult<TrackDto>.CreateFailResult("Failed to deserialize response");
    }
}