using AsbCloudApp.Data.User;
using AsbCloudWebApi.Converters;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudWebApi.Controllers.Interfaces;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Mvc;

public static class Extensions
{
    public static int? GetCompanyId(this ClaimsPrincipal user)
    {
        var claimIdCompany = user.FindFirst(nameof(UserDto.IdCompany));
        if (claimIdCompany is null)
            return null;

        return int.TryParse(claimIdCompany.Value, out int uid)
            ? uid
            : null;
    }

    public static int? GetUserId(this ClaimsPrincipal user)
    {
        var userId = user.FindFirst(nameof(UserDto.Id));
        if (userId is null)
            return null;

        return int.TryParse(userId.Value, out int uid)
            ? uid
            : null;
    }

    /// <summary>
    /// <para>
    /// Returns BadRequest with ValidationProblemDetails as body
    /// </para>
    /// <para>
    /// Используйте этот метод только если валидацию нельзя сделать через 
    /// атрибуты валидации или IValidatableObject модели.
    /// </para>
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="paramName"></param>
    /// <param name="error"></param>
    /// <returns></returns>
    public static BadRequestObjectResult ValidationBadRequest(this ControllerBase controller, string paramName, string error)
    {
        return MakeBadRequestObjectResult(paramName, error);
    }

    private static BadRequestObjectResult MakeBadRequestObjectResult(string paramName, string error)
    {
        var errors = new Dictionary<string, string[]> {
            { paramName, new[]{ error } }
        };
        var problem = new ValidationProblemDetails(errors);
        var badRequestObject = new BadRequestObjectResult(problem);
        return badRequestObject;
    }

    /// <summary>
    /// <para>
    /// Returns BadRequest with ValidationProblemDetails as body
    /// </para>
    /// <para>
    /// Используйте этот метод только если валидацию нельзя сделать через 
    /// атрибуты валидации или IValidatableObject модели.
    /// </para>
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="validationResults"></param>
    /// <returns></returns>
    public static BadRequestObjectResult ValidationBadRequest(this ControllerBase controller, IEnumerable<ValidationResult> validationResults)
    {
        var errors = validationResults
            .SelectMany(e => e.MemberNames.Select(name => new { name, e.ErrorMessage }))
            .GroupBy(e => e.name)
            .ToDictionary(e => e.Key, e => e.Select(el => el.ErrorMessage ?? string.Empty).ToArray());

        var problem = new ValidationProblemDetails(errors);
        return controller.BadRequest(problem);
    }

    public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options)
    {
        TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
        return options;
    }
    
    /// <summary>
    /// Вызов парсера со стандартной валидацией входного файла
    /// </summary>
    /// <typeparam name="TDto"></typeparam>
    /// <typeparam name="TOptions"></typeparam>
    /// <param name="controller"></param>
    /// <param name="files"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static ActionResult<ParserResultDto<TDto>> ParseExcelFile<TDto, TOptions>(
        this IControllerWithParser<TDto, TOptions> controller,
        IFormFileCollection files,
        TOptions options)
        where TDto : class, IId
        where TOptions : class, IParserOptionsRequest
    {
        if (files.Count < 1)
            return MakeBadRequestObjectResult(nameof(files), "Нет файла");

        var file = files[0];
        if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
            return MakeBadRequestObjectResult(nameof(files), "Требуется .xlsx файл.");

        var stream = file.OpenReadStream();

        return controller.Parse(stream, options);
    }
}