using BaseDomain.Results;
using Microsoft.AspNetCore.Http;
using Postall.Domain;
using Postall.Domain.Contracts.Repositories;
using Postall.Domain.Dtos;
using Postall.Domain.Entities;
using Postall.Domain.Services.Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Postall.Infra.Services
{
    public class VideoService : IVideoService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IVideoRepository _videoRepository;
        private readonly IChannelRepository _channelRepository;
        private readonly IVideoYoutubeService _videoYoutubeService;

        public VideoService(
            IHttpContextAccessor httpContextAccessor,
            IVideoRepository videoRepository,
            IChannelRepository channelRepository,
            IVideoYoutubeService videoYoutubeService)
        {
            _httpContextAccessor = httpContextAccessor;
            _videoRepository = videoRepository;
            _channelRepository = channelRepository;
            _videoYoutubeService = videoYoutubeService;
        }

        /// <summary>
        /// Obtém o ID do usuário atual a partir das claims
        /// </summary>
        private string GetCurrentUserId()
        {
            var user = _httpContextAccessor.HttpContext.User;
            var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if (string.IsNullOrEmpty(userId))
            {
                var email = user.FindFirst(ClaimTypes.Email)?.Value;
                if (!string.IsNullOrEmpty(email))
                {
                    userId = Convert.ToBase64String(
                        System.Text.Encoding.UTF8.GetBytes(email)
                    ).Replace("/", "_").Replace("+", "-").Replace("=", "");
                }
            }

            return userId;
        }

        /// <summary>
        /// Obtém todos os vídeos do usuário atual
        /// </summary>
        public async Task<Result<List<VideoResponse>>> GetUserVideosAsync()
        {
            var userId = GetCurrentUserId();

            if (string.IsNullOrEmpty(userId))
                return new List<VideoResponse>();

            var videos = await _videoRepository.GetByUserIdAsync(userId);
            return videos.Select(v => v.ToVideoResponse()).ToList();
        }

        /// <summary>
        /// Obtém os vídeos de um canal específico para o usuário atual
        /// </summary>
        public async Task<Result<List<VideoResponse>>> GetVideosByChannelIdAsync(string channelId)
        {
            if (string.IsNullOrEmpty(channelId))
                return new List<VideoResponse>();

            var userId = GetCurrentUserId();

            if (string.IsNullOrEmpty(userId))
                return new List<VideoResponse>();

            var videos = await _videoRepository.GetByUserIdAndChannelIdAsync(userId, channelId);
            return videos.Select(v => v.ToVideoResponse()).ToList();
        }

        /// <summary>
        /// Obtém os detalhes de um vídeo específico
        /// </summary>
        public async Task<Result<VideoResponse>> GetVideoDetailsAsync(string videoId)
        {
            if (string.IsNullOrEmpty(videoId))
                return Result<VideoResponse>.Failure("ID do vídeo inválido");

            var videoDetails = await _videoYoutubeService.GetVideoDetailsAsync(videoId);
            return videoDetails;
        }

        /// <summary>
        /// Obtém os vídeos de um canal diretamente do YouTube
        /// </summary>
        public async Task<Result<List<VideoResponse>>> GetChannelVideosFromYouTubeAsync(string channelId, int maxResults = 10)
        {
            if (string.IsNullOrEmpty(channelId))
                return Result<List<VideoResponse>>.Failure("ID do canal inválido");

            // Verifica se o canal existe para o usuário
            var userId = GetCurrentUserId();
            var channel = await _channelRepository.GetByUserIdAndChannelIdAsync(userId, channelId);

            if (channel == null)
                return Result<List<VideoResponse>>.Failure("Canal não encontrado para este usuário");

            // Obtém os vídeos que o usuário já adicionou para este canal
            var userVideos = await _videoRepository.GetByUserIdAndChannelIdAsync(userId, channelId);
            var userVideoIds = userVideos.Select(v => v.VideoId).ToHashSet();

            // Busca os vídeos do canal no YouTube
            var channelVideos = await _videoYoutubeService.GetChannelVideosAsync(channelId, maxResults);

            if (!channelVideos.IsSuccess)
                return channelVideos;

            // Marca os vídeos que já foram adicionados pelo usuário
            foreach (var video in channelVideos.Value)
            {
                if (userVideoIds.Contains(video.VideoId))
                {
                    // Esse vídeo já foi adicionado pelo usuário
                    video.Id = userVideos.First(v => v.VideoId == video.VideoId).Id;
                }
            }

            return channelVideos;
        }

        /// <summary>
        /// Adiciona um vídeo à lista do usuário
        /// </summary>
        public async Task<Result<bool>> AddVideoAsync(string videoId, string channelId)
        {
            if (string.IsNullOrEmpty(videoId))
                return Result<bool>.Failure("ID do vídeo inválido");

            if (string.IsNullOrEmpty(channelId))
                return Result<bool>.Failure("ID do canal inválido");

            var userId = GetCurrentUserId();

            if (string.IsNullOrEmpty(userId))
                return Result<bool>.Failure("Usuário não identificado");

            // Verifica se o canal existe para o usuário
            var channel = await _channelRepository.GetByUserIdAndChannelIdAsync(userId, channelId);

            if (channel == null)
                return Result<bool>.Failure("Canal não encontrado para este usuário");

            // Verifica se o vídeo já existe para o usuário
            var existingVideo = await _videoRepository.GetByUserIdAndVideoIdAsync(userId, videoId);

            if (existingVideo != null)
                return true; // Vídeo já existe, retorna sucesso

            try
            {
                // Obtém os detalhes do vídeo do YouTube
                var videoDetails = await _videoYoutubeService.GetVideoDetailsAsync(videoId);

                if (!videoDetails.IsSuccess)
                    return Result<bool>.Failure(videoDetails.Error);

                // Cria o objeto de dados do vídeo
                var videoData = new VideoData
                {
                    UserId = userId,
                    ChannelId = channelId,
                    VideoId = videoId,
                    Title = videoDetails.Value.Title,
                    Description = videoDetails.Value.Description,
                    ThumbnailUrl = videoDetails.Value.ThumbnailUrl,
                    PublishedAt = videoDetails.Value.PublishedAt,
                    ViewCount = videoDetails.Value.ViewCount,
                    LikeCount = videoDetails.Value.LikeCount,
                    DislikeCount = videoDetails.Value.DislikeCount,
                    CommentCount = videoDetails.Value.CommentCount,
                    CreatedAt = DateTime.UtcNow
                };

                // Adiciona o vídeo ao repositório
                await _videoRepository.AddAsync(videoData);

                return true;
            }
            catch (Exception ex)
            {
                return Result<bool>.Failure(ex.Message);
            }
        }

        /// <summary>
        /// Adiciona múltiplos vídeos à lista do usuário
        /// </summary>
        public async Task<Result<bool>> AddVideosAsync(List<string> videoIds, string channelId)
        {
            if (videoIds == null || !videoIds.Any())
                return Result<bool>.Failure("Lista de IDs de vídeos inválida");

            if (string.IsNullOrEmpty(channelId))
                return Result<bool>.Failure("ID do canal inválido");

            var userId = GetCurrentUserId();

            if (string.IsNullOrEmpty(userId))
                return Result<bool>.Failure("Usuário não identificado");

            // Verifica se o canal existe para o usuário
            var channel = await _channelRepository.GetByUserIdAndChannelIdAsync(userId, channelId);

            if (channel == null)
                return Result<bool>.Failure("Canal não encontrado para este usuário");

            // Obtém os vídeos que o usuário já adicionou
            var existingVideos = await _videoRepository.GetByUserIdAsync(userId);
            var existingVideoIds = existingVideos.Select(v => v.VideoId).ToHashSet();

            // Filtra os vídeos que ainda não foram adicionados
            var newVideoIds = videoIds.Where(id => !existingVideoIds.Contains(id)).ToList();

            if (newVideoIds.Count == 0)
                return true; // Todos os vídeos já existem

            try
            {
                var videoDataList = new List<VideoData>();

                // Obtém os detalhes de cada vídeo do YouTube e cria os objetos de dados
                foreach (var videoId in newVideoIds)
                {
                    var videoDetails = await _videoYoutubeService.GetVideoDetailsAsync(videoId);

                    if (videoDetails.IsSuccess)
                    {
                        videoDataList.Add(new VideoData
                        {
                            UserId = userId,
                            ChannelId = channelId,
                            VideoId = videoId,
                            Title = videoDetails.Value.Title,
                            Description = videoDetails.Value.Description,
                            ThumbnailUrl = videoDetails.Value.ThumbnailUrl,
                            PublishedAt = videoDetails.Value.PublishedAt,
                            ViewCount = videoDetails.Value.ViewCount,
                            LikeCount = videoDetails.Value.LikeCount,
                            DislikeCount = videoDetails.Value.DislikeCount,
                            CommentCount = videoDetails.Value.CommentCount,
                            CreatedAt = DateTime.UtcNow
                        });
                    }
                }

                // Adiciona os vídeos ao repositório
                if (videoDataList.Any())
                {
                    await _videoRepository.AddManyAsync(videoDataList);
                }

                return true;
            }
            catch (Exception ex)
            {
                return Result<bool>.Failure(ex.Message);
            }
        }

        /// <summary>
        /// Remove um vídeo da lista do usuário
        /// </summary>
        public async Task<Result<bool>> RemoveVideoAsync(string videoId)
        {
            if (string.IsNullOrEmpty(videoId))
                return Result<bool>.Failure("ID do vídeo inválido");

            try
            {
                await _videoRepository.DeleteAsync(videoId);
                return true;
            }
            catch (Exception ex)
            {
                return Result<bool>.Failure(ex.Message);
            }
        }

        /// <summary>
        /// Busca vídeos no YouTube pelo título ou descrição
        /// </summary>
        public async Task<Result<List<VideoResponse>>> SearchVideosAsync(string query, int maxResults = 10)
        {
            if (string.IsNullOrEmpty(query) || query.Length < 3)
                return new List<VideoResponse>();

            var results = await _videoYoutubeService.SearchVideosAsync(query, maxResults);
            return results;
        }
    }
}