using Microsoft.Extensions.Logging;
using RealCV.Application.Interfaces;
using RealCV.Application.Models;
using RealCV.Infrastructure.Clients;

using InterfaceOrcidSearchResult = RealCV.Application.Interfaces.OrcidSearchResult;

namespace RealCV.Infrastructure.Services;

public sealed class AcademicVerifierService : IAcademicVerifierService
{
    private readonly OrcidClient _orcidClient;
    private readonly ILogger<AcademicVerifierService> _logger;

    public AcademicVerifierService(
        OrcidClient orcidClient,
        ILogger<AcademicVerifierService> logger)
    {
        _orcidClient = orcidClient;
        _logger = logger;
    }

    public async Task<AcademicVerificationResult> VerifyByOrcidAsync(string orcidId)
    {
        try
        {
            _logger.LogInformation("Verifying ORCID: {OrcidId}", orcidId);

            var record = await _orcidClient.GetRecordAsync(orcidId);

            if (record == null)
            {
                return new AcademicVerificationResult
                {
                    ClaimedName = orcidId,
                    IsVerified = false,
                    VerificationNotes = "ORCID record not found"
                };
            }

            // Extract name
            string? name = null;
            if (record.Person?.Name != null)
            {
                var nameParts = new List<string>();
                if (!string.IsNullOrEmpty(record.Person.Name.GivenNames?.Value))
                    nameParts.Add(record.Person.Name.GivenNames.Value);
                if (!string.IsNullOrEmpty(record.Person.Name.FamilyName?.Value))
                    nameParts.Add(record.Person.Name.FamilyName.Value);
                name = string.Join(" ", nameParts);
            }

            // Get detailed employment information
            var affiliations = new List<AcademicAffiliation>();
            var employments = await _orcidClient.GetEmploymentsAsync(orcidId);
            if (employments?.AffiliationGroup != null)
            {
                affiliations.AddRange(ExtractAffiliations(employments.AffiliationGroup, "employment"));
            }

            // Get detailed education information
            var educationList = new List<AcademicEducation>();
            var educations = await _orcidClient.GetEducationsAsync(orcidId);
            if (educations?.AffiliationGroup != null)
            {
                educationList.AddRange(ExtractEducations(educations.AffiliationGroup));
            }

            // Get works/publications
            var publications = new List<Publication>();
            var works = await _orcidClient.GetWorksAsync(orcidId);
            if (works?.Group != null)
            {
                publications.AddRange(ExtractPublications(works.Group));
            }

            return new AcademicVerificationResult
            {
                ClaimedName = name ?? orcidId,
                IsVerified = true,
                OrcidId = orcidId,
                MatchedName = name,
                OrcidUrl = record.OrcidIdentifier?.Uri,
                Affiliations = affiliations,
                TotalPublications = publications.Count,
                RecentPublications = publications.Take(10).ToList(),
                Education = educationList,
                VerificationNotes = BuildVerificationSummary(affiliations, publications.Count, educationList.Count)
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error verifying ORCID: {OrcidId}", orcidId);
            return new AcademicVerificationResult
            {
                ClaimedName = orcidId,
                IsVerified = false,
                VerificationNotes = $"Error during verification: {ex.Message}"
            };
        }
    }

    public async Task<AcademicVerificationResult> VerifyByNameAsync(
        string name,
        string? affiliation = null)
    {
        try
        {
            _logger.LogInformation("Searching ORCID for: {Name} at {Affiliation}",
                name, affiliation ?? "any affiliation");

            var query = name;
            if (!string.IsNullOrEmpty(affiliation))
            {
                query = $"{name} {affiliation}";
            }

            var searchResponse = await _orcidClient.SearchResearchersAsync(query);

            if (searchResponse?.Result == null || searchResponse.Result.Count == 0)
            {
                return new AcademicVerificationResult
                {
                    ClaimedName = name,
                    IsVerified = false,
                    VerificationNotes = "No matching ORCID records found"
                };
            }

            // Get the first match's ORCID ID
            var firstMatch = searchResponse.Result.First();
            var orcidId = firstMatch.OrcidIdentifier?.Path;

            if (string.IsNullOrEmpty(orcidId))
            {
                return new AcademicVerificationResult
                {
                    ClaimedName = name,
                    IsVerified = false,
                    VerificationNotes = "Search returned results but no ORCID ID found"
                };
            }

            // Get full details
            var result = await VerifyByOrcidAsync(orcidId);

            // Update claimed name to the search name
            return result with { ClaimedName = name };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error searching ORCID for: {Name}", name);
            return new AcademicVerificationResult
            {
                ClaimedName = name,
                IsVerified = false,
                VerificationNotes = $"Error during search: {ex.Message}"
            };
        }
    }

    public async Task<List<InterfaceOrcidSearchResult>> SearchResearchersAsync(
        string name,
        string? affiliation = null)
    {
        try
        {
            var query = name;
            if (!string.IsNullOrEmpty(affiliation))
            {
                query = $"{name} {affiliation}";
            }

            var searchResponse = await _orcidClient.SearchResearchersAsync(query, 0, 20);

            if (searchResponse?.Result == null)
            {
                return [];
            }

            var results = new List<InterfaceOrcidSearchResult>();

            foreach (var searchResult in searchResponse.Result.Take(10))
            {
                var orcidId = searchResult.OrcidIdentifier?.Path;
                if (string.IsNullOrEmpty(orcidId))
                    continue;

                var record = await _orcidClient.GetRecordAsync(orcidId);
                if (record == null)
                    continue;

                // Extract name
                string? researcherName = null;
                if (record.Person?.Name != null)
                {
                    var nameParts = new List<string>();
                    if (!string.IsNullOrEmpty(record.Person.Name.GivenNames?.Value))
                        nameParts.Add(record.Person.Name.GivenNames.Value);
                    if (!string.IsNullOrEmpty(record.Person.Name.FamilyName?.Value))
                        nameParts.Add(record.Person.Name.FamilyName.Value);
                    researcherName = string.Join(" ", nameParts);
                }

                // Get affiliations
                var affiliations = new List<string>();
                if (record.ActivitiesSummary?.Employments?.AffiliationGroup != null)
                {
                    affiliations = record.ActivitiesSummary.Employments.AffiliationGroup
                        .SelectMany(g => g.Summaries ?? [])
                        .Select(s => s.EmploymentSummary?.Organization?.Name)
                        .Where(n => !string.IsNullOrEmpty(n))
                        .Distinct()
                        .Take(5)
                        .ToList()!;
                }

                // Get publication count
                var publicationCount = record.ActivitiesSummary?.Works?.Group?.Count ?? 0;

                results.Add(new InterfaceOrcidSearchResult
                {
                    OrcidId = orcidId,
                    Name = researcherName ?? "Unknown",
                    OrcidUrl = searchResult.OrcidIdentifier?.Uri,
                    Affiliations = affiliations,
                    PublicationCount = publicationCount
                });
            }

            return results;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error searching ORCID for: {Name}", name);
            return [];
        }
    }

    public async Task<List<PublicationVerificationResult>> VerifyPublicationsAsync(
        string orcidId,
        List<string> claimedPublications)
    {
        var results = new List<PublicationVerificationResult>();

        try
        {
            var works = await _orcidClient.GetWorksAsync(orcidId);

            if (works?.Group == null)
            {
                return claimedPublications.Select(title => new PublicationVerificationResult
                {
                    ClaimedTitle = title,
                    IsVerified = false,
                    Notes = "Could not retrieve ORCID publications"
                }).ToList();
            }

            var orcidPublications = ExtractPublications(works.Group);

            foreach (var claimedTitle in claimedPublications)
            {
                var match = FindBestPublicationMatch(claimedTitle, orcidPublications);

                if (match != null)
                {
                    results.Add(new PublicationVerificationResult
                    {
                        ClaimedTitle = claimedTitle,
                        IsVerified = true,
                        MatchedTitle = match.Title,
                        Doi = match.Doi,
                        Year = match.Year,
                        Notes = "Publication found in ORCID record"
                    });
                }
                else
                {
                    results.Add(new PublicationVerificationResult
                    {
                        ClaimedTitle = claimedTitle,
                        IsVerified = false,
                        Notes = "Publication not found in ORCID record"
                    });
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error verifying publications for ORCID: {OrcidId}", orcidId);

            return claimedPublications.Select(title => new PublicationVerificationResult
            {
                ClaimedTitle = title,
                IsVerified = false,
                Notes = $"Error during verification: {ex.Message}"
            }).ToList();
        }

        return results;
    }

    private static List<AcademicAffiliation> ExtractAffiliations(
        List<OrcidAffiliationGroup> groups,
        string type)
    {
        var affiliations = new List<AcademicAffiliation>();

        foreach (var group in groups)
        {
            if (group.Summaries == null)
                continue;

            foreach (var wrapper in group.Summaries)
            {
                var summary = type == "employment"
                    ? wrapper.EmploymentSummary
                    : wrapper.EducationSummary;

                if (summary?.Organization?.Name == null)
                    continue;

                affiliations.Add(new AcademicAffiliation
                {
                    Organization = summary.Organization.Name,
                    Department = summary.DepartmentName,
                    Role = summary.RoleTitle,
                    StartDate = ParseOrcidDate(summary.StartDate),
                    EndDate = ParseOrcidDate(summary.EndDate)
                });
            }
        }

        // Sort by start date descending (most recent first)
        return affiliations
            .OrderByDescending(a => a.StartDate)
            .ToList();
    }

    private static List<AcademicEducation> ExtractEducations(List<OrcidAffiliationGroup> groups)
    {
        var educations = new List<AcademicEducation>();

        foreach (var group in groups)
        {
            if (group.Summaries == null)
                continue;

            foreach (var wrapper in group.Summaries)
            {
                var summary = wrapper.EducationSummary;
                if (summary?.Organization?.Name == null)
                    continue;

                var startYear = summary.StartDate?.Year?.Value;
                var endYear = summary.EndDate?.Year?.Value;

                educations.Add(new AcademicEducation
                {
                    Institution = summary.Organization.Name,
                    Degree = summary.RoleTitle,
                    Subject = summary.DepartmentName,
                    Year = !string.IsNullOrEmpty(endYear)
                        ? int.TryParse(endYear, out var y) ? y : null
                        : !string.IsNullOrEmpty(startYear)
                            ? int.TryParse(startYear, out var sy) ? sy : null
                            : null
                });
            }
        }

        return educations
            .OrderByDescending(e => e.Year)
            .ToList();
    }

    private static List<Publication> ExtractPublications(List<OrcidWorkGroup> groups)
    {
        var publications = new List<Publication>();

        foreach (var group in groups)
        {
            if (group.WorkSummary == null || group.WorkSummary.Count == 0)
                continue;

            // Take the first work summary from each group (they're typically duplicates)
            var work = group.WorkSummary.First();

            var title = work.TitleObj?.Title?.Value ?? work.Title;
            if (string.IsNullOrEmpty(title))
                continue;

            // Extract DOI if available
            string? doi = null;
            if (work.ExternalIds?.ExternalId != null)
            {
                var doiEntry = work.ExternalIds.ExternalId
                    .FirstOrDefault(e => e.ExternalIdType?.Equals("doi", StringComparison.OrdinalIgnoreCase) == true);
                doi = doiEntry?.ExternalIdValue;
            }

            // Parse year
            int? year = null;
            if (!string.IsNullOrEmpty(work.PublicationDate?.Year?.Value) &&
                int.TryParse(work.PublicationDate.Year.Value, out var y))
            {
                year = y;
            }

            publications.Add(new Publication
            {
                Title = title,
                Journal = work.JournalTitle?.Value,
                Year = year,
                Doi = doi,
                Type = work.Type
            });
        }

        // Sort by publication year descending
        return publications
            .OrderByDescending(p => p.Year)
            .ToList();
    }

    private static DateOnly? ParseOrcidDate(OrcidDate? date)
    {
        if (date?.Year?.Value == null)
            return null;

        if (!int.TryParse(date.Year.Value, out var year))
            return null;

        var month = 1;
        if (!string.IsNullOrEmpty(date.Month?.Value) && int.TryParse(date.Month.Value, out var m))
            month = m;

        var day = 1;
        if (!string.IsNullOrEmpty(date.Day?.Value) && int.TryParse(date.Day.Value, out var d))
            day = d;

        try
        {
            return new DateOnly(year, month, day);
        }
        catch
        {
            return new DateOnly(year, 1, 1);
        }
    }

    private static Publication? FindBestPublicationMatch(string claimedTitle, List<Publication> publications)
    {
        var normalizedClaimed = NormalizeTitle(claimedTitle);

        // First try exact match
        var exactMatch = publications.FirstOrDefault(p =>
            NormalizeTitle(p.Title).Equals(normalizedClaimed, StringComparison.OrdinalIgnoreCase));

        if (exactMatch != null)
            return exactMatch;

        // Then try contains match
        var containsMatch = publications.FirstOrDefault(p =>
            NormalizeTitle(p.Title).Contains(normalizedClaimed, StringComparison.OrdinalIgnoreCase) ||
            normalizedClaimed.Contains(NormalizeTitle(p.Title), StringComparison.OrdinalIgnoreCase));

        return containsMatch;
    }

    private static string NormalizeTitle(string title)
    {
        return title
            .ToLowerInvariant()
            .Replace(":", " ")
            .Replace("-", " ")
            .Replace("  ", " ")
            .Trim();
    }

    private static string BuildVerificationSummary(List<AcademicAffiliation> affiliations, int publicationCount, int educationCount)
    {
        var parts = new List<string>();

        var currentAffiliation = affiliations.FirstOrDefault(a => !a.EndDate.HasValue);
        if (currentAffiliation != null)
        {
            parts.Add($"Current: {currentAffiliation.Organization}");
        }

        if (publicationCount > 0)
        {
            parts.Add($"Publications: {publicationCount}");
        }

        if (affiliations.Count > 0)
        {
            parts.Add($"Positions: {affiliations.Count}");
        }

        if (educationCount > 0)
        {
            parts.Add($"Education: {educationCount}");
        }

        return parts.Count > 0 ? string.Join(" | ", parts) : "ORCID record verified";
    }
}
