using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using YouTubeMusicAPI.Client;
using System.Text.Json;
using Acornima.Ast;
using AngleSharp.Text;
using System.Text.RegularExpressions;
using System.Text.Json.Serialization;

public class NavidromeValidator : SongValidatorBase
{
    private YouTubeMusicClient youtubeClient;

    public NavidromeValidator(ILogger logger, SpotifyApiClient spotifyApiClient) : base(logger, spotifyApiClient)
    {
        youtubeClient = new("AT");
    }

    public override async Task<bool> CanValidateUriAsync(Uri songUri)
    {
        // Check if behind this URL there is a public navidrome share
        var config = Configuration.Default.WithDefaultLoader();
        var address = songUri.ToString();
        var context = BrowsingContext.New(config);
        var document = await context.OpenAsync(address);
        var titleCell = (document.DocumentElement.GetDescendants().First() as HtmlElement)
                                                .GetElementsByTagName("title").First();
        return "Navidrome".Equals(titleCell.TextContent);
    }

    public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
    {
        return await this.CanValidateUriAsync(songUri);
    }

    public override SongProvider GetSongProvider()
    {
        return SongProvider.NavidromeSharedLink;
    }

    private static Stream GenerateStreamFromString(string s)
    {
        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);
        writer.Write(s);
        writer.Flush();
        stream.Position = 0;
        return stream;
    }

    public override async Task<Song> ValidateAsync(Uri songUri)
    {
        var config = Configuration.Default.WithDefaultLoader();
        var address = songUri.ToString();
        var context = BrowsingContext.New(config);
        var document = await context.OpenAsync(address);
        var infoScriptNode = document.GetElementsByTagName("script").Where(e => e.TextContent.Contains("__SHARE_INFO__")).First();
        var manipulatedValue = infoScriptNode.TextContent.Replace("window.__SHARE_INFO__ = \"", "").StripLeadingTrailingSpaces().StripLineBreaks();
        manipulatedValue = manipulatedValue.Remove(manipulatedValue.Length - 1);
        var infoScriptJsonData = Regex.Unescape(Regex.Unescape(manipulatedValue));

        var title = string.Empty;
        var artist = string.Empty;

        using (var stream = GenerateStreamFromString(infoScriptJsonData))
        {
            var jsonContent = await JsonSerializer.DeserializeAsync<NavidromeShareInfoData>(stream);
            title = jsonContent.Tracks[0].Title;
            artist = jsonContent.Tracks[0].Artist;
        }

        var song = new Song
        {
            Name = title,
            Artist = artist,
            Url = songUri.ToString(),
            Provider = SongProvider.NavidromeSharedLink,
            SpotifyId = await this.LookupSpotifyIdAsync(title, artist)
        };

        return song;
    }
}

public class NavidromeShareInfoData
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("description")]
    public required string Description { get; set; }

    [JsonPropertyName("downloadable")]
    public bool Downloadable { get; set; }

    [JsonPropertyName("tracks")]
    public required List<NavidromeTrackInfoData> Tracks { get; set; }
}

public class NavidromeTrackInfoData
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("title")]
    public required string Title { get; set; }

    [JsonPropertyName("artist")]
    public required string Artist { get; set; }

    [JsonPropertyName("album")]
    public required string Album { get; set; }

    [JsonPropertyName("updatedAt")]
    public DateTime UpdatedAt { get; set; }

    [JsonPropertyName("duration")]
    public float Duration { get; set; }
}