using System.Diagnostics;
using System.Threading.Tasks;
using Db.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TagVid.Helpers;
using TagVid.Models;

namespace TagVid.Controllers;

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly AdapterContext _adapterContext;

    public HomeController(ILogger<HomeController> logger, AdapterContext adapterContext)
    {
        _logger = logger;
        _adapterContext = adapterContext;
    }

    public async Task<IActionResult> Index()
    {
        // check if the video path was set
        var pathSettings = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
        if (string.IsNullOrEmpty(pathSettings))
        {
            _logger.LogWarning("Video path is not set. Please configure the video path in settings.");
            return RedirectToAction("Settings", "Home");
        }
        return View();
    }

    [HttpGet]
    [Route("Home/Index/{id}")]
    public async Task<IActionResult> Index(string id)
    {
        throw new NotImplementedException();
    }    

    public async Task<IActionResult> Settings()
    {
        var vm = new SettingsViewModel
        {
            VideoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath)
        };

        return View(vm);
    }

    [HttpPost]
    public async Task<IActionResult> Settings(SettingsViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // Validate the video path
        if (string.IsNullOrWhiteSpace(model.VideoPath))
        {
            ModelState.AddModelError(nameof(model.VideoPath), "Video path cannot be empty.");
            return View(model);
        }

        // Check if the path exists
        if (!Directory.Exists(model.VideoPath))
        {
            ModelState.AddModelError(nameof(model.VideoPath), "The specified video path does not exist.");
            return View(model);
        }

        // Check if the path is a directory
        if (!System.IO.File.GetAttributes(model.VideoPath).HasFlag(FileAttributes.Directory))
        {
            ModelState.AddModelError(nameof(model.VideoPath), "The specified path must be a directory.");
            return View(model);
        }

        // Save settings to the database
        await _adapterContext.SetSettingAsync(SettingName.VideoPath, model.VideoPath);
        return View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Upload(IFormFile videoFile)
    {
        _logger.LogInformation("Upload called with file: {FileName}", videoFile?.Name);
        if (videoFile == null || videoFile.Length == 0)
        {
            return RedirectToAction("Index");
        }

        // Validate the video file
        // no size limit for now, but could be added later

        if (!videoFile.ContentType.StartsWith("video/"))
        {
            return RedirectToAction("Index");
        }

        // Save the video file to the configured path
        var videoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
        var ext = Path.GetExtension(videoFile.FileName);

        if (string.IsNullOrEmpty(videoPath) || !Directory.Exists(videoPath))
        {
            return RedirectToAction("Settings");
        }

        // asp.net docu suggests not using .FileName due to security reasons
        var id = Guid.NewGuid().ToString();

        var filePath = Path.Combine(videoPath, $"{id}{ext}");
        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await videoFile.CopyToAsync(stream);
        }

        var data = await new FFProbeHelper<HomeController>(_logger).GetVideoMetadataAsync(filePath);

        var video = new Video
        {
            Id = id,
            Duration = data?.Duration ?? TimeSpan.Zero,
            Width = data?.Width ?? 0,
            Height = data?.Height ?? 0,
            Extension = ext,
            VideoTags = []
        };

        _adapterContext.Videos.Add(video);
        await _adapterContext.SaveChangesAsync();

        // redirect to the video details page 
        return RedirectToAction("Details", "Home", new { id = video.Id });
    }

    public async Task<IActionResult> Details(string id)
    {
        if (string.IsNullOrEmpty(id))
        {
            return NotFound();
        }

        var video = await _adapterContext.Videos
            .Include(v => v.VideoTags)
                .ThenInclude(vt => vt.Tag)
            .FirstOrDefaultAsync(v => v.Id == id);

        if (video == null)
        {
            return NotFound();
        }

        var vm = new DetailsViewModel
        {
            VideoId = id,
            Title = video?.Title ?? string.Empty,
            Tags = video?.VideoTags.Select(vt => vt.Tag.Name).ToList() ?? []
        };
        
        return View(vm);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Details(DetailsViewModel videoModel)
    {
        if (string.IsNullOrEmpty(videoModel.VideoId))
        {
            return NotFound();
        }

        var existingVideo = await _adapterContext.Videos
            .Include(v => v.VideoTags)
                .ThenInclude(vt => vt.Tag)
            .FirstOrDefaultAsync(v => v.Id == videoModel.VideoId);

        if (existingVideo == null)
        {
            return NotFound();
        }

        // Update video title
        existingVideo.Title = videoModel.Title;
        // Update tags
        existingVideo.VideoTags.Clear();
        foreach (var tagName in videoModel.Tags)
        {
            var tag = await _adapterContext.Tags.FirstOrDefaultAsync(t => t.Name == tagName);
            if (tag == null)
            {
                tag = new Tag { Name = tagName };
                _adapterContext.Tags.Add(tag);
            }
            existingVideo.VideoTags.Add(new VideoTag { Video = existingVideo, Tag = tag });
        }
        _adapterContext.Videos.Update(existingVideo);
        await _adapterContext.SaveChangesAsync();
        
        return RedirectToAction("Details", new { id = videoModel.VideoId });
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Delete(string id)
    {
        if (string.IsNullOrEmpty(id))
        {
            return NotFound();
        }   
        var video = await _adapterContext.Videos.FindAsync(id);
        if (video == null)
        {
            return NotFound();
        }
        // Delete the video file from the file system
        var videoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
        if (!string.IsNullOrEmpty(videoPath))
        {
            var filePath = Path.Combine(videoPath, $"{video.Id}{video.Extension}");
            if (System.IO.File.Exists(filePath))
            {
                System.IO.File.Delete(filePath);
            }
        }
        // Remove the video from the database
        _adapterContext.Videos.Remove(video);
        await _adapterContext.SaveChangesAsync();
        
        return RedirectToAction("Index");
    }

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
}
