using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.AI;
using Moq;
using FluentResults;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.DTOs.AI;
using NexusReader.Application.DTOs.User;
using NexusReader.Application.Queries.Library;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Entities;
using Xunit;
using Polly;
using Polly.Registry;
using MapsterMapper;
using Pgvector;

namespace NexusReader.Application.Tests.Queries;

public class QueryTests : IDisposable
{
    private readonly SqliteConnection _connection;
    private readonly DbContextOptions<AppDbContext> _contextOptions;
    private readonly Mock<IDbContextFactory<AppDbContext>> _dbContextFactoryMock;
    private readonly Mock<IEmbeddingGenerator<string, Embedding<float>>> _embeddingGeneratorMock;
    private readonly Mock<ResiliencePipelineProvider<string>> _pipelineProviderMock;
    private readonly Mock<IMapper> _mapperMock;

    public QueryTests()
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        _contextOptions = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlite(_connection)
            .Options;

        // Seed initial database schema
        using var context = new AppDbContext(_contextOptions);
        context.Database.EnsureCreated();

        _dbContextFactoryMock = new Mock<IDbContextFactory<AppDbContext>>();
        _dbContextFactoryMock.Setup(f => f.CreateDbContextAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => new AppDbContext(_contextOptions));
        _dbContextFactoryMock.Setup(f => f.CreateDbContext())
            .Returns(() => new AppDbContext(_contextOptions));

        _embeddingGeneratorMock = new Mock<IEmbeddingGenerator<string, Embedding<float>>>();

        _pipelineProviderMock = new Mock<ResiliencePipelineProvider<string>>();
        _pipelineProviderMock.Setup(p => p.GetPipeline("ai-retry"))
            .Returns(ResiliencePipeline.Empty);

        _mapperMock = new Mock<IMapper>();
    }

    [Fact]
    public async Task GetMyEbooksQuery_WithPopulatedDescription_ReturnsCorrectDescription()
    {
        // Arrange
        using (var context = new AppDbContext(_contextOptions))
        {
            var user = new NexusUser
            {
                Id = "user-123",
                UserName = "testuser",
                Email = "test@example.com",
                TenantId = "tenant-123",
                SubscriptionPlanId = 1
            };
            context.Users.Add(user);

            var author = new Author { Id = 1, Name = "Adam Mickiewicz" };
            context.Authors.Add(author);

            var ebook = new Ebook
            {
                Id = Guid.NewGuid(),
                UserId = "user-123",
                Title = "Pan Tadeusz",
                AuthorId = author.Id,
                Description = "A Polish epic poem written by Adam Mickiewicz.",
                CoverUrl = "cover.png",
                Progress = 42.5,
                LastChapter = "Księga I",
                LastChapterIndex = 1,
                AddedDate = DateTime.UtcNow,
                LastReadDate = DateTime.UtcNow,
                FilePath = "dummy.epub"
            };
            context.Ebooks.Add(ebook);
            await context.SaveChangesAsync();
        }

        var handler = new GetMyEbooksQueryHandler(_dbContextFactoryMock.Object);
        var query = new GetMyEbooksQuery("user-123");

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeTrue();
        result.Value.Should().HaveCount(1);
        result.Value.First().Title.Should().Be("Pan Tadeusz");
        result.Value.First().Description.Should().Be("A Polish epic poem written by Adam Mickiewicz.");
        result.Value.First().Progress.Should().Be(42.5);
    }

    [Fact]
    public async Task SearchLibrarySemanticallyQuery_WithEmptyQueryText_ReturnsFailure()
    {
        // Arrange
        var knowledgeServiceMock = new Mock<IKnowledgeService>();
        var handler = new SearchLibrarySemanticallyQueryHandler(knowledgeServiceMock.Object);
        var query = new SearchLibrarySemanticallyQuery("", "tenant-123");

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeFalse();
        result.Errors.First().Message.Should().Be("Query text cannot be empty.");
    }

    [Fact]
    public async Task SearchLibrarySemanticallyQuery_WithValidQuery_CallsKnowledgeService()
    {
        // Arrange
        var queryText = "test query";
        var tenantId = "tenant-123";
        var expectedResponse = new List<SemanticSearchResultDto>
        {
            new SemanticSearchResultDto
            {
                Snippet = "Matched content",
                RelevanceScore = 0.95f,
                SourceBookTitle = "Test Book"
            }
        };

        var knowledgeServiceMock = new Mock<IKnowledgeService>();
        knowledgeServiceMock.Setup(s => s.SearchLibrarySemanticallyAsync(queryText, tenantId, 5, It.IsAny<CancellationToken>()))
            .ReturnsAsync(Result.Ok(expectedResponse));

        var handler = new SearchLibrarySemanticallyQueryHandler(knowledgeServiceMock.Object);
        var query = new SearchLibrarySemanticallyQuery(queryText, tenantId);

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeTrue();
        result.Value.Should().HaveCount(1);
        result.Value.First().Snippet.Should().Be("Matched content");
        result.Value.First().SourceBookTitle.Should().Be("Test Book");

        knowledgeServiceMock.Verify(s => s.SearchLibrarySemanticallyAsync(queryText, tenantId, 5, It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task AskLibraryQuestionQuery_WithEmptyQuestion_ReturnsFailure()
    {
        // Arrange
        var knowledgeServiceMock = new Mock<IKnowledgeService>();
        var handler = new AskLibraryQuestionQueryHandler(knowledgeServiceMock.Object);
        var query = new AskLibraryQuestionQuery("", "tenant-123");

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeFalse();
        result.Errors.First().Message.Should().Be("Question cannot be empty.");
    }

    [Fact]
    public async Task AskLibraryQuestionQuery_WithValidQuestion_CallsKnowledgeService()
    {
        // Arrange
        var knowledgeServiceMock = new Mock<IKnowledgeService>();
        var expectedResponse = new GroundedResponseDto
        {
            Answer = "Based on the book, water boils at 100 degrees Celsius.",
            Citations = new List<CitationDto>
            {
                new CitationDto
                {
                    CitationId = "chunk-1",
                    Snippet = "Water boils at 100 degrees Celsius.",
                    SourceBook = "Physics 101"
                }
            }
        };

        knowledgeServiceMock.Setup(s => s.AskQuestionAsync("what temp does water boil?", "tenant-123", null, 5, It.IsAny<CancellationToken>()))
            .ReturnsAsync(Result.Ok(expectedResponse));

        var handler = new AskLibraryQuestionQueryHandler(knowledgeServiceMock.Object);
        var query = new AskLibraryQuestionQuery("what temp does water boil?", "tenant-123");

        // Act
        var result = await handler.Handle(query, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeTrue();
        result.Value.Answer.Should().Be("Based on the book, water boils at 100 degrees Celsius.");
        result.Value.Citations.Should().HaveCount(1);
        result.Value.Citations.First().CitationId.Should().Be("chunk-1");
    }

    public void Dispose()
    {
        _connection.Close();
        _connection.Dispose();
    }
}
