using System.Runtime.Serialization.Json;
using Fast.Shared.Logic.FileService;
using Fast.Web.Models;
using Microsoft.AspNetCore.OData.Formatter;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using Microsoft.Extensions.FileProviders;

namespace Fast.Web.Controllers;

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SalesTaxExemptionController : ODataController
{
    private readonly MyDbContext db;
    private readonly AuthorizationHelper authHelper;
    private readonly FileService fileService;

    public SalesTaxExemptionController(MyDbContext context, IWebHostEnvironment env)
    {
        db = context;
        authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);
        var config = new FileServiceConfig(env.ContentRootPath);
        fileService = new FileService(config);
    }

    [Permission("Sales Tax Exemption", PermissionType.View)]
    [Route("/odata/GetSalesTaxExemptionItems")]
    public IActionResult GetItems(ODataQueryOptions<SalesTaxExemptionListItem> queryOptions, bool isExport)
    {
        var itemsQueryable = GetItemsInternal();
        var items = (queryOptions.ApplyTo(itemsQueryable) as IEnumerable<SalesTaxExemptionListItem>)?.ToList();

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

    [Permission("Sales Tax Exemption", PermissionType.View)]
    [Route("[action]")]
    public IQueryable<SalesTaxExemptionListItem> GetItemsInternal()
    {
        IQueryable<SalesTaxExemptionListItem>? itemsQueryable = null;

        itemsQueryable = (
            from q in db.SalesTaxExemptions
            select new SalesTaxExemptionListItem
            {
                SalesTaxExemptionId = q.Id,
                Company = q.Company.Name,
                State = q.State.Name ?? "",
                EffectiveDate = q.EffectiveDate,
                ExpirationDate = q.ExpirationDate
            }
        ).AsNoTracking();

        return itemsQueryable;
    }

    [Permission("Sales Tax Exemption", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> GetRequiredData()
    {
        var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Sales Tax Exemption", PermissionType.Modify);

        var companies = (await DataHelper.GetCounterpartiesAsync(false, Enums.ProductCategory.All)).Select(x => new IdName(x.EntityId, x.FullName));

        var states = await DataHelper.GetTerritoriesAsync();

        var result = new { hasModifyPermission, companies, states };
        return Ok(result);
    }

    [Permission("Sales Tax Exemption", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> GetDetail(int id)
    {
        var detail = await (
            from q in db.SalesTaxExemptions
            where q.Id == id
            select new SalesTaxExemptionDetail
            {
                SalesTaxExemptionId = q.Id,
                CompanyId = q.CompanyId,
                StateId = q.StateId,
                EffectiveDate = q.EffectiveDate,
                ExpirationDate = q.ExpirationDate,
                Certificates = q.SalesTaxExemptionDocs.Select(x => new DocItem { FileNameOriginal = x.FileNameOriginal, FileNameOnDisk = x.FileNameOnDisk }).ToList(),
                Notes = q.Notes,
            }
        ).AsNoTracking().FirstAsync();

        return Ok(detail);
    }

    public enum SaveType
    {
        New = 1,
        Normal = 2
    }

    [Permission("Sales Tax Exemption", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> SaveDetail(SalesTaxExemptionDetail detail, SaveType saveType)
    {
        int resultId = 0;
        DateOnly noEndDate = new(9999, 12, 31);

        await db.Database.CreateExecutionStrategy().Execute(async () =>
        {
            using var dbContextTransaction = await db.Database.BeginTransactionAsync();
            SalesTaxExemption? dbItem = null;
            if (saveType != SaveType.New)
            {
                dbItem = await (
                    from q in db.SalesTaxExemptions
                    .Include(x => x.SalesTaxExemptionDocs)
                    where q.Id == detail.SalesTaxExemptionId
                    select q
                ).FirstOrDefaultAsync();
            }

            if (dbItem == null) //if the item does not exist then add it
            {
                dbItem = new SalesTaxExemption();
                db.SalesTaxExemptions.Add(dbItem);
            }
            else
            {
                //remove existing items so that they get completely re-inserted
                db.SalesTaxExemptionDocs.RemoveRange(dbItem.SalesTaxExemptionDocs);
            }

            var d = detail;

            dbItem.CompanyId = d.CompanyId;
            dbItem.StateId = d.StateId;
            dbItem.EffectiveDate = d.EffectiveDate;
            dbItem.ExpirationDate = d.ExpirationDate ?? noEndDate;
            dbItem.Notes = d.Notes;

            dbItem.SalesTaxExemptionDocs = d.Certificates?.Select(x => new SalesTaxExemptionDoc
            {
                FileNameOriginal = x.FileNameOriginal,
                FileNameOnDisk = x.FileNameOnDisk
            }).ToList() ?? new List<SalesTaxExemptionDoc>();

            await db.SaveChangesAsync();
            resultId = dbItem.Id;

            await dbContextTransaction.CommitAsync();
        });

        return Ok(resultId);
    }

    [Permission("Sales Tax Exemption", PermissionType.Modify)]
    [Route("[action]/{id}")]
    public async Task<IActionResult> DeleteDetail(int id)
    {
        SalesTaxExemption dbItem = await db.SalesTaxExemptions.Where(x => x.Id == id).FirstAsync();
        db.SalesTaxExemptions.Remove(dbItem);
        await db.SaveChangesAsync();

        return Ok();
    }

    [Permission("Sales Tax Exemption", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> UploadDoc(IEnumerable<IFormFile> files, [FromODataUri] string metaData)
    {
        var docItems = new List<DocItem>();

        // Deserialize the metadata to extract the original filename details
        using MemoryStream ms = new(Encoding.UTF8.GetBytes(metaData));
        var serializer = new DataContractJsonSerializer(typeof(ChunkMetaData));
        if (serializer.ReadObject(ms) is not ChunkMetaData chunkData)
            return BadRequest("Invalid metadata.");

        string? fileNameOnDisk = null;

        if (files != null)
        {
            foreach (var file in files)
            {
                if (file.Length > 0 && chunkData != null)
                {
                    var newFileName = Util.String.GetNewGuid() + Path.GetExtension(chunkData.FileName);
                    fileNameOnDisk = await fileService.UploadFileAsync("SalesTaxExemptionCertificates", newFileName, file);
                }
            }
        }

        if (fileNameOnDisk != null && chunkData != null && chunkData.ChunkIndex == chunkData.TotalChunks - 1)
        {
            docItems.Add(new DocItem
            {
                FileNameOriginal = chunkData.FileName,
                FileNameOnDisk = fileNameOnDisk
            });
            return Ok(docItems);
        }

        return Ok();
    }

    [Permission("Sales Tax Exemption", PermissionType.View)]
    [Route("[action]")]
    public async Task<IActionResult> DownloadDoc(string fileNameOnDisk)
    {
        try
        {
            var downloadResponse = await fileService.DownloadFileAsync("SalesTaxExemptionCertificates", fileNameOnDisk);

            return File(downloadResponse.Stream, downloadResponse.ContentType, downloadResponse.FileName);
        }
        catch (Exception ex)
        {
            var prefix = "SalesTaxExemption";
            var bytes = Util.GetExceptionFilesBytes(ex, prefix);
            return File(bytes, "text/plain");
        }
    }

}
