using Fast.Shared.Logic.FileService;

namespace Fast.Web.Controllers;

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class TemplateController : ODataController
{
    private readonly MyDbContext db;
    private readonly AuthorizationHelper authHelper;
    private readonly IWebHostEnvironment env;

    private static readonly Dictionary<string, string> templateFolderPaths = new();
    private static IFileService? fileService;
    private static Task? initializationTask;
    private static readonly Lock lockObj = new();

    // Template directories to search for
    private static readonly List<string> templateFolderNames = new()
    {
        "ContractTemplates",
        "DealTemplates",
        "InvoiceTemplates"
    };

    public TemplateController(MyDbContext context, IWebHostEnvironment env)
    {
        db = context;
        this.env = env;
        authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);

        StartInitializationIfNeeded();
    }

    /// <summary>
    /// Kicks off the initialization task in the background if not already started.
    /// </summary>
    private void StartInitializationIfNeeded()
    {
        if (initializationTask != null) return;
        lock (lockObj)
        {
            if (initializationTask != null) return;

            var useLocalFiles = Main.UseLocalFiles;
            var contentRootPath = env.ContentRootPath;
            var envType = useLocalFiles
                ? FolderResolver.DetectEnvironmentType(db, env.IsDevelopment())
                : EnvType.Unknown;

            // Run folder resolution in the background
            initializationTask = Task.Run(() => InitializeFileService(useLocalFiles, contentRootPath, envType));
        }
    }

    private static void InitializeFileService(bool useLocalFiles, string contentRootPath, EnvType envType)
    {
        if (useLocalFiles)
        {
            var folderPaths = FolderResolver.ResolveFolderPaths(
                contentRootPath,
                envType,
                templateFolderNames,
                throwOnNotFound: false);

            foreach (var kvp in folderPaths)
                templateFolderPaths.TryAdd(kvp.Key, kvp.Value);
        }
        else
        {
            foreach (var folder in templateFolderNames)
                templateFolderPaths.TryAdd(folder, folder);
        }

        var config = new FileServiceConfig(contentRootPath);
        config.LocalFolderMappings = templateFolderPaths;
        fileService = new FileService(config);
    }

    /// <summary>
    /// Gets the file service, waiting for initialization to complete if necessary.
    /// </summary>
    private static async Task<IFileService> GetFileServiceAsync()
    {
        if (initializationTask != null)
            await initializationTask;

        return fileService!;
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> GetRequiredData()
    {
        var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Template", PermissionType.Modify);
        return Ok(new { hasModifyPermission });
    }

    [Permission("Template", PermissionType.View)]
    [Route("/odata/GetTemplateItems")]
    public IActionResult GetItems(ODataQueryOptions<TemplateListItem> queryOptions, bool isExport)
    {
        queryOptions = Util.GetQueryOptionsWithConvertedDates(queryOptions);
        var itemsQueryable = GetItemsInternal();
        var items = (queryOptions.ApplyTo(itemsQueryable) as IEnumerable<TemplateListItem>)?.ToList();

        if (isExport)
            return File(Util.Excel.GetExportFileStream(items), "application/octet-stream");
        else
            return Ok(items);
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]")]
    public IQueryable<TemplateListItem> GetItemsInternal()
    {
        var itemsQueryable = (
            from t in db.Templates
            select new TemplateListItem
            {
                Id = t.Id,
                Name = t.Name,
                Directory = t.Directory,
                FileName = t.FileName
            }
        ).OrderBy(t => t.Directory).ThenBy(t => t.Name).AsNoTracking();

        return itemsQueryable;
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> GetTemplates()
    {
        var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Template", PermissionType.Modify);
        var templates = await GetItemsInternal().ToListAsync();
        return Ok(new { templates, hasModifyPermission });
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> ExportTemplates()
    {
        var templates = await GetItemsInternal().ToListAsync();
        return File(Util.Excel.GetExportFileStream(templates), "application/octet-stream", "Templates.xlsx");
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]/{id}")]
    public async Task<IActionResult> GetDetail(int id)
    {
        var detail = await db.Templates
            .Where(t => t.Id == id)
            .Select(t => new TemplateListItem
            {
                Id = t.Id,
                Name = t.Name,
                Directory = t.Directory,
                FileName = t.FileName
            })
            .AsNoTracking()
            .FirstAsync();

        return Ok(detail);
    }

    [Permission("Template", PermissionType.View)]
    [Route("[action]/{id}")]
    public async Task<IActionResult> DownloadTemplate(int id)
    {
        var template = await db.Templates.FindAsync(id);
        if (template == null)
            return NotFound("Template not found");

        // Wait for initialization to complete before checking folder paths
        var svc = await GetFileServiceAsync();

        if (!templateFolderPaths.ContainsKey(template.Directory))
            return NotFound($"Template folder '{template.Directory}' not found");

        var downloadResult = await svc.DownloadFileAsync(template.Directory, template.FileName, useSharding: false);
        if (!downloadResult.Success)
            return NotFound($"Template file not found: {template.FileName}");

        var mimeType = Util.GetMimeType(template.FileName);
        return File(downloadResult.Stream, mimeType, template.FileName);
    }

    [Permission("Template", PermissionType.Modify)]
    [Route("[action]/{id}")]
    public async Task<IActionResult> UploadTemplate(int id, IFormFile file)
    {
        var template = await db.Templates.FindAsync(id);
        if (template == null)
            return NotFound("Template not found");

        // Wait for initialization to complete before checking folder paths
        var svc = await GetFileServiceAsync();

        if (!templateFolderPaths.ContainsKey(template.Directory))
            return NotFound($"Template folder '{template.Directory}' not found");

        var result = await svc.UploadFileAsync(template.Directory, template.FileName, file, useSharding: false);
        if (result == null)
            return BadRequest("Failed to upload template file");

        return Ok(new { success = true, message = "Template uploaded successfully" });
    }
}

public class TemplateListItem
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Directory { get; set; } = "";
    public string FileName { get; set; } = "";
}
