using MailApi.Models;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;

namespace MailApi.Services;

public class MailStore
{
    private readonly IMongoCollection<MailMessage> _messages;
    private readonly IMongoCollection<CharacterDocument> _characters;

    public MailStore(IConfiguration cfg)
    {
        var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
        var dbName = cfg["MongoDB:DatabaseName"] ?? "promiscuity";
        var client = new MongoClient(cs);
        var db = client.GetDatabase(dbName);
        _messages = db.GetCollection<MailMessage>("MailMessages");
        _characters = db.GetCollection<CharacterDocument>("Characters");

        _messages.Indexes.CreateOne(new CreateIndexModel<MailMessage>(
            Builders<MailMessage>.IndexKeys.Ascending(m => m.RecipientCharacterId).Descending(m => m.CreatedUtc)));
        _messages.Indexes.CreateOne(new CreateIndexModel<MailMessage>(
            Builders<MailMessage>.IndexKeys.Ascending(m => m.SenderCharacterId).Descending(m => m.CreatedUtc)));
    }

    public async Task<CharacterAccessResult> ResolveCharacterAsync(string characterId, string userId, bool allowAnyOwner)
    {
        var character = await _characters.Find(c => c.Id == characterId).FirstOrDefaultAsync();
        if (character is null)
            return new CharacterAccessResult { Exists = false };

        return new CharacterAccessResult
        {
            Exists = true,
            IsAuthorized = allowAnyOwner || character.OwnerUserId == userId,
            CharacterId = character.Id ?? string.Empty,
            CharacterName = character.Name,
            OwnerUserId = character.OwnerUserId
        };
    }

    public async Task<List<MailMessage>> GetInboxAsync(string characterId) =>
        await _messages.Find(m => m.RecipientCharacterId == characterId)
            .SortByDescending(m => m.CreatedUtc)
            .ToListAsync();

    public async Task<List<MailMessage>> GetSentAsync(string characterId) =>
        await _messages.Find(m => m.SenderCharacterId == characterId)
            .SortByDescending(m => m.CreatedUtc)
            .ToListAsync();

    public async Task<SendMailResult> SendAsync(string senderCharacterId, string recipientCharacterName, string subject, string body)
    {
        var sender = await _characters.Find(c => c.Id == senderCharacterId).FirstOrDefaultAsync();
        if (sender is null)
            return new SendMailResult { Status = SendMailStatus.SenderNotFound };

        var normalizedRecipientName = recipientCharacterName.Trim();
        var recipients = await _characters.Find(c => c.Name == normalizedRecipientName).ToListAsync();
        if (recipients.Count == 0)
            return new SendMailResult { Status = SendMailStatus.RecipientNotFound };
        if (recipients.Count > 1)
            return new SendMailResult { Status = SendMailStatus.RecipientAmbiguous };

        var recipient = recipients[0];
        if (recipient.Id == sender.Id)
            return new SendMailResult { Status = SendMailStatus.Invalid };

        var message = new MailMessage
        {
            SenderCharacterId = sender.Id ?? string.Empty,
            SenderCharacterName = sender.Name,
            RecipientCharacterId = recipient.Id ?? string.Empty,
            RecipientCharacterName = recipient.Name,
            Subject = subject.Trim(),
            Body = body.Trim(),
            CreatedUtc = DateTime.UtcNow
        };

        await _messages.InsertOneAsync(message);
        return new SendMailResult { Status = SendMailStatus.Ok, Message = message };
    }

    public async Task<MailMessage?> MarkReadAsync(string characterId, string messageId)
    {
        var filter = Builders<MailMessage>.Filter.And(
            Builders<MailMessage>.Filter.Eq(m => m.Id, messageId),
            Builders<MailMessage>.Filter.Eq(m => m.RecipientCharacterId, characterId)
        );
        var update = Builders<MailMessage>.Update.Set(m => m.ReadUtc, DateTime.UtcNow);
        var options = new FindOneAndUpdateOptions<MailMessage> { ReturnDocument = ReturnDocument.After };
        return await _messages.FindOneAndUpdateAsync(filter, update, options);
    }

    public class CharacterAccessResult
    {
        public bool Exists { get; set; }

        public bool IsAuthorized { get; set; }

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

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

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

    public class SendMailResult
    {
        public SendMailStatus Status { get; set; }

        public MailMessage? Message { get; set; }
    }

    public enum SendMailStatus
    {
        Ok,
        SenderNotFound,
        RecipientNotFound,
        RecipientAmbiguous,
        Invalid
    }

    [BsonIgnoreExtraElements]
    private class CharacterDocument
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string? Id { get; set; }

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

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