using BookReader.Models;
using Polly;
using Polly.Retry;
using SQLite;

namespace BookReader.Services;

public class DatabaseService : IDatabaseService
{
    private SQLiteAsyncConnection? _database;
    private readonly string _dbPath;
    private readonly ResiliencePipeline _retryPipeline;

    public DatabaseService()
    {
        _dbPath = Path.Combine(FileSystem.AppDataDirectory, "bookreader.db3");
        
        // Polly 8.x retry pipeline: 3 попытки с экспоненциальной задержкой
        _retryPipeline = new ResiliencePipelineBuilder()
            .AddRetry(new RetryStrategyOptions
            {
                ShouldHandle = new PredicateBuilder().Handle<SQLiteException>()
                    .Handle<Exception>(ex => ex.Message.Contains("database is locked") || ex.Message.Contains("busy")),
                MaxRetryAttempts = 3,
                DelayGenerator = context => new ValueTask<TimeSpan?>(TimeSpan.FromMilliseconds(100 * Math.Pow(2, context.AttemptNumber))),
                OnRetry = context =>
                {
                    System.Diagnostics.Debug.WriteLine($"[Database] Retry {context.AttemptNumber + 1} after {context.RetryDelay.TotalMilliseconds}ms: {context.Outcome.Exception?.Message}");
                    return default;
                }
            })
            .Build();
    }

    public async Task InitializeAsync(CancellationToken ct = default)
    {
        if (_database != null) return;

        _database = new SQLiteAsyncConnection(_dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);

        await _retryPipeline.ExecuteAsync(async (token) =>
        {
            await _database!.CreateTableAsync<Book>();
            await _database!.CreateTableAsync<AppSettings>();
            await _database!.CreateTableAsync<ReadingProgress>();
        }, ct);
    }

    private async Task EnsureInitializedAsync()
    {
        if (_database == null)
            await InitializeAsync();
    }

    // Books
    public async Task<List<Book>> GetAllBooksAsync(CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.Table<Book>().OrderByDescending(b => b.LastRead).ToListAsync(), ct);
    }

    public async Task<Book?> GetBookByIdAsync(int id, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.Table<Book>().Where(b => b.Id == id).FirstOrDefaultAsync(), ct);
    }

    public async Task<int> SaveBookAsync(Book book, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
        {
            if (book.Id != 0)
                return await _database!.UpdateAsync(book);
            return await _database!.InsertAsync(book);
        }, ct);
    }

    public async Task<int> UpdateBookAsync(Book book, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.UpdateAsync(book), ct);
    }

    public async Task<int> DeleteBookAsync(Book book, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();

        // Delete associated file
        if (File.Exists(book.FilePath))
        {
            try { File.Delete(book.FilePath); } catch { }
        }

        // Delete progress records
        await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.Table<ReadingProgress>().DeleteAsync(p => p.BookId == book.Id), ct);

        return await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.DeleteAsync(book), ct);
    }

    // Settings
    public async Task<string?> GetSettingAsync(string key, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
        {
            var setting = await _database!.Table<AppSettings>().Where(s => s.Key == key).FirstOrDefaultAsync();
            return setting?.Value;
        }, ct);
    }

    public async Task SetSettingAsync(string key, string value, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        await _retryPipeline.ExecuteAsync(async (token) =>
        {
            var existing = await _database!.Table<AppSettings>().Where(s => s.Key == key).FirstOrDefaultAsync();
            if (existing != null)
            {
                existing.Value = value;
                await _database.UpdateAsync(existing);
            }
            else
            {
                await _database.InsertAsync(new AppSettings { Key = key, Value = value });
            }
        }, ct);
    }

    public async Task<Dictionary<string, string>> GetAllSettingsAsync(CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
        {
            var settings = await _database!.Table<AppSettings>().ToListAsync();
            return settings.ToDictionary(s => s.Key, s => s.Value);
        }, ct);
    }

    // Reading Progress
    public async Task SaveProgressAsync(ReadingProgress progress, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        await _retryPipeline.ExecuteAsync(async (token) =>
        {
            progress.Timestamp = DateTime.UtcNow;
            await _database!.InsertAsync(progress);
        }, ct);
    }

    public async Task<ReadingProgress?> GetLatestProgressAsync(int bookId, CancellationToken ct = default)
    {
        await EnsureInitializedAsync();
        return await _retryPipeline.ExecuteAsync(async (token) =>
            await _database!.Table<ReadingProgress>()
                .Where(p => p.BookId == bookId)
                .OrderByDescending(p => p.Timestamp)
                .FirstOrDefaultAsync(), ct);
    }
}