using Coscine.Configuration;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using VDS.RDF;

namespace Coscine.Metadata.Models
{
    public class OrganizationModel
    {
        private readonly IConfiguration _configuration;

        public OrganizationModel(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        private List<string> AdaptExternalIds(IEnumerable<ExternalId> externalIds)
        {
            List<string> adaptedIds = new List<string>();

            var externalAuthenticatorModel = new ExternalAuthenticatorModel();
            var orcid = externalAuthenticatorModel.GetWhere((exAuth) => exAuth.DisplayName == "ORCiD");

            foreach (var externalId in externalIds)
            {
                var exId = externalId.ExternalId_Column;
                if (externalId.ExternalAuthenticatorId == orcid.Id)
                {
                    exId = "https://orcid.org/" + exId;
                }
                adaptedIds.Add(exId);
            }
            return adaptedIds;
        }

        private bool IsMemberFilteringActivated()
        {
            var memberFilterOption = _configuration.GetStringAndWait("coscine/local/organizations/memberfiltering");

            var parseable = int.TryParse(memberFilterOption, out int memberFiltering);

            return memberFilterOption == null || (parseable && memberFiltering == 1);
        }

        public bool IsMember(IEnumerable<ExternalId> externalIds, IGraph graph)
        {
            if (IsMemberFilteringActivated())
            {
                var openIdPredicate = "http://xmlns.com/foaf/0.1/#openId";
                bool access = false;
                var adaptedIds = AdaptExternalIds(externalIds);
                foreach (var externalIdObject in graph.GetTriplesWithPredicate(new Uri(openIdPredicate))
                                                        .Select((triple) => triple.Object.ToString()))
                {
                    if (adaptedIds.Any((externalId) => externalId == externalIdObject))
                    {
                        access = true;
                        break;
                    }
                }

                return access;
            }
            else
            {
                return true;
            }
        }

        public bool HasLabel(string query, IGraph graph)
        {
            var labelUri = new Uri("http://www.w3.org/1999/02/22-rdf-syntax-ns#label");
            return graph.GetTriplesWithPredicate(labelUri).Any((triple) => triple.Object.ToString().Contains(query));
        }

        public void RemoveBySubject(IGraph graph, INode subject)
        {
            foreach (var triple in graph.GetTriplesWithSubject(subject).ToList())
            {
                graph.Retract(triple);
            }
        }

        public IGraph FilterByMember(IGraph organizationGraph, IEnumerable<ExternalId> externalIds)
        {
            if (IsMemberFilteringActivated())
            {
                var openIdPredicate = "http://xmlns.com/foaf/0.1/#openId";
                var identifierPredicateUri = new Uri("http://www.w3.org/ns/org#identifier");
                var hasMemberPredicateUri = new Uri("http://www.w3.org/ns/org#hasMember");
                var hasMemberPredicateNode = organizationGraph.CreateUriNode(hasMemberPredicateUri);
                var adaptedIds = AdaptExternalIds(externalIds);
                var removedSubOrganisations = organizationGraph.GetTriplesWithPredicate(identifierPredicateUri)
                                                            .Select((triple) => triple.Subject);
                foreach (var externalIdTriple in organizationGraph.GetTriplesWithPredicate(new Uri(openIdPredicate)))
                {
                    var externalIdObject = externalIdTriple.Object.ToString();
                    if (adaptedIds.Any((externalId) => externalId == externalIdObject))
                    {
                        foreach (var subOrganisation in organizationGraph.GetTriplesWithPredicateObject(hasMemberPredicateNode,
                                                                                                            externalIdTriple.Subject))
                        {
                            removedSubOrganisations = removedSubOrganisations.Where((entry) => entry.ToString() != subOrganisation.Subject.ToString());
                        }
                    }
                }

                foreach (var removedSubOrganisation in removedSubOrganisations.ToList())
                {
                    RemoveBySubject(organizationGraph, removedSubOrganisation);
                }
            }

            return organizationGraph;
        }

        public IGraph FilterByQuery(IGraph organizationGraph, string query)
        {
            var identifierPredicateUri = new Uri("http://www.w3.org/ns/org#identifier");
            var labelUri = new Uri("http://www.w3.org/1999/02/22-rdf-syntax-ns#label");
            var removedSubOrganisations = organizationGraph.GetTriplesWithPredicate(identifierPredicateUri)
                                                        .Select((triple) => triple.Subject);
            foreach (var externalIdTriple in organizationGraph.GetTriplesWithPredicate(labelUri))
            {
                var label = externalIdTriple.Object.ToString();
                if (label.Contains(query))
                {
                    removedSubOrganisations = removedSubOrganisations.Where((entry) => entry.ToString() != externalIdTriple.Subject.ToString());
                }
            }

            foreach (var removedSubOrganisation in removedSubOrganisations.ToList())
            {
                RemoveBySubject(organizationGraph, removedSubOrganisation);
            }

            return organizationGraph;
        }

        public void RemoveMembershipTriples(IGraph graph)
        {
            var membershipObject = "http://www.w3.org/ns/org#Membership";
            var personObject = "http://xmlns.com/foaf/0.1/#Person";

            var memberShipSubjects = graph.GetTriplesWithObject(new Uri(membershipObject)).Select((triple) => triple.Subject);

            foreach (var memberShibSubject in memberShipSubjects.ToList())
            {
                RemoveBySubject(graph, memberShibSubject);
            }

            var personSubjects = graph.GetTriplesWithObject(new Uri(personObject)).Select((triple) => triple.Subject);

            foreach (var personSubject in personSubjects.ToList())
            {
                RemoveBySubject(graph, personSubject);
            }

            var hasMember = "http://www.w3.org/ns/org#hasMember";

            foreach (var membershipLink in graph.GetTriplesWithPredicate(new Uri(hasMember)).ToList())
            {
                graph.Retract(membershipLink);
            }

        }

        public IEnumerable<Triple> GetTriplesByPredicate(IGraph graph, Uri predicate)
        {
            INode predicateNode = graph.GetUriNode(predicate);
            IEnumerable<Triple> filteredTriples = graph.GetTriplesWithPredicate(predicateNode);
            return filteredTriples;
        }
    }
}