using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Postall.Domain;
using Postall.Domain.Entities;
using Postall.Infra.MongoDB.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Postall.Infra.MongoDB.Repositories
{
    public class UserSocialRepository : IUserSocialRepository
    {
        private readonly IMongoCollection<UserSocialData> _userSocialCollection;

        public UserSocialRepository(IOptions<MongoDbSettings> mongoDbSettings)
        {
            var client = new MongoClient(mongoDbSettings.Value.ConnectionString);
            var database = client.GetDatabase(mongoDbSettings.Value.DatabaseName);
            _userSocialCollection = database.GetCollection<UserSocialData>(mongoDbSettings.Value.UserSocialCollectionName);

            // Cria índice para userId (deve ser único)
            var indexKeysDefinition = Builders<UserSocialData>.IndexKeys
                .Ascending(u => u.UserId);
            _userSocialCollection.Indexes.CreateOne(new CreateIndexModel<UserSocialData>(indexKeysDefinition, new CreateIndexOptions { Unique = true }));
        }

        /// <summary>
        /// Obtém todos os dados sociais dos usuários
        /// </summary>
        public async Task<IEnumerable<UserSocialData>> GetAllAsync()
        {
            return await _userSocialCollection.Find(u => true).ToListAsync();
        }

        /// <summary>
        /// Obtém os dados sociais pelo ID do MongoDB
        /// </summary>
        public async Task<UserSocialData> GetByIdAsync(string id)
        {
            if (!ObjectId.TryParse(id, out _))
                return null;

            return await _userSocialCollection.Find(u => u.Id == ObjectId.Parse(id)).FirstOrDefaultAsync();
        }

        /// <summary>
        /// Obtém os dados sociais pelo ID do usuário
        /// </summary>
        public async Task<UserSocialData> GetByUserIdAsync(string userId)
        {
            return await _userSocialCollection.Find(u => u.UserId == userId).FirstOrDefaultAsync();
        }

        /// <summary>
        /// Obtém os dados sociais pelo token do Google
        /// </summary>
        public async Task<UserSocialData> GetByGoogleTokenAsync(string googleToken)
        {
            return await _userSocialCollection.Find(u => u.GoogleToken == googleToken).FirstOrDefaultAsync();
        }

        /// <summary>
        /// Adiciona novos dados sociais de usuário
        /// </summary>
        public async Task<UserSocialData> AddAsync(UserSocialData userSocialData)
        {
            var existingUserSocial = await GetByUserIdAsync(userSocialData.UserId);
            if (existingUserSocial != null)
                return existingUserSocial;

            if (userSocialData.Id == ObjectId.Empty)
            {
                userSocialData.Id = ObjectId.GenerateNewId();
            }

            try
            {
                await _userSocialCollection.InsertOneAsync(userSocialData);
            }
            catch (MongoWriteException ex)
            {
                if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
                {
                    return await GetByUserIdAsync(userSocialData.UserId);
                }
                else
                {
                    throw;
                }
            }
            return userSocialData;
        }

        /// <summary>
        /// Atualiza os dados sociais de um usuário existente
        /// </summary>
        public async Task<bool> UpdateAsync(UserSocialData userSocialData)
        {
            if (userSocialData.Id == ObjectId.Empty)
                return false;

            var result = await _userSocialCollection.ReplaceOneAsync(
                u => u.Id == userSocialData.Id,
                userSocialData,
                new ReplaceOptions { IsUpsert = false });

            return result.IsAcknowledged && result.ModifiedCount > 0;
        }

        /// <summary>
        /// Atualiza os dados sociais de um usuário existente
        /// </summary>
        public async Task<bool> UpdateOneAsync(UserSocialData userSocialData)
        {
            if (userSocialData.Id == ObjectId.Empty)
                return false;

            var result = await _userSocialCollection.ReplaceOneAsync(
                u => u.Id == userSocialData.Id,
                userSocialData,
                new ReplaceOptions { IsUpsert = false });

            return result.IsAcknowledged && result.ModifiedCount > 0;
        }

        /// <summary>
        /// Atualiza ou insere os dados sociais de um usuário
        /// </summary>
        public async Task<UserSocialData> UpsertAsync(UserSocialData userSocialData)
        {
            var existingData = await GetByUserIdAsync(userSocialData.UserId);

            if (existingData != null)
            {
                userSocialData.Id = existingData.Id;
                await _userSocialCollection.ReplaceOneAsync(
                    u => u.Id == existingData.Id,
                    userSocialData,
                    new ReplaceOptions { IsUpsert = true });
                return userSocialData;
            }
            else
            {
                return await AddAsync(userSocialData);
            }
        }

        /// <summary>
        /// Remove os dados sociais pelo ID do MongoDB
        /// </summary>
        public async Task<bool> DeleteAsync(string id)
        {
            if (!ObjectId.TryParse(id, out _))
                return false;

            var result = await _userSocialCollection.DeleteOneAsync(u => u.Id == ObjectId.Parse(id));
            return result.IsAcknowledged && result.DeletedCount > 0;
        }

        /// <summary>
        /// Remove os dados sociais pelo ID do usuário
        /// </summary>
        public async Task<bool> DeleteByUserIdAsync(string userId)
        {
            var result = await _userSocialCollection.DeleteOneAsync(u => u.UserId == userId);
            return result.IsAcknowledged && result.DeletedCount > 0;
        }

        /// <summary>
        /// Atualiza apenas o token do Google para um usuário
        /// </summary>
        public async Task<bool> UpdateGoogleTokenAsync(string userId, string googleToken)
        {
            var update = Builders<UserSocialData>.Update.Set(u => u.GoogleToken, googleToken);
            var result = await _userSocialCollection.UpdateOneAsync(
                u => u.UserId == userId,
                update,
                new UpdateOptions { IsUpsert = true });

            return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
        }

        /// <summary>
        /// Atualiza apenas o token do Facebook para um usuário
        /// </summary>
        public async Task<bool> UpdateFacebookTokenAsync(string userId, FacebookToken facebookToken)
        {
            var update = Builders<UserSocialData>.Update.Set(u => u.FacebookToken, facebookToken);
            var result = await _userSocialCollection.UpdateOneAsync(
                u => u.UserId == userId,
                update,
                new UpdateOptions { IsUpsert = true });

            return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
        }

        public async Task<bool> UpdateOneAsync(string userId, UpdateDefinition<UserSocialData> update)
        {
            var result = await _userSocialCollection.UpdateOneAsync(
                x => x.UserId == userId,
                update,
                new UpdateOptions { IsUpsert = true });

            return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
        }
    }
}