using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using RR3CommunityServer.Models;
using System.Security.Cryptography;
using static RR3CommunityServer.Data.RR3DbContext;

namespace RR3CommunityServer.Controllers;

[ApiController]
[Route("modding/api")]
public class ModdingController : ControllerBase
{
    private readonly RR3DbContext _context;
    private readonly ILogger<ModdingController> _logger;
    private readonly IConfiguration _configuration;
    private readonly string _customAssetsPath;
    private readonly string _modsPath;

    public ModdingController(RR3DbContext context, ILogger<ModdingController> logger, IConfiguration configuration)
    {
        _context = context;
        _logger = logger;
        _configuration = configuration;
        
        _customAssetsPath = configuration.GetValue<string>("CustomAssetsPath") 
            ?? Path.Combine(Directory.GetCurrentDirectory(), "Assets", "custom");
        
        _modsPath = configuration.GetValue<string>("ModsPath") 
            ?? Path.Combine(Directory.GetCurrentDirectory(), "Assets", "mods");
            
        // Ensure directories exist
        Directory.CreateDirectory(_customAssetsPath);
        Directory.CreateDirectory(_modsPath);
    }

    /// <summary>
    /// Upload a custom car
    /// </summary>
    [HttpPost("cars/upload")]
    [RequestSizeLimit(100_000_000)] // 100 MB max
    public async Task<IActionResult> UploadCustomCar([FromForm] CustomCarUpload upload)
    {
        _logger.LogInformation("Custom car upload: {Name} by {Author}", upload.CarName, upload.AuthorName);

        try
        {
            // Validate files
            if (upload.Model3D == null || upload.Thumbnail == null)
            {
                return BadRequest(new { error = "Model and thumbnail are required" });
            }

            // Generate unique car ID
            var carId = $"custom_{Guid.NewGuid():N}";
            var carFolder = Path.Combine(_customAssetsPath, "cars", carId);
            Directory.CreateDirectory(carFolder);

            // Save files
            var modelPath = await SaveFile(upload.Model3D, carFolder, "model.pak");
            var thumbnailPath = await SaveFile(upload.Thumbnail, carFolder, "thumbnail.png");
            
            string? texturePath = null;
            if (upload.Textures != null)
            {
                texturePath = await SaveFile(upload.Textures, carFolder, "textures.pvr");
            }

            string? audioPath = null;
            if (upload.EngineAudio != null)
            {
                audioPath = await SaveFile(upload.EngineAudio, carFolder, "engine.ogg");
            }

            // Calculate MD5 hashes
            var modelMd5 = await CalculateMd5(modelPath);

            // Create car record
            var customCar = new Car
            {
                CarId = carId,
                Name = upload.CarName,
                Manufacturer = upload.Manufacturer,
                ClassType = upload.ClassType,
                BasePerformanceRating = upload.PerformanceRating,
                Year = upload.Year ?? DateTime.Now.Year,
                CashPrice = upload.CashPrice ?? 0,
                GoldPrice = upload.GoldPrice ?? 0,
                Description = upload.Description,
                IsCustom = true,
                CustomAuthor = upload.AuthorName,
                CustomVersion = upload.Version ?? "1.0",
                CreatedAt = DateTime.UtcNow
            };

            _context.Cars.Add(customCar);

            // Create asset entries
            var modelAsset = new GameAsset
            {
                AssetType = "car_model",
                FileName = "model.pak",
                EaCdnPath = $"/custom/cars/{carId}/model.pak",
                LocalPath = modelPath,
                FileSize = new FileInfo(modelPath).Length,
                Md5Hash = modelMd5,
                ContentType = "application/octet-stream",
                Category = "cars",
                CarId = carId,
                IsCustomContent = true,
                CustomAuthor = upload.AuthorName,
                DownloadedAt = DateTime.UtcNow
            };

            _context.GameAssets.Add(modelAsset);

            // Add thumbnail asset
            var thumbAsset = new GameAsset
            {
                AssetType = "car_thumbnail",
                FileName = "thumbnail.png",
                EaCdnPath = $"/custom/cars/{carId}/thumbnail.png",
                LocalPath = thumbnailPath,
                FileSize = new FileInfo(thumbnailPath).Length,
                Md5Hash = await CalculateMd5(thumbnailPath),
                ContentType = "image/png",
                Category = "ui",
                CarId = carId,
                IsCustomContent = true,
                CustomAuthor = upload.AuthorName,
                DownloadedAt = DateTime.UtcNow
            };

            _context.GameAssets.Add(thumbAsset);

            await _context.SaveChangesAsync();

            _logger.LogInformation("Custom car uploaded successfully: {CarId} - {Name}", carId, upload.CarName);

            return Ok(new
            {
                success = true,
                carId = carId,
                name = upload.CarName,
                message = "Custom car uploaded successfully! It will appear in the catalog.",
                files = new
                {
                    model = modelPath,
                    thumbnail = thumbnailPath,
                    textures = texturePath,
                    audio = audioPath
                }
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error uploading custom car");
            return StatusCode(500, new { error = "Upload failed", details = ex.Message });
        }
    }

    /// <summary>
    /// Upload a custom track
    /// </summary>
    [HttpPost("tracks/upload")]
    [RequestSizeLimit(200_000_000)] // 200 MB max for tracks
    public async Task<IActionResult> UploadCustomTrack([FromForm] CustomTrackUpload upload)
    {
        _logger.LogInformation("Custom track upload: {Name} by {Author}", upload.TrackName, upload.AuthorName);

        try
        {
            if (upload.TrackData == null || upload.Thumbnail == null)
            {
                return BadRequest(new { error = "Track data and thumbnail are required" });
            }

            var trackId = $"custom_{Guid.NewGuid():N}";
            var trackFolder = Path.Combine(_customAssetsPath, "tracks", trackId);
            Directory.CreateDirectory(trackFolder);

            // Save files
            var trackPath = await SaveFile(upload.TrackData, trackFolder, "track.pak");
            var thumbnailPath = await SaveFile(upload.Thumbnail, trackFolder, "thumbnail.png");
            
            string? sceneryPath = null;
            if (upload.Scenery != null)
            {
                sceneryPath = await SaveFile(upload.Scenery, trackFolder, "scenery.pak");
            }

            // Create track record in database (you'll need to add Track entity)
            var trackAsset = new GameAsset
            {
                AssetType = "track",
                FileName = "track.pak",
                EaCdnPath = $"/custom/tracks/{trackId}/track.pak",
                LocalPath = trackPath,
                FileSize = new FileInfo(trackPath).Length,
                Md5Hash = await CalculateMd5(trackPath),
                ContentType = "application/octet-stream",
                Category = "tracks",
                TrackId = trackId,
                IsCustomContent = true,
                CustomAuthor = upload.AuthorName,
                DownloadedAt = DateTime.UtcNow
            };

            _context.GameAssets.Add(trackAsset);
            await _context.SaveChangesAsync();

            return Ok(new
            {
                success = true,
                trackId = trackId,
                name = upload.TrackName,
                message = "Custom track uploaded successfully!",
                files = new
                {
                    track = trackPath,
                    thumbnail = thumbnailPath,
                    scenery = sceneryPath
                }
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error uploading custom track");
            return StatusCode(500, new { error = "Upload failed", details = ex.Message });
        }
    }

    /// <summary>
    /// Get list of all custom content
    /// </summary>
    [HttpGet("content")]
    public async Task<IActionResult> GetCustomContent(
        [FromQuery] string? type = null,
        [FromQuery] string? author = null)
    {
        var query = _context.GameAssets
            .Where(a => a.IsCustomContent);

        if (!string.IsNullOrEmpty(type))
        {
            query = query.Where(a => a.AssetType == type);
        }

        if (!string.IsNullOrEmpty(author))
        {
            query = query.Where(a => a.CustomAuthor == author);
        }

        var content = await query
            .GroupBy(a => new { a.CarId, a.TrackId, a.CustomAuthor })
            .Select(g => new
            {
                id = g.Key.CarId ?? g.Key.TrackId,
                author = g.Key.CustomAuthor,
                type = g.First().AssetType,
                files = g.Select(a => new
                {
                    name = a.FileName,
                    path = a.EaCdnPath,
                    size = a.FileSize,
                    uploadedAt = a.DownloadedAt
                }).ToList()
            })
            .ToListAsync();

        return Ok(new
        {
            success = true,
            count = content.Count,
            content = content.Select(c => new
            {
                id = c.id,
                author = c.author,
                type = c.type,
                files = c.files.ToList()
            }).ToList()
        });
    }

    /// <summary>
    /// Get custom cars list
    /// </summary>
    [HttpGet("cars")]
    public async Task<IActionResult> GetCustomCars()
    {
        var customCars = await _context.Cars
            .Where(c => c.IsCustom)
            .Select(c => new
            {
                carId = c.CarId,
                name = c.Name,
                manufacturer = c.Manufacturer,
                classType = c.ClassType,
                performanceRating = c.BasePerformanceRating,
                year = c.Year,
                author = c.CustomAuthor,
                version = c.CustomVersion,
                description = c.Description,
                cashPrice = c.CashPrice,
                goldPrice = c.GoldPrice,
                createdAt = c.CreatedAt
            })
            .ToListAsync();

        return Ok(new
        {
            success = true,
            count = customCars.Count,
            cars = customCars
        });
    }

    /// <summary>
    /// Delete custom content (moderator only)
    /// </summary>
    [HttpDelete("content/{contentId}")]
    public async Task<IActionResult> DeleteCustomContent(string contentId)
    {
        _logger.LogInformation("Deleting custom content: {ContentId}", contentId);

        // Find all assets for this content
        var assets = await _context.GameAssets
            .Where(a => a.CarId == contentId || a.TrackId == contentId)
            .ToListAsync();

        if (assets.Count == 0)
        {
            return NotFound(new { error = "Content not found" });
        }

        // Delete files
        foreach (var asset in assets)
        {
            if (!string.IsNullOrEmpty(asset.LocalPath) && System.IO.File.Exists(asset.LocalPath))
            {
                System.IO.File.Delete(asset.LocalPath);
            }
        }

        // Delete from database
        _context.GameAssets.RemoveRange(assets);

        // Delete car record if it's a car
        var car = await _context.Cars.FirstOrDefaultAsync(c => c.CarId == contentId);
        if (car != null)
        {
            _context.Cars.Remove(car);
        }

        await _context.SaveChangesAsync();

        return Ok(new { success = true, message = "Content deleted" });
    }

    /// <summary>
    /// Create a mod pack (bundle of custom content)
    /// </summary>
    [HttpPost("modpack/create")]
    public async Task<IActionResult> CreateModPack([FromBody] ModPackRequest request)
    {
        _logger.LogInformation("Creating mod pack: {Name} by {Author}", request.PackName, request.Author);

        var modPackId = $"modpack_{Guid.NewGuid():N}";
        var modPackPath = Path.Combine(_modsPath, modPackId);
        Directory.CreateDirectory(modPackPath);

        // Create mod pack metadata
        var modPack = new ModPack
        {
            PackId = modPackId,
            Name = request.PackName,
            Author = request.Author,
            Description = request.Description,
            Version = request.Version ?? "1.0",
            CarIds = request.CarIds != null ? string.Join(",", request.CarIds) : string.Empty,
            TrackIds = request.TrackIds != null ? string.Join(",", request.TrackIds) : string.Empty,
            CreatedAt = DateTime.UtcNow
        };

        _context.ModPacks.Add(modPack);
        await _context.SaveChangesAsync();

        return Ok(new
        {
            success = true,
            packId = modPackId,
            name = request.PackName,
            message = "Mod pack created! Users can now subscribe to it.",
            downloadUrl = $"/modding/api/modpack/{modPackId}/download"
        });
    }

    /// <summary>
    /// Get available mod packs
    /// </summary>
    [HttpGet("modpacks")]
    public async Task<IActionResult> GetModPacks()
    {
        var packs = await _context.ModPacks
            .ToListAsync();
        
        var result = packs.Select(p => new
            {
                packId = p.PackId,
                name = p.Name,
                author = p.Author,
                description = p.Description,
                version = p.Version,
                carCount = !string.IsNullOrEmpty(p.CarIds) ? p.CarIds.Split(',').Length : 0,
                trackCount = !string.IsNullOrEmpty(p.TrackIds) ? p.TrackIds.Split(',').Length : 0,
                downloads = p.DownloadCount,
                rating = p.Rating,
                createdAt = p.CreatedAt
            })
            .ToList();

        return Ok(new
        {
            success = true,
            count = result.Count,
            modPacks = result
        });
    }

    #region Helper Methods

    private async Task<string> SaveFile(IFormFile file, string folder, string filename)
    {
        var filePath = Path.Combine(folder, filename);
        using var stream = new FileStream(filePath, FileMode.Create);
        await file.CopyToAsync(stream);
        return filePath;
    }

    private async Task<string> CalculateMd5(string filePath)
    {
        using var md5 = MD5.Create();
        using var stream = System.IO.File.OpenRead(filePath);
        var hash = await md5.ComputeHashAsync(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }

    #endregion
}

#region Request Models

public class CustomCarUpload
{
    public IFormFile Model3D { get; set; } = null!;
    public IFormFile Thumbnail { get; set; } = null!;
    public IFormFile? Textures { get; set; }
    public IFormFile? EngineAudio { get; set; }
    
    public string CarName { get; set; } = string.Empty;
    public string Manufacturer { get; set; } = string.Empty;
    public string ClassType { get; set; } = "Custom";
    public int PerformanceRating { get; set; }
    public int? Year { get; set; }
    public int? CashPrice { get; set; }
    public int? GoldPrice { get; set; }
    public string? Description { get; set; }
    public string AuthorName { get; set; } = string.Empty;
    public string? Version { get; set; }
}

public class CustomTrackUpload
{
    public IFormFile TrackData { get; set; } = null!;
    public IFormFile Thumbnail { get; set; } = null!;
    public IFormFile? Scenery { get; set; }
    
    public string TrackName { get; set; } = string.Empty;
    public string Country { get; set; } = string.Empty;
    public string? Description { get; set; }
    public string AuthorName { get; set; } = string.Empty;
    public string? Version { get; set; }
    public double? LengthKm { get; set; }
    public int? Corners { get; set; }
}

public class ModPackRequest
{
    public string PackName { get; set; } = string.Empty;
    public string Author { get; set; } = string.Empty;
    public string? Description { get; set; }
    public string? Version { get; set; }
    public List<string>? CarIds { get; set; }
    public List<string>? TrackIds { get; set; }
}

#endregion
