namespace TaxBaik.Application.Tests;

using FluentValidation;
using TaxBaik.Application.DTOs;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using Xunit;

public class InquiryServiceTests
{
    [Fact]
    public async Task UpdateStatusAsync_WhenStatusIsInvalid_ThrowsValidationException()
    {
        var service = new InquiryService(new FakeInquiryRepository(), new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions()), new PassThroughValidator<SubmitInquiryDto>(), new PassThroughValidator<UpdateInquiryDto>());

        await Assert.ThrowsAsync<TaxBaik.Application.Services.ValidationException>(() => service.UpdateStatusAsync(1, "invalid"));
    }

    [Fact]
    public async Task SubmitAsync_StoresEmailAndNewStatus()
    {
        var repository = new FakeInquiryRepository();
        var service = new InquiryService(repository, new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions()), new PassThroughValidator<SubmitInquiryDto>(), new PassThroughValidator<UpdateInquiryDto>());

        await service.SubmitAsync("홍길동", "010-1234-5678", "기장", "사업자 세무 관련해서 문의드립니다.", "user@example.com");

        Assert.Equal("user@example.com", repository.Inquiries.Single().Email);
        Assert.Equal("new", repository.Inquiries.Single().Status);
    }

    private sealed class FakeInquiryRepository : IInquiryRepository
    {
        public List<Inquiry> Inquiries { get; } = [];

        public Task<int> CreateAsync(Inquiry inquiry, CancellationToken cancellationToken = default)
        {
            inquiry.Id = Inquiries.Count + 1;
            Inquiries.Add(inquiry);
            return Task.FromResult(inquiry.Id);
        }

        public Task<Inquiry?> GetByIdAsync(int id, CancellationToken cancellationToken = default) =>
            Task.FromResult(Inquiries.FirstOrDefault(x => x.Id == id));

        public Task<(IEnumerable<Inquiry> Items, int Total)> GetPagedAsync(
            int page, int pageSize, string? status = null, CancellationToken cancellationToken = default)
        {
            var items = status == null ? Inquiries : Inquiries.Where(x => x.Status == status).ToList();
            return Task.FromResult<(IEnumerable<Inquiry>, int)>((items, items.Count()));
        }

        public Task<int> CountAsync(CancellationToken cancellationToken = default)
            => Task.FromResult(Inquiries.Count);

        public Task<int> CountThisMonthAsync(CancellationToken cancellationToken = default)
            => Task.FromResult(Inquiries.Count);

        public Task<int> CountByStatusAsync(string status, CancellationToken cancellationToken = default)
            => Task.FromResult(Inquiries.Count(x => x.Status == status));

        public Task<int> CountByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
            => Task.FromResult(Inquiries.Count(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate));

        public Task<int> CountByStatusAndDateAsync(string status, DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
            => Task.FromResult(Inquiries.Count(x => x.Status == status && x.CreatedAt >= startDate && x.CreatedAt <= endDate));

        public Task UpdateStatusAsync(int id, string status, CancellationToken cancellationToken = default)
        {
            var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
            if (inquiry != null)
                inquiry.Status = status;
            return Task.CompletedTask;
        }

        public Task UpdateAdminMemoAsync(int id, string? adminMemo, CancellationToken cancellationToken = default)
        {
            var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
            if (inquiry != null)
                inquiry.AdminMemo = adminMemo;
            return Task.CompletedTask;
        }

        public Task UpdateAsync(Inquiry inquiry, CancellationToken cancellationToken = default)
        {
            var existing = Inquiries.FirstOrDefault(x => x.Id == inquiry.Id);
            if (existing != null)
            {
                existing.Name = inquiry.Name;
                existing.Phone = inquiry.Phone;
                existing.Email = inquiry.Email;
                existing.ServiceType = inquiry.ServiceType;
                existing.Message = inquiry.Message;
                existing.Status = inquiry.Status;
                existing.AdminMemo = inquiry.AdminMemo;
            }
            return Task.CompletedTask;
        }

        public Task LinkClientAsync(int inquiryId, int clientId, CancellationToken cancellationToken = default)
        {
            var inquiry = Inquiries.FirstOrDefault(x => x.Id == inquiryId);
            if (inquiry != null)
                inquiry.ClientId = clientId;
            return Task.CompletedTask;
        }

        public Task DeleteAsync(int id, CancellationToken cancellationToken = default)
        {
            var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
            if (inquiry != null)
                Inquiries.Remove(inquiry);
            return Task.CompletedTask;
        }
    }

    private sealed class FakeInquiryNotificationService : IInquiryNotificationService
    {
        public Task NotifyCreatedAsync(int inquiryId, string name, string phone, string serviceType, string message, string? ipAddress, DateTime createdAtUtc, CancellationToken ct = default)
            => Task.CompletedTask;

        public Task NotifyStatusChangedAsync(int inquiryId, string name, string phone, string serviceType, string previousStatus, string newStatus, string? changedBy = null, CancellationToken ct = default)
            => Task.CompletedTask;
    }

    private sealed class PassThroughValidator<T> : IValidator<T>
    {
        public FluentValidation.Results.ValidationResult Validate(T instance) => new();
        public Task<FluentValidation.Results.ValidationResult> ValidateAsync(T instance, CancellationToken cancellation = default) => Task.FromResult(new FluentValidation.Results.ValidationResult());
        public FluentValidation.Results.ValidationResult Validate(IValidationContext context) => new();
        public Task<FluentValidation.Results.ValidationResult> ValidateAsync(IValidationContext context, CancellationToken cancellation = default) => Task.FromResult(new FluentValidation.Results.ValidationResult());
        public IValidatorDescriptor CreateDescriptor() => throw new NotImplementedException();
        public bool CanValidateInstancesOfType(Type type) => true;
    }
}
