using PlaylistShared.Data.Contexts;
using PlaylistShared.Data.Entities;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace PlaylistShared.Services;

public class YandexMusicService : IYandexMusicService
{
    private readonly AppDbContext _db;
    private readonly HttpClient _http;
    private readonly IConfiguration _config;
    private readonly ILogger<YandexMusicService> _logger;

    public YandexMusicService(AppDbContext db, IHttpClientFactory factory, IConfiguration config, ILogger<YandexMusicService> logger)
    {
        _db = db;
        _http = factory.CreateClient();
        _config = config;
        _logger = logger;
    }

    private async Task<ApplicationUser> GetUserWithValidTokenAsync(string userId)
    {
        var user = await _db.Users.FindAsync(userId);
        if (user == null) throw new Exception("User not found");

        if (user.AccessTokenExpiresAt <= DateTime.UtcNow.AddMinutes(5))
        {
            var newToken = await RefreshYandexTokenAsync(user.RefreshToken!);
            user.AccessToken = newToken.access_token;
            user.RefreshToken = newToken.refresh_token;
            user.AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(newToken.expires_in);
            await _db.SaveChangesAsync();
        }
        return user;
    }

    private async Task<(string access_token, string refresh_token, int expires_in)> RefreshYandexTokenAsync(string refreshToken)
    {
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "refresh_token"),
            new KeyValuePair<string, string>("refresh_token", refreshToken),
            new KeyValuePair<string, string>("client_id", _config["YandexOAuth:ClientId"]),
            new KeyValuePair<string, string>("client_secret", _config["YandexOAuth:ClientSecret"])
        });
        var response = await _http.PostAsync(_config["YandexOAuth:TokenEndpoint"], content);
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        return (
            doc.RootElement.GetProperty("access_token").GetString()!,
            doc.RootElement.GetProperty("refresh_token").GetString()!,
            doc.RootElement.GetProperty("expires_in").GetInt32()
        );
    }

    public async Task<string> CreatePlaylistAsync(string userId, string title)
    {
        var user = await GetUserWithValidTokenAsync(userId);
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
        var payload = new { title, visibility = "public" };
        var json = JsonSerializer.Serialize(payload);
        var response = await _http.PostAsync("https://api.music.yandex.net/users/self/playlists/create",
            new StringContent(json, Encoding.UTF8, "application/json"));
        response.EnsureSuccessStatusCode();
        var resultJson = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistData>>(resultJson);
        return result!.Result.Kind;
    }

    public async Task AddTrackToPlaylistAsync(string userId, string yandexPlaylistId, string trackId)
    {
        var user = await GetUserWithValidTokenAsync(userId);
        var uid = user.YandexId; // или user.Id? В API Яндекса используется uid из аккаунта, но можно использовать "self"
        // Получаем альбом трека (опционально, можно передать без albumId)
        // Для простоты используем трек без albumId
        var diff = new[]
        {
            new
            {
                op = "insert",
                at = 0,
                tracks = new[] { new { id = trackId } }
            }
        };
        var json = JsonSerializer.Serialize(diff);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}/change";
        var response = await _http.PostAsync(url, content);
        response.EnsureSuccessStatusCode();
    }

    public async Task RemoveTrackFromPlaylistAsync(string userId, string yandexPlaylistId, string trackId)
    {
        var user = await GetUserWithValidTokenAsync(userId);
        var uid = user.YandexId;
        // Сначала нужно получить позицию трека в плейлисте – упростим: удаляем по индексу (не надёжно)
        // Лучше получить плейлист, найти индекс трека, потом удалить
        var playlist = await GetPlaylistInfoAsync(userId, yandexPlaylistId);
        var tracks = await GetPlaylistTracksAsync(userId, yandexPlaylistId);
        var index = tracks.FindIndex(t => t.Id == trackId);
        if (index == -1) throw new Exception("Track not found in playlist");
        var diff = new[]
        {
            new
            {
                op = "delete",
                from = index,
                to = index + 1
            }
        };
        var json = JsonSerializer.Serialize(diff);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}/change";
        var response = await _http.PostAsync(url, content);
        response.EnsureSuccessStatusCode();
    }

    public async Task<List<YandexTrackInfo>> GetPlaylistTracksAsync(string userId, string yandexPlaylistId)
    {
        var user = await GetUserWithValidTokenAsync(userId);
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
        var uid = user.YandexId;
        var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}";
        var response = await _http.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        var data = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistFull>>(json);
        return data!.Result.Tracks?.Select(t => new YandexTrackInfo(
            t.Id.ToString(),
            t.Title,
            t.Artists?.FirstOrDefault()?.Name,
            t.Albums?.FirstOrDefault()?.Title,
            t.DurationMs
        )).ToList() ?? new();
    }

    public async Task<YandexPlaylistInfo> GetPlaylistInfoAsync(string userId, string yandexPlaylistId)
    {
        var user = await GetUserWithValidTokenAsync(userId);
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
        var uid = user.YandexId;
        var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}";
        var response = await _http.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        var data = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistFull>>(json);
        return new YandexPlaylistInfo(data!.Result.Kind, data.Result.Title, data.Result.TrackCount);
    }

    public async Task<string?> RefreshUserTokenAsync(string userId)
    {
        var user = await _db.Users.FindAsync(userId);
        if (user?.RefreshToken == null) return null;
        var newToken = await RefreshYandexTokenAsync(user.RefreshToken);
        user.AccessToken = newToken.access_token;
        user.RefreshToken = newToken.refresh_token;
        user.AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(newToken.expires_in);
        await _db.SaveChangesAsync();
        return newToken.access_token;
    }

    // Вспомогательные record
    private record YandexApiResponse<T>(T Result);
    private record YandexPlaylistData(string Kind);
    private record YandexPlaylistFull(string Kind, string Title, int TrackCount, List<YandexTrack> Tracks);
    private record YandexTrack(string Id, string Title, int DurationMs, List<YandexArtist> Artists, List<YandexAlbum> Albums);
    private record YandexArtist(string Name);
    private record YandexAlbum(string Title);
}