﻿using HealthAndFitness.Api.Classes;
using HealthAndFitness.Api.Data;
using HealthAndFitness.Api.Data.Entities;
using HealthAndFitness.Api.Tests;
using Microsoft.EntityFrameworkCore;
using System;
using System.Threading.Tasks;
using Zel.Testing;

namespace HealthAndFitness.Api.Integration
{
    public class IntegrationFixture : ApiTestBase, IDisposable
    {
        private string _connectionString;
        private bool _inMemory;
        public Guid UserId;
        public Guid UserToken;

        public IntegrationFixture() : base(typeof(IntegrationStartup))
        {
            var context = (HealthAndFitnessContext)Server.Host.Services
                .GetService(typeof(HealthAndFitnessContext));
            _inMemory = context.Database.IsInMemory();
            if (context.Database.IsSqlServer())
            {
                _connectionString = context.Database.GetDbConnection().ConnectionString;
            }

            SetUserAndToken();
            Client.DefaultRequestHeaders.Add("Authorization", $"Bearer {UserToken.ToString()}");
        }

        public async Task<Exercise> CreateDistanceExerciseAsync()
        {
            return await CreateExerciseAsync(Constants.ExerciseTypes.DISTANCE);
        }

        public async Task<Exercise> CreateRepetitionExerciseAsync()
        {
            return await CreateExerciseAsync(Constants.ExerciseTypes.REPETITION);
        }

        public async Task<ExerciseGoal> CreateWeightExerciseGoalEntryAsync()
        {
            var exercise = await CreateWeightExerciseAsync();
            return await CreateExerciseGoalEntryAsync(exercise, Constants.WeightGoalTypes.WEIGHT_X_REPETITIONS,
                null, null, 2, 30);
        }

        public async Task<Exercise> CreateWeightExerciseAsync()
        {
            return await CreateExerciseAsync(Constants.ExerciseTypes.WEIGHT);
        }

        public async Task<ExerciseLog> CreateWeightExerciseLogEntryAsync()
        {
            var exercise = await CreateWeightExerciseAsync();
            return await CreateExerciseLogEntryAsync(exercise, 100, 8, null, null);
        }

        public HealthAndFitnessContext GetContext()
        {
            DbContextOptionsBuilder<HealthAndFitnessContext> dbContextOptionsBuilder = null;

            if (_inMemory)
            {
                dbContextOptionsBuilder = new DbContextOptionsBuilder<HealthAndFitnessContext>()
                 .UseInMemoryDatabase("HealthAndFitness");
            }
            else
            {
                dbContextOptionsBuilder = new DbContextOptionsBuilder<HealthAndFitnessContext>()
                    .UseSqlServer(_connectionString, x => x.EnableRetryOnFailure());
            }

            return new HealthAndFitnessContext(dbContextOptionsBuilder.Options);
        }

        private async Task<Exercise> CreateExerciseAsync(string type)
        {
            using (var context = GetContext())
            {
                var user = await context.Users.FirstOrDefaultAsync(x => x.Id == UserId);
                var exercise = new Exercise
                {
                    Name = RandomString(10),
                    Type = type,
                    User = user,
                    CreatedOn = DateTime.UtcNow,
                    ModifiedOn = DateTime.UtcNow
                };
                await context.Exercises.AddAsync(exercise);
                await context.SaveChangesAsync();
                return exercise;
            }
        }

        private async Task<ExerciseGoal> CreateExerciseGoalEntryAsync(Exercise exercise, string type, int? weight,
            int? repetitions, decimal? distance, int? duration)
        {
            using (var context = GetContext())
            {
                context.Attach(exercise);
                var goal = new ExerciseGoal
                {
                    Type = type,
                    Weight = weight,
                    Repetitions = repetitions,
                    Distance = distance,
                    Duration = duration,
                    Exercise = exercise,
                    ModifiedOn = DateTime.UtcNow,
                    CreatedOn = DateTime.UtcNow
                };

                await context.ExerciseGoals.AddAsync(goal);
                await context.SaveChangesAsync();
                return goal;
            }
        }

        private async Task<ExerciseLog> CreateExerciseLogEntryAsync(Exercise exercise, int? weight,
            int? repetitions, decimal? distance, int? duration)
        {
            using (var context = GetContext())
            {
                context.Attach(exercise);
                var logEntry = new ExerciseLog
                {
                    Date = DateTime.UtcNow,
                    Weight = weight,
                    Repetitions = repetitions,
                    Distance = distance,
                    Duration = duration,
                    Exercise = exercise,
                    ModifiedOn = DateTime.UtcNow,
                    CreatedOn = DateTime.UtcNow
                };

                await context.ExerciseLogs.AddAsync(logEntry);
                await context.SaveChangesAsync();
                return logEntry;
            }
        }

        private void SetUserAndToken()
        {
            var user = new User
            {
                CreatedOn = DateTime.UtcNow,
                Email = RandomString(10),
                FirstName = RandomString(10),
                LastName = RandomString(10),
                ModifiedOn = DateTime.UtcNow,
                Password = "password",
                Username = RandomString(10)
            };
            var userToken = new UserToken
            {
                ExpiresOn = DateTime.UtcNow.AddDays(1),
                User = user
            };

            using (var context = GetContext())
            {
                context.Add(user);
                context.Add(userToken);
                context.SaveChanges();
            }

            UserId = user.Id;
            UserToken = userToken.Id;
        }
    }
}