using System;
using System.Globalization;
using System.Threading.Tasks;
using Dapper;
using QuantEngine.Core.Interfaces;
using QuantEngine.Infrastructure.Data;

namespace QuantEngine.Infrastructure.Services
{
    public class PostgresTokenCache : ITokenCache
    {
        private readonly IDbConnectionFactory _connectionFactory;
        private static readonly int TokenRefreshSkewMinutes = 10;

        public PostgresTokenCache(IDbConnectionFactory connectionFactory)
        {
            _connectionFactory = connectionFactory;
        }

        public async Task<string?> GetCachedTokenAsync(string account)
        {
            await EnsureTableAsync();
            using var conn = _connectionFactory.CreateConnection();

            var token = await conn.QueryFirstOrDefaultAsync<dynamic>(@"
                SELECT access_token as AccessToken, expires_at as ExpiresAt
                FROM quantengine.kis_tokens
                WHERE account = @Account",
                new { Account = account }
            );

            if (token == null)
                return null;

            DateTime expiresAt;
            if (!DateTime.TryParse((string)token.ExpiresAt, CultureInfo.InvariantCulture,
                    DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
                    out expiresAt))
                return null;
            var now = DateTime.UtcNow;
            var refreshSkew = TimeSpan.FromMinutes(TokenRefreshSkewMinutes);

            // Return token only if it expires more than refresh skew from now
            if (expiresAt > now.Add(refreshSkew))
                return token.AccessToken;

            return null;
        }

        public async Task SaveTokenAsync(string account, string token, DateTime expiresAt)
        {
            await EnsureTableAsync();
            using var conn = _connectionFactory.CreateConnection();

            await conn.ExecuteAsync(@"
                INSERT INTO quantengine.kis_tokens (account, access_token, expires_at, updated_at)
                VALUES (@Account, @Token, @ExpiresAt, @UpdatedAt)
                ON CONFLICT (account) DO UPDATE SET
                    access_token = EXCLUDED.access_token,
                    expires_at = EXCLUDED.expires_at,
                    updated_at = EXCLUDED.updated_at",
                new
                {
                    Account = account,
                    Token = token,
                    ExpiresAt = expiresAt.ToString("o"),
                    UpdatedAt = DateTime.UtcNow.ToString("o")
                }
            );
        }

        public async Task ClearExpiredTokensAsync()
        {
            using var conn = _connectionFactory.CreateConnection();

            await conn.ExecuteAsync(@"
                DELETE FROM quantengine.kis_tokens
                WHERE expires_at < @Now",
                new { Now = DateTime.UtcNow.ToString("o") }
            );
        }

        private async Task EnsureTableAsync()
        {
            using var conn = _connectionFactory.CreateConnection();
            await conn.ExecuteAsync(@"
                CREATE TABLE IF NOT EXISTS quantengine.kis_tokens (
                    account TEXT PRIMARY KEY,
                    access_token TEXT NOT NULL,
                    expires_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL
                );

                CREATE INDEX IF NOT EXISTS idx_kis_tokens_expires_at ON quantengine.kis_tokens(expires_at);
            ");
        }
    }
}
