using ChatMvc.Models;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authorization;
using System.Linq;
using Microsoft.AspNetCore.Authentication;
using Chat.Domain.Tools;
using Newtonsoft.Json.Linq;

namespace ChatMvc.Controllers
{
    [Authorize]
    public class ChatController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly IConfiguration _configuration;
        private readonly IAntiforgery _antiforgery;

        public ChatController(IHttpClientFactory httpClientFactory, IConfiguration configuration, IAntiforgery antiforgery)
        {
            _httpClientFactory = httpClientFactory;
            _configuration = configuration;
            _antiforgery = antiforgery;
        }

        public IActionResult Index()
        {
            var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
            var token = HttpContext.GetTokenAsync("access_token").Result;
            ViewBag.Token = token;
            return View();
        }

        [HttpPost("chat/proxy-response")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> ProxyResponse([FromForm] string sessionId, [FromForm] string message, [FromForm] IFormFile file1 = null, [FromForm] IFormFile file2 = null)
        {
            try
            {
                if (!Request.Headers["X-Requested-With"].Equals("XMLHttpRequest"))
                {
                    return BadRequest("Requisição inválida");
                }

                var userId = User.Claims.FirstOrDefault(f => f.Type == "UserId")?.Value;
                var name = User.Claims.FirstOrDefault(f => f.Type == "FirstName")?.Value;
                var company = User.Claims.FirstOrDefault(f => f.Type == "CompanyName")?.Value;
                var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal")?.Value;

                if (string.IsNullOrEmpty(token))
                {
                    return BadRequest("Token não encontrado");
                }

                // Verificar se há arquivos e palavras-chave
                var keyWords = new[] { "diferença", "diferenças", "comparação", "comparar", "compare" };
                bool hasKeyword = keyWords.Any(k => message.ToLower().Contains(k.ToLower()));
                bool hasBothFiles = file1 != null && file2 != null;
                bool hasOneFile = file1 != null || file2 != null;
                bool isPDF = file1 != null && file1.ContentType == "application/pdf" && file2 != null && file2.ContentType == "application/pdf";
                bool isImage = file1 != null && file1.ContentType.Contains("image") && file2 != null && file2.ContentType.Contains("image");

                // Se tem apenas um arquivo ou se tem arquivos mas não tem palavra-chave
                if ((hasOneFile && !hasBothFiles) || (hasBothFiles && !hasKeyword))
                {
                    return Ok("Por favor, especifique o que deseja fazer com os arquivos. " +
                             "Para comparar dois PDFs, inclua palavras como 'diferença' ou 'comparação' em sua mensagem.");
                }

                var client = _httpClientFactory.CreateClient();
                client.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", ""));

                var baseUrl = _configuration["ExternalApiBaseUrl"];

                if (hasBothFiles && hasKeyword && isPDF)
                {
                    var content = await CompareFiles(baseUrl, sessionId, client, file1, file2);
                    return Ok(content);
                }
                else if (hasBothFiles && hasKeyword && isImage)
                {
                    var content = await CompareFilesImage(baseUrl, sessionId, client, file1, file2);
                    return Ok(content);
                }

                var chatContent = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("sessionId", sessionId),
                    new KeyValuePair<string, string>("message", message)
                });

                var response = await client.PostAsync($"{baseUrl}/chat/response", chatContent);

                response.EnsureSuccessStatusCode();
                var responseContent = await response.Content.ReadAsStringAsync();

                return Ok(responseContent);
            }
            catch (Exception ex)
            {
                return BadRequest($"Erro ao processar mensagem: {ex.Message}");
            }
        }

        private async Task<string> CompareFiles(string? baseUrl, string sessionId, HttpClient? client, [FromForm] IFormFile? file1 = null, [FromForm] IFormFile? file2 = null)
        {
            // Primeiro, comparar os PDFs
            var formData = new MultipartFormDataContent();
            var content1 = new StreamContent(file1.OpenReadStream());
            var content2 = new StreamContent(file2.OpenReadStream());
            content1.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
            content2.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
            formData.Add(content1, "File1", file1.FileName);
            formData.Add(content2, "File2", file2.FileName);

            var comparisonResponse = await client.PostAsync(
                "https://localhost:7200/api/PdfComparison/compare",
                formData
            );

            if (!comparisonResponse.IsSuccessStatusCode)
            {
                throw new Exception("Erro ao comparar os PDFs");
            }

            var differences = await comparisonResponse.Content.ReadAsStringAsync();
            var dynamicContent = JsonConvert.DeserializeObject<dynamic>(differences);
            var formattedDifferences = FormatDifferencesForChat(dynamicContent);

            var promptMessage = $"Ao comparar dois textos obtive as diferenças do json. Você poderia explicar e resumir: {formattedDifferences}";

            var chatContentSend = new FormUrlEncodedContent(new[]
            {
                        new KeyValuePair<string, string>("sessionId", sessionId),
                        new KeyValuePair<string, string>("message", promptMessage)
                    });

            var chatResponse = await client.PostAsync($"{baseUrl}/chat/response-fromia", chatContentSend);

            chatResponse.EnsureSuccessStatusCode();
            var content = await chatResponse.Content.ReadAsStringAsync();

            return content;
        }

        private async Task<string> CompareFilesImage(string? baseUrl, string sessionId, HttpClient? client, [FromForm] IFormFile? file1 = null, [FromForm] IFormFile? file2 = null)
        {
            var formData = new MultipartFormDataContent();
            var content1 = new StreamContent(file1.OpenReadStream());
            var content2 = new StreamContent(file2.OpenReadStream());
            content1.Headers.ContentType = new MediaTypeHeaderValue(file1.ContentType);
            content2.Headers.ContentType = new MediaTypeHeaderValue(file2.ContentType);
            formData.Add(content1, "Drawing1", file1.FileName);
            formData.Add(content2, "Drawing2", file2.FileName);

            var comparisonResponse = await client.PostAsync(
                "https://localhost:7200/api/TechnicalDrawing/compare-images",
                formData
            );

            if (!comparisonResponse.IsSuccessStatusCode)
            {
                throw new Exception("Erro ao comparar os PDFs");
            }

            var differences = await comparisonResponse.Content.ReadAsStringAsync();
            JObject obj = JObject.Parse(differences);
            string image = obj["image"].ToString();

            var file3 = Base64ToFormFile.ConvertBase64ToFormFile(image, "compared.png");

            var promptMessage = $"Ao comparar dois desenhos técnicos, obtive as diferenças entre eles. Você precisa explicar e resumir estas diferenças ressaltando mudanças em medidas da peça. O arquivo ";

            var response = "";
            using (formData = AddFormDataToResponse(sessionId, promptMessage, file1, file2, file3))
            {
                    var chatResponse = await client.PostAsync($"{baseUrl}/chat/response-fromia", formData);
                    chatResponse.EnsureSuccessStatusCode();
                    response = await chatResponse.Content.ReadAsStringAsync();
            }
            return response;
        }

        private MultipartFormDataContent AddFormDataToResponse(string sessionId, string message, IFormFile? file1 = null, IFormFile? file2 = null, IFormFile? file3 = null)
        {
            var formData = new MultipartFormDataContent();
            formData.Add(new StringContent(sessionId), "SessionId");
            formData.Add(new StringContent(message), "Message");

            if (file1 != null)
            {
                var streamContent1 = new StreamContent(file1.OpenReadStream());
                formData.Add(streamContent1, "File1", file1.FileName);
            }

            if (file2 != null)
            {
                var streamContent2 = new StreamContent(file2.OpenReadStream());
                formData.Add(streamContent2, "File2", file2.FileName);
            }

            if (file3 != null)
            {
                var streamContent3 = new StreamContent(file3.OpenReadStream());
                formData.Add(streamContent3, "File3", file3.FileName);
            }

            return formData;
        }

        [HttpPost("chat/authenticate")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequest request)
        {
            try
            {
                if (!Request.Headers["X-Requested-With"].Equals("XMLHttpRequest"))
                {
                    return BadRequest("Requisição inválida");
                }

                var client = _httpClientFactory.CreateClient();
                var baseUrl = _configuration["ExternalApiBaseUrl"];

                // Primeira requisição - newclient
                var newClientRequest = new
                {
                    localId = request.UserId,
                    companyTenant = request.Company,
                    name = request.Name
                };

                var newClientResponse = await client.PostAsync(
                    $"{baseUrl}/login/newclient",
                    new StringContent(JsonConvert.SerializeObject(newClientRequest),
                        Encoding.UTF8, "application/json"));

                newClientResponse.EnsureSuccessStatusCode();
                var clientContent = await newClientResponse.Content.ReadAsStringAsync();
                var clientResult = JsonConvert.DeserializeObject<NewClientResponse>(clientContent);

                // Segunda requisição - token
                var tokenRequest = new
                {
                    clientId = request.UserId,
                    clientName = request.Name,
                    clientSecret = clientResult.Secret
                };

                var tokenResponse = await client.PostAsync(
                    $"{baseUrl}/login/token",
                    new StringContent(JsonConvert.SerializeObject(tokenRequest),
                        Encoding.UTF8, "application/json"));

                tokenResponse.EnsureSuccessStatusCode();
                var tokenContent = await tokenResponse.Content.ReadAsStringAsync();
                var tokenResult = JsonConvert.DeserializeObject<TokenResponse>(tokenContent);

                return Ok(new { token = tokenResult.Token });
            }
            catch (Exception ex)
            {
                return BadRequest($"Erro na autenticação: {ex.Message}");
            }
        }

        private string FormatDifferencesForChat(dynamic jsonDifferences)
        {
            var sb = new StringBuilder();
            sb.AppendLine("Análise das diferenças entre os documentos:");
            sb.AppendLine();

            try
            {
                // Focar apenas nas diferenças reais (onlyDifferences)
                var onlyDiffs = jsonDifferences.onlyDifferences;

                if (onlyDiffs != null && onlyDiffs.Count > 0)
                {
                    foreach (var diff in onlyDiffs)
                    {
                        string content = diff.content?.ToString() ?? "";
                        string type = diff.typeName?.ToString() ?? "";
                        int lineNumber = diff.lineNumber;

                        switch (type)
                        {
                            case "Deleted":
                                sb.AppendLine($"Removido (linha {lineNumber}):");
                                sb.AppendLine($"- {content}");
                                break;
                            case "Inserted":
                                sb.AppendLine($"Adicionado (linha {lineNumber}):");
                                sb.AppendLine($"+ {content}");
                                break;
                        }
                        sb.AppendLine();
                    }
                }
                else
                {
                    sb.AppendLine("Nenhuma diferença significativa encontrada entre os documentos.");
                }
            }
            catch (Exception ex)
            {
                sb.AppendLine("Erro ao processar diferenças. Resumo simplificado:");
                sb.AppendLine("As diferenças entre os documentos não puderam ser formatadas adequadamente.");
            }

            return sb.ToString();
        }
    }

    public class AuthenticateRequest
    {
        public string UserId { get; set; }
        public string Company { get; set; }
        public string Name { get; set; }
    }

    public class NewClientResponse
    {
        public string Secret { get; set; }
    }

    public class TokenResponse
    {
        public string Token { get; set; }
    }
}
