using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using SecureSharing.Business.Dtos;
using SecureSharing.Business.Interfaces;
using SecureSharing.Data.Data;
using SecureSharing.Infrastructure;
using SecureSharing.Models;

namespace SecureSharing.Controllers;

[Authorize]
public sealed class HomeController : Controller
{
    private const string DefaultPath = "files";
    private const string DefaultPathTmp = "filestmp";
    private readonly ILogger<HomeController> _logger;
    private readonly IMessageService _messageService;
    private readonly IModelFactory _modelFactory;
    private readonly IWebHostEnvironment _webHostEnvironment;
    private readonly IMapper _mapper;

    public HomeController(ILogger<HomeController> logger, IMessageService messageService, IModelFactory modelFactory,
        IWebHostEnvironment webHostEnvironment, IMapper mapper)
    {
        _logger = logger;
        _messageService = messageService;
        _modelFactory = modelFactory;
        _webHostEnvironment = webHostEnvironment;
        _mapper = mapper;
    }

    public IActionResult Index()
    {
        return View();
    }

    [AllowAnonymous]
    public async Task<string> UploadTemporaryFile()
    {
        var code = Guid.NewGuid().ToString();
        var files = Request.Form.Files.ToList();
        var basePath = Path.Combine(_webHostEnvironment.WebRootPath.Split('/')[0], DefaultPathTmp, code);
        Directory.CreateDirectory(basePath);

        foreach (var formFile in files)
        {
            if (formFile.Length <= 0)
                continue;

            var filePath = Path.Combine(basePath, formFile.FileName);
            await using var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
            await formFile.CopyToAsync(stream);
        }

        return code;
    }

    [HttpPost]
    public async Task<IActionResult> CreateMessage(MessageModel model)
    {
        if (!model.AllowEditing
            && string.IsNullOrWhiteSpace(model.Text)
            && model.Files.Count == 0
            && model.FilesAsText == "978682e8-3ce7-4258-b731-d027b5b213aa")
        {
            return Redirect("/");
        }

        model.FilesAsText = model.FilesAsText != "978682e8-3ce7-4258-b731-d027b5b213aa"
            ? model.FilesAsText.Split("978682e8-3ce7-4258-b731-d027b5b213aa")[1]
            : "";

        var message = new MessageDto { Text = model.Text, Anonymous = model.AllowEditing || model.Anonymous, AllowEditing = model.AllowEditing};

        await UploadFiles(model, message);

        var code = await _messageService.Create(message, model.ChosenPeriod);

        return RedirectToAction("Link", "Home", new { code, share = true });
    }

    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> UpdateMessage(MessageModel model)
    {
        if (string.IsNullOrWhiteSpace(model.Text) && model.Files.Count == 0 &&
            model.FilesAsText == "978682e8-3ce7-4258-b731-d027b5b213aa")
        {
            return Redirect("/");
        }

        model.FilesAsText = model.FilesAsText != "978682e8-3ce7-4258-b731-d027b5b213aa"
            ? model.FilesAsText.Split("978682e8-3ce7-4258-b731-d027b5b213aa")[1]
            : "";

        var message = new MessageDto { Text = model.Text, Anonymous = model.Anonymous, Id = model.Id, Code = model.Code};
        // var messageDto = _mapper.Map<MessageDto>(model); 

        await UploadFiles(model, message);

        
        await _messageService.Update(message);

        return RedirectToAction("LinkAnonymous", "Home", new { model.Code, share = true, edit = true });
    }

    [AllowAnonymous]
    private async Task UploadFiles(MessageModel model, MessageDto message)
    {
        var basePath = Path.Combine(_webHostEnvironment.WebRootPath.Split('/')[0], DefaultPath,
            message.Code.ToString());
        var basePathTemporary = Path.Combine(_webHostEnvironment.WebRootPath.Split('/')[0], DefaultPathTmp);
        Directory.CreateDirectory(basePath);

        var directoryNames = model.FilesAsText
            .Split(';')
            .Select(x => x.Split(':')[0])
            .Where(x => !string.IsNullOrEmpty(x))
            .Distinct()
            .ToList();

        var fileNamesTmp = model.FilesAsText
            .Split(';')
            .Where(x => !string.IsNullOrEmpty(x))
            .ToList();
        var fileNames = fileNamesTmp.Select(x => x.Split(':')[1]).ToList();

        foreach (var file in fileNames) message.FileNames.Add(new FileModel { Name = file });

        for (var ind = 0; ind < directoryNames.Count; ind++)
        {
            var directoryName = directoryNames[ind];
            var directoryPath = Path.Combine(basePathTemporary, directoryName);
            var files = Directory.GetFiles(directoryPath);

            for (var i = 0; i < files.Length; i++)
            {
                // var file = files[i];
                var file = fileNames[ind + i];
                var filePath = Path.Combine(directoryPath, file);
                var newFilePath = Path.Combine(basePath, file);
                System.IO.File.Move(filePath, newFilePath);
            }
        }

        foreach (var directory in directoryNames) Directory.Delete(Path.Combine(basePathTemporary, directory), true);

        foreach (var formFile in model.Files)
        {
            if (formFile.Length <= 0)
                continue;

            var filePath = Path.Combine(basePath, formFile.FileName);
            await using var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
            await formFile.CopyToAsync(stream);
            message.FileNames.Add(new FileModel { Name = formFile.FileName });
        }
    }

    [AllowAnonymous]
    public async Task<FileStreamResult> Download(string filename, Guid code)
    {
        var path = Path.Combine(_webHostEnvironment.WebRootPath.Split('/')[0], DefaultPath, code.ToString(), filename);
        var memory = new MemoryStream();
        await using var stream = new FileStream(path, FileMode.Open);
        await stream.CopyToAsync(memory);
        memory.Position = 0;
        new FileExtensionContentTypeProvider().TryGetContentType(filename, out var contentType);
        return contentType is null ? null : File(memory, contentType, Path.GetFileName(path));
    }

    [HttpGet]
    public async Task<IActionResult> Link(Guid code, bool? share, bool? edit)
    {
        var model = await _modelFactory.PrepareLinkVM(code, share, edit);
        return View(model);
    }

    [AllowAnonymous]
    [HttpGet]
    public async Task<IActionResult> LinkAnonymous(Guid code, bool? edit)
    {
        var model = await _modelFactory.PrepareLinkAnonymous(code, edit);
        return View(model);
    }

    [AllowAnonymous]
    [HttpGet]
    public async Task<IActionResult> LinkEdit(Guid code, bool? edit)
    {
        var model = await _modelFactory.PrepareLinkEdit(code, edit);
        return View(model.MessageModel);
    }

    public IActionResult Privacy()
    {
        return View();
    }
}