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

namespace RealCV.Infrastructure.Clients;

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

    public OrcidClient(
        HttpClient httpClient,
        ILogger<OrcidClient> logger)
    {
        _httpClient = httpClient;
        _logger = logger;

        _httpClient.BaseAddress = new Uri("https://pub.orcid.org/v3.0/");
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<OrcidSearchResponse?> SearchResearchersAsync(string query, int start = 0, int rows = 20)
    {
        try
        {
            var encodedQuery = Uri.EscapeDataString(query);
            var url = $"search?q={encodedQuery}&start={start}&rows={rows}";

            _logger.LogDebug("Searching ORCID: {Query}", query);

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("ORCID API returned {StatusCode} for search: {Query}",
                    response.StatusCode, query);
                return null;
            }

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

    public async Task<OrcidRecord?> GetRecordAsync(string orcidId)
    {
        try
        {
            // Normalize ORCID ID format (remove URL prefix if present)
            orcidId = NormalizeOrcidId(orcidId);

            var url = $"{orcidId}/record";

            _logger.LogDebug("Getting ORCID record: {OrcidId}", orcidId);

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("ORCID API returned {StatusCode} for ID: {OrcidId}",
                    response.StatusCode, orcidId);
                return null;
            }

            return await response.Content.ReadFromJsonAsync<OrcidRecord>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting ORCID record: {OrcidId}", orcidId);
            return null;
        }
    }

    public async Task<OrcidWorks?> GetWorksAsync(string orcidId)
    {
        try
        {
            orcidId = NormalizeOrcidId(orcidId);
            var url = $"{orcidId}/works";

            var response = await _httpClient.GetAsync(url);

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

            return await response.Content.ReadFromJsonAsync<OrcidWorks>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting ORCID works: {OrcidId}", orcidId);
            return null;
        }
    }

    public async Task<OrcidEmployments?> GetEmploymentsAsync(string orcidId)
    {
        try
        {
            orcidId = NormalizeOrcidId(orcidId);
            var url = $"{orcidId}/employments";

            var response = await _httpClient.GetAsync(url);

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

            return await response.Content.ReadFromJsonAsync<OrcidEmployments>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting ORCID employments: {OrcidId}", orcidId);
            return null;
        }
    }

    public async Task<OrcidEducations?> GetEducationsAsync(string orcidId)
    {
        try
        {
            orcidId = NormalizeOrcidId(orcidId);
            var url = $"{orcidId}/educations";

            var response = await _httpClient.GetAsync(url);

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

            return await response.Content.ReadFromJsonAsync<OrcidEducations>(JsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting ORCID educations: {OrcidId}", orcidId);
            return null;
        }
    }

    private static string NormalizeOrcidId(string orcidId)
    {
        // Remove URL prefixes
        orcidId = orcidId.Replace("https://orcid.org/", "")
                        .Replace("http://orcid.org/", "")
                        .Trim();
        return orcidId;
    }

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

// Response models
public class OrcidSearchResponse
{
    [JsonPropertyName("num-found")]
    public int NumFound { get; set; }

    public List<OrcidSearchResult>? Result { get; set; }
}

public class OrcidSearchResult
{
    [JsonPropertyName("orcid-identifier")]
    public OrcidIdentifier? OrcidIdentifier { get; set; }
}

public class OrcidIdentifier
{
    public string? Uri { get; set; }
    public string? Path { get; set; }
    public string? Host { get; set; }
}

public class OrcidRecord
{
    [JsonPropertyName("orcid-identifier")]
    public OrcidIdentifier? OrcidIdentifier { get; set; }

    public OrcidPerson? Person { get; set; }

    [JsonPropertyName("activities-summary")]
    public OrcidActivitiesSummary? ActivitiesSummary { get; set; }
}

public class OrcidPerson
{
    public OrcidName? Name { get; set; }
    public OrcidBiography? Biography { get; set; }
}

public class OrcidName
{
    [JsonPropertyName("given-names")]
    public OrcidValue? GivenNames { get; set; }

    [JsonPropertyName("family-name")]
    public OrcidValue? FamilyName { get; set; }

    [JsonPropertyName("credit-name")]
    public OrcidValue? CreditName { get; set; }
}

public class OrcidValue
{
    public string? Value { get; set; }
}

public class OrcidBiography
{
    public string? Content { get; set; }
}

public class OrcidActivitiesSummary
{
    public OrcidEmployments? Employments { get; set; }
    public OrcidEducations? Educations { get; set; }
    public OrcidWorks? Works { get; set; }
}

public class OrcidEmployments
{
    [JsonPropertyName("affiliation-group")]
    public List<OrcidAffiliationGroup>? AffiliationGroup { get; set; }
}

public class OrcidEducations
{
    [JsonPropertyName("affiliation-group")]
    public List<OrcidAffiliationGroup>? AffiliationGroup { get; set; }
}

public class OrcidAffiliationGroup
{
    public List<OrcidAffiliationSummaryWrapper>? Summaries { get; set; }
}

public class OrcidAffiliationSummaryWrapper
{
    [JsonPropertyName("employment-summary")]
    public OrcidAffiliationSummary? EmploymentSummary { get; set; }

    [JsonPropertyName("education-summary")]
    public OrcidAffiliationSummary? EducationSummary { get; set; }
}

public class OrcidAffiliationSummary
{
    [JsonPropertyName("department-name")]
    public string? DepartmentName { get; set; }

    [JsonPropertyName("role-title")]
    public string? RoleTitle { get; set; }

    [JsonPropertyName("start-date")]
    public OrcidDate? StartDate { get; set; }

    [JsonPropertyName("end-date")]
    public OrcidDate? EndDate { get; set; }

    public OrcidOrganization? Organization { get; set; }
}

public class OrcidDate
{
    public OrcidValue? Year { get; set; }
    public OrcidValue? Month { get; set; }
    public OrcidValue? Day { get; set; }
}

public class OrcidOrganization
{
    public string? Name { get; set; }
    public OrcidAddress? Address { get; set; }
}

public class OrcidAddress
{
    public string? City { get; set; }
    public string? Region { get; set; }
    public string? Country { get; set; }
}

public class OrcidWorks
{
    public List<OrcidWorkGroup>? Group { get; set; }
}

public class OrcidWorkGroup
{
    [JsonPropertyName("work-summary")]
    public List<OrcidWorkSummary>? WorkSummary { get; set; }
}

public class OrcidWorkSummary
{
    public string? Title { get; set; }
    public OrcidTitle? TitleObj { get; set; }
    public string? Type { get; set; }

    [JsonPropertyName("publication-date")]
    public OrcidDate? PublicationDate { get; set; }

    [JsonPropertyName("journal-title")]
    public OrcidValue? JournalTitle { get; set; }

    [JsonPropertyName("external-ids")]
    public OrcidExternalIds? ExternalIds { get; set; }
}

public class OrcidTitle
{
    public OrcidValue? Title { get; set; }
}

public class OrcidExternalIds
{
    [JsonPropertyName("external-id")]
    public List<OrcidExternalId>? ExternalId { get; set; }
}

public class OrcidExternalId
{
    [JsonPropertyName("external-id-type")]
    public string? ExternalIdType { get; set; }

    [JsonPropertyName("external-id-value")]
    public string? ExternalIdValue { get; set; }
}
