﻿using GAC_ERP.Infra.IdentityServer.AspNetIdentity;
using GAC_ERP.Infra.IdentityServer.AspNetIdentity.Entities;
using GAC_ERP.Infra.IdentityServer.AspNetIdentity.Stores;
using GAC_ERP.Infra.IdentityServer.IdSrv.Config;
using IdentityServer3.Core;
using IdentityServer3.Core.Configuration;
using IdentityServer3.Core.Models;
using IdentityServer3.Core.Services;
using IdentityServer3.Core.Services.InMemory;
using IdentityServer3.EntityFramework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace GAC_ERP.Infra.IdentityServer.IdSvr.Config.AspNetIdentity {
	
	
	public static class AspNetIdFactory {
		
		/// <summary>
		/// Configura o servico do IdentityServer3 com dados do AspNetIdentity
		/// </summary>
		/// <param name="factory"></param>
		/// <param name="nameOrConnectionString"></param>
		/// <returns></returns>
		public static IdentityServerServiceFactory ConfigureIdSvrFactoryAspNet(this IdentityServerServiceFactory factory, string nameOrConnectionString) {

			var efConfig = new EntityFrameworkServiceOptions { ConnectionString = nameOrConnectionString };


			// TODO: estas tres linhas sao somente para testes devem ser removidas
			ConfigureClients(Clients.Get(), efConfig);
			ConfigureScopes(Scopes.Get(), efConfig);
			ConfigureUsers(Users.Get(), efConfig, new UserManager(new UserStore(new Contexto(nameOrConnectionString))));


			//Configura o banco de dados para armazenar os Clients e Scopes
			factory.RegisterConfigurationServices(efConfig);

			//Configura o banco de dados que armazena os dados operacionais 
			//(Ex.: authorization codes, refresh tokens, reference tokens, e user consent).
			factory.RegisterOperationalServices(efConfig);

			factory.Register(new Registration<Contexto>(resolver => new Contexto(nameOrConnectionString)));
			factory.Register(new Registration<UserStore>());
			factory.Register(new Registration<UserManager>());
			factory.UserService = new Registration<IUserService, AspNetIdUserService>();

			return factory;
		}

		public static void ConfigureClients(IEnumerable<Client> clients, EntityFrameworkServiceOptions options) {
			using(var db = new ClientConfigurationDbContext(options.ConnectionString, options.Schema)) {
				if(!db.Clients.Any()) {
					foreach(var c in clients) {
						var e = c.ToEntity();
						db.Clients.Add(e);
					}
					db.SaveChanges();
				}
			}
		}

		public static void ConfigureScopes(IEnumerable<Scope> scopes, EntityFrameworkServiceOptions options) {
			using(var db = new ScopeConfigurationDbContext(options.ConnectionString, options.Schema)) {
				if(!db.Scopes.Any()) {
					foreach(var s in scopes) {
						var e = s.ToEntity();
						db.Scopes.Add(e);
					}
					db.SaveChanges();
				}
			}
		}

		public static async void ConfigureUsers(IEnumerable<InMemoryUser> users, EntityFrameworkServiceOptions options, UserManager userManager) {
			using(var db = new Contexto(options.ConnectionString)) {
				
				if(!db.Users.Any()) {
					
					foreach(var u in users) {
						var entity = new Usuario { Email = u.Claims.First(x=>x.Type==Constants.ClaimTypes.Email).Value , UserName = u.Username };
						var response = await userManager.CreateAsync(entity, u.Password);
						
						if(!response.Succeeded) {
							throw new Exception("Não foi possível criar o usuario" + u.Username + response.Errors.ToString());
						}
						else {
							
							var user = await userManager.FindAsync(u.Username,u.Password);
							foreach(var c in u.Claims) {

								await userManager.AddClaimAsync(user.Id,c);
							}
														
							await userManager.UpdateAsync(user);
						}
					}
					db.SaveChanges();
				}
			}
		}
	}
}
