using Microsoft.Extensions.Options;
using System.Text.Json.Serialization;

namespace StockFin
{
    public sealed class TwelveDataClient : ITwelveDataClient
    {
        private readonly HttpClient _httpClient;
        private readonly TwelveDataOptions _options;

        public TwelveDataClient(
            HttpClient httpClient,
            IOptions<TwelveDataOptions> options)
        {
            _httpClient = httpClient;
            _options = options.Value;
        }

        public async Task<decimal?> GetPriceAsync(
            string symbol,
            CancellationToken ct = default)
        {
            var result = await GetAsync<TwelvePrice>(
                $"price?symbol={Uri.EscapeDataString(symbol)}",
                ct);

            if (result?.Price == null)
                return null;

            return decimal.Parse(
                result.Price,
                System.Globalization.CultureInfo.InvariantCulture);
        }

        public Task<TwelveQuote?> GetQuoteAsync(
            string symbol,
            CancellationToken ct = default)
        {
            return GetAsync<TwelveQuote>(
                $"quote?symbol={Uri.EscapeDataString(symbol)}",
                ct);
        }

        public Task<TwelveTimeSeries?> GetTimeSeriesAsync(
            string symbol,
            string interval = "1day",
            int outputSize = 5000,
            CancellationToken ct = default)
        {
            return GetAsync<TwelveTimeSeries>(
                $"time_series?symbol={Uri.EscapeDataString(symbol)}" +
                $"&interval={interval}" +
                $"&outputsize={outputSize}",
                ct);
        }

        public async Task<List<TwelveSearchResult>> SearchAsync(
            string query,
            CancellationToken ct = default)
        {
            var response = await GetAsync<SearchResponse>(
                $"symbol_search?symbol={Uri.EscapeDataString(query)}",
                ct);

            return response?.Data ?? [];
        }

        private async Task<T?> GetAsync<T>(
            string endpoint,
            CancellationToken ct)
        {
            var separator = endpoint.Contains('?') ? '&' : '?';

            var url =
                $"{endpoint}{separator}apikey={_options.ApiKey}";

            var response = await _httpClient.GetAsync(url, ct);

            var content = await response.Content.ReadAsStringAsync(ct);

            if (!response.IsSuccessStatusCode)
                throw new TwelveDataException(content);

            return await response.Content.ReadFromJsonAsync<T>(
                cancellationToken: ct);
        }

        private sealed class SearchResponse
        {
            public List<TwelveSearchResult> Data { get; set; } = [];
        }
    }
    public sealed class TwelveDataException : Exception
    {
        public TwelveDataException(string message)
            : base(message)
        {
        }
    }
    public sealed class TwelveTimeSeries
    {
        [JsonPropertyName("meta")]
        public TwelveMeta? Meta { get; set; }

        [JsonPropertyName("values")]
        public List<TwelveBar> Values { get; set; } = [];
    }

    public sealed class TwelveMeta
    {
        [JsonPropertyName("symbol")]
        public string? Symbol { get; set; }

        [JsonPropertyName("currency")]
        public string? Currency { get; set; }

        [JsonPropertyName("exchange")]
        public string? Exchange { get; set; }
    }

    public sealed class TwelveBar
    {
        [JsonPropertyName("datetime")]
        public string? DateTime { get; set; }

        [JsonPropertyName("open")]
        public string? Open { get; set; }

        [JsonPropertyName("high")]
        public string? High { get; set; }

        [JsonPropertyName("low")]
        public string? Low { get; set; }

        [JsonPropertyName("close")]
        public string? Close { get; set; }

        [JsonPropertyName("volume")]
        public string? Volume { get; set; }
    }
    public sealed class TwelveSearchResult
    {
        [JsonPropertyName("symbol")]
        public string? Symbol { get; set; }

        [JsonPropertyName("instrument_name")]
        public string? InstrumentName { get; set; }

        [JsonPropertyName("exchange")]
        public string? Exchange { get; set; }

        [JsonPropertyName("country")]
        public string? Country { get; set; }

        [JsonPropertyName("type")]
        public string? Type { get; set; }
    }
    public sealed class TwelveQuote
    {
        [JsonPropertyName("symbol")]
        public string? Symbol { get; set; }

        [JsonPropertyName("name")]
        public string? Name { get; set; }

        [JsonPropertyName("exchange")]
        public string? Exchange { get; set; }

        [JsonPropertyName("currency")]
        public string? Currency { get; set; }

        [JsonPropertyName("close")]
        public string? Close { get; set; }

        [JsonPropertyName("previous_close")]
        public string? PreviousClose { get; set; }

        [JsonPropertyName("change")]
        public string? Change { get; set; }

        [JsonPropertyName("percent_change")]
        public string? PercentChange { get; set; }

        [JsonPropertyName("is_market_open")]
        public bool IsMarketOpen { get; set; }
    }


    public sealed class TwelvePrice
    {
        [JsonPropertyName("price")]
        public string? Price { get; set; }
    }
    public interface ITwelveDataClient
    {
        Task<decimal?> GetPriceAsync(
            string symbol,
            CancellationToken ct = default);

        Task<TwelveQuote?> GetQuoteAsync(
            string symbol,
            CancellationToken ct = default);

        Task<TwelveTimeSeries?> GetTimeSeriesAsync(
            string symbol,
            string interval = "1day",
            int outputSize = 5000,
            CancellationToken ct = default);

        Task<List<TwelveSearchResult>> SearchAsync(
            string query,
            CancellationToken ct = default);
    }
    public sealed class TwelveDataOptions
    {
        public const string SectionName = "TwelveData";

        public string ApiKey { get; set; } = string.Empty;
    }
}
