using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;
using Mindforge.API.Exceptions;
using Mindforge.API.Models;
using Mindforge.API.Services.Interfaces;

namespace Mindforge.API.Services
{
    public class GiteaService : IGiteaService
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly string _baseUrl = string.Empty;
        private readonly string _owner = string.Empty;
        private readonly string _repo = string.Empty;
        private readonly string _token = string.Empty;
        private readonly bool _isConfigured;

        private static readonly JsonSerializerOptions JsonOptions = new()
        {
            PropertyNameCaseInsensitive = true
        };

        public GiteaService(IHttpClientFactory httpClientFactory, IConfiguration configuration)
        {
            _httpClientFactory = httpClientFactory;

            var repoUrl = configuration["GITEA_REPO_URL"];
            var token = configuration["GITEA_ACCESS_TOKEN"];
            if (string.IsNullOrWhiteSpace(repoUrl) || string.IsNullOrWhiteSpace(token))
            {
                _isConfigured = false;
                return;
            }

            var normalizedUrl = repoUrl.TrimEnd('/').TrimEnd(".git".ToCharArray());
            var uri = new Uri(normalizedUrl);
            var parts = uri.AbsolutePath.Trim('/').Split('/');
            if (parts.Length < 2)
            {
                _isConfigured = false;
                return;
            }

            _baseUrl = $"{uri.Scheme}://{uri.Host}{(uri.IsDefaultPort ? "" : $":{uri.Port}")}";
            _owner = parts[0];
            _repo = parts[1];
            _token = token;
            _isConfigured = true;
        }

        public string GetRepositoryName() => _isConfigured ? _repo : "repositorio";

        public async Task<List<FileTreeNode>> GetFileTreeAsync()
        {
            EnsureConfigured();

            // Get the master branch to obtain the latest commit SHA
            var branchJson = await GetApiAsync($"/api/v1/repos/{_owner}/{_repo}/branches/master");
            var branch = JsonSerializer.Deserialize<GiteaBranch>(branchJson, JsonOptions)
                ?? throw new InvalidOperationException("Failed to parse branch response from Gitea.");

            var treeSha = branch.Commit.Id;

            // Fetch the full recursive tree
            var treeJson = await GetApiAsync($"/api/v1/repos/{_owner}/{_repo}/git/trees/{treeSha}?recursive=true");
            var treeResponse = JsonSerializer.Deserialize<GiteaTreeResponse>(treeJson, JsonOptions)
                ?? throw new InvalidOperationException("Failed to parse tree response from Gitea.");

            return BuildTree(treeResponse.Tree);
        }

        public async Task<string> GetFileContentAsync(string path)
        {
            EnsureConfigured();

            var request = new HttpRequestMessage(HttpMethod.Get,
                $"{_baseUrl}/api/v1/repos/{_owner}/{_repo}/raw/{path}?ref=master");
            request.Headers.Add("Authorization", $"token {_token}");

            var httpClient = _httpClientFactory.CreateClient();
            var response = await httpClient.SendAsync(request);
            if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                throw new UserException($"File not found in repository: {path}");

            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }

        private async Task<string> GetApiAsync(string endpoint)
        {
            EnsureConfigured();

            var httpClient = _httpClientFactory.CreateClient();
            var request = new HttpRequestMessage(HttpMethod.Get, $"{_baseUrl}{endpoint}");
            request.Headers.Add("Authorization", $"token {_token}");
            var response = await httpClient.SendAsync(request);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }

        private void EnsureConfigured()
        {
            if (!_isConfigured)
            {
                throw new InvalidOperationException("Gitea nao configurado. Defina GITEA_REPO_URL e GITEA_ACCESS_TOKEN.");
            }
        }

        private static List<FileTreeNode> BuildTree(List<GiteaTreeItem> items)
        {
            var root = new List<FileTreeNode>();
            var folderMap = new Dictionary<string, FileTreeNode>();

            // Only include .md files and their parent folders
            var mdFiles = items.Where(i => i.Type == "blob" && i.Path.EndsWith(".md")).ToList();
            var neededFolders = new HashSet<string>();
            foreach (var file in mdFiles)
            {
                var dir = System.IO.Path.GetDirectoryName(file.Path)?.Replace('\\', '/');
                while (!string.IsNullOrEmpty(dir))
                {
                    neededFolders.Add(dir);
                    dir = System.IO.Path.GetDirectoryName(dir)?.Replace('\\', '/');
                }
            }

            // Create folder nodes
            foreach (var item in items.Where(i => i.Type == "tree" && neededFolders.Contains(i.Path)).OrderBy(i => i.Path))
            {
                var node = new FileTreeNode
                {
                    Name = System.IO.Path.GetFileName(item.Path),
                    Path = item.Path,
                    Type = "folder",
                    Children = []
                };
                folderMap[item.Path] = node;
            }

            // Place file and folder nodes into parent
            var allNodes = mdFiles.Select(i => new FileTreeNode
            {
                Name = System.IO.Path.GetFileName(i.Path),
                Path = i.Path,
                Type = "file"
            }).Concat(folderMap.Values).OrderBy(n => n.Path);

            foreach (var node in allNodes)
            {
                var parentPath = System.IO.Path.GetDirectoryName(node.Path)?.Replace('\\', '/');
                if (string.IsNullOrEmpty(parentPath))
                    root.Add(node);
                else if (folderMap.TryGetValue(parentPath, out var parent))
                    parent.Children!.Add(node);
            }

            return root;
        }

        // ---- Gitea API DTOs ----

        private class GiteaBranch
        {
            [JsonPropertyName("commit")]
            public GiteaCommit Commit { get; set; } = new();
        }

        private class GiteaCommit
        {
            [JsonPropertyName("id")]
            public string Id { get; set; } = "";
        }

        private class GiteaTreeResponse
        {
            [JsonPropertyName("tree")]
            public List<GiteaTreeItem> Tree { get; set; } = [];
        }

        private class GiteaTreeItem
        {
            [JsonPropertyName("path")]
            public string Path { get; set; } = "";

            [JsonPropertyName("type")]
            public string Type { get; set; } = ""; // "blob" | "tree"
        }
    }
}
