using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace RealCV.Infrastructure.Clients;

public sealed class GitHubApiClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<GitHubApiClient> _logger;

    public GitHubApiClient(
        HttpClient httpClient,
        IOptions<GitHubOptions> options,
        ILogger<GitHubApiClient> logger)
    {
        _httpClient = httpClient;
        _logger = logger;

        _httpClient.BaseAddress = new Uri("https://api.github.com/");
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
        _httpClient.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
        _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("RealCV/1.0");

        if (!string.IsNullOrEmpty(options.Value.PersonalAccessToken))
        {
            _httpClient.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", options.Value.PersonalAccessToken);
        }
    }

    public async Task<GitHubUser?> GetUserAsync(string username)
    {
        try
        {
            var url = $"users/{Uri.EscapeDataString(username)}";

            _logger.LogDebug("Getting GitHub user: {Username}", username);

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("GitHub API returned {StatusCode} for user: {Username}",
                    response.StatusCode, username);
                return null;
            }

            return await response.Content.ReadFromJsonAsync<GitHubUser>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting GitHub user: {Username}", username);
            return null;
        }
    }

    public async Task<List<GitHubRepo>> GetUserReposAsync(string username, int perPage = 100)
    {
        var repos = new List<GitHubRepo>();
        var page = 1;

        try
        {
            while (true)
            {
                var url = $"users/{Uri.EscapeDataString(username)}/repos?per_page={perPage}&page={page}&sort=updated";

                var response = await _httpClient.GetAsync(url);

                if (!response.IsSuccessStatusCode)
                {
                    break;
                }

                var pageRepos = await response.Content.ReadFromJsonAsync<List<GitHubRepo>>(JsonOptions);

                if (pageRepos == null || pageRepos.Count == 0)
                {
                    break;
                }

                repos.AddRange(pageRepos);

                if (pageRepos.Count < perPage)
                {
                    break;
                }

                page++;

                // Limit to avoid rate limiting
                if (page > 5)
                {
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting repos for user: {Username}", username);
        }

        return repos;
    }

    public async Task<Dictionary<string, int>?> GetRepoLanguagesAsync(string owner, string repo)
    {
        try
        {
            var url = $"repos/{Uri.EscapeDataString(owner)}/{Uri.EscapeDataString(repo)}/languages";

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                return null;
            }

            return await response.Content.ReadFromJsonAsync<Dictionary<string, int>>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting languages for repo: {Owner}/{Repo}", owner, repo);
            return null;
        }
    }

    public async Task<GitHubUserSearchResponse?> SearchUsersAsync(string query, int perPage = 30)
    {
        try
        {
            var encodedQuery = Uri.EscapeDataString(query);
            var url = $"search/users?q={encodedQuery}&per_page={perPage}";

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                return null;
            }

            return await response.Content.ReadFromJsonAsync<GitHubUserSearchResponse>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error searching GitHub users: {Query}", query);
            return null;
        }
    }

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };
}

public class GitHubOptions
{
    public string PersonalAccessToken { get; set; } = string.Empty;
}

// Response models
public class GitHubUser
{
    public string? Login { get; set; }
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? Company { get; set; }
    public string? Blog { get; set; }
    public string? Location { get; set; }
    public string? Email { get; set; }
    public string? Bio { get; set; }

    [JsonPropertyName("twitter_username")]
    public string? TwitterUsername { get; set; }

    [JsonPropertyName("public_repos")]
    public int PublicRepos { get; set; }

    [JsonPropertyName("public_gists")]
    public int PublicGists { get; set; }

    public int Followers { get; set; }
    public int Following { get; set; }

    [JsonPropertyName("created_at")]
    public DateTime CreatedAt { get; set; }

    [JsonPropertyName("updated_at")]
    public DateTime UpdatedAt { get; set; }

    [JsonPropertyName("html_url")]
    public string? HtmlUrl { get; set; }

    [JsonPropertyName("avatar_url")]
    public string? AvatarUrl { get; set; }
}

public class GitHubRepo
{
    public int Id { get; set; }
    public string? Name { get; set; }

    [JsonPropertyName("full_name")]
    public string? FullName { get; set; }

    public string? Description { get; set; }
    public string? Language { get; set; }

    [JsonPropertyName("html_url")]
    public string? HtmlUrl { get; set; }

    public bool Fork { get; set; }
    public bool Private { get; set; }

    [JsonPropertyName("stargazers_count")]
    public int StargazersCount { get; set; }

    [JsonPropertyName("watchers_count")]
    public int WatchersCount { get; set; }

    [JsonPropertyName("forks_count")]
    public int ForksCount { get; set; }

    public int Size { get; set; }

    [JsonPropertyName("created_at")]
    public DateTime CreatedAt { get; set; }

    [JsonPropertyName("updated_at")]
    public DateTime UpdatedAt { get; set; }

    [JsonPropertyName("pushed_at")]
    public DateTime? PushedAt { get; set; }
}

public class GitHubUserSearchResponse
{
    [JsonPropertyName("total_count")]
    public int TotalCount { get; set; }

    [JsonPropertyName("incomplete_results")]
    public bool IncompleteResults { get; set; }

    public List<GitHubUserSearchItem>? Items { get; set; }
}

public class GitHubUserSearchItem
{
    public string? Login { get; set; }
    public int Id { get; set; }

    [JsonPropertyName("avatar_url")]
    public string? AvatarUrl { get; set; }

    [JsonPropertyName("html_url")]
    public string? HtmlUrl { get; set; }

    public double Score { get; set; }
}
