using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using pavesys_iRAP.DTO;
using pavesys_iRAP.Helper;
using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;

namespace pavesys_iRAP.Business
{
    /// <summary>
    /// Export e Import dos projetos da aplicação irap pavesys
    /// </summary>
    internal class ExportImportHandler
    {
        public enum CamFolderOption
        {
            OptionExport,
            OptionImport,
        }
        private Project project;
        private List<Coding> codingList;
        private List<CodingImage> codingImagesList;



        private static string SanitizePath(string path)
        {
            return path.Replace(".", ",").Replace("\\", "");
        }

        private static async Task CopyFileAsync(string sourceFile, string destinationFile)
        {
            using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
            using (var destinationStream = new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
                await sourceStream.CopyToAsync(destinationStream);
        }
        public static async Task<string> OutputCamFolders(List<CodingImage> codingImagesList, CamFolderOption ExportImport, Project project)
        {
            string exportFolderPath;
            if (ExportImport == CamFolderOption.OptionExport)
            {
                exportFolderPath = Path.Combine(ConfigManager.GetExportPath(), $"{SanitizePath(project.Section)}-{project.KmStart}-{project.KmEnd}");
                //Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                //"Pavesys", "iRap", "Export", $"{SanitizePath(project.Section)}-{project.KmStart}-{project.KmEnd}");
            }
            else // Importing Project
            {

                exportFolderPath =
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                    "Pavesys", "iRap", "Projects", $"{SanitizePath(project.Section)}-{project.KmStart}-{project.KmEnd}-" +
                    $"{CrudHelper.FindCodingsByProject((int)project.ProjectId, false).Select(coding => coding.RoadSurveyDate).First().Value.ToString("dd-MM-yyyy", CultureInfo.InvariantCulture)}");
            }
            Directory.CreateDirectory(Path.Combine(exportFolderPath, "CAM 1"));
            Directory.CreateDirectory(Path.Combine(exportFolderPath, "CAM 2"));
            foreach (CodingImage image in codingImagesList)
            {
                if (!Path.Exists(Path.Combine(project.ProjectPath, "CAM 1", image.ImageName)) && !Path.Exists(Path.Combine(project.ProjectPath, "CAM 2", image.ImageName)))
                    break;

                if (image.Type == 1 && Path.Exists(Path.Combine(project.ProjectPath, "CAM 1", image.ImageName)))
                {
                    await CopyFileAsync(Path.Combine(project.ProjectPath, "CAM 1", image.ImageName), Path.Combine(exportFolderPath, "CAM 1", image.ImageName));
                }
                else if (image.Type == 2 && Path.Exists(Path.Combine(project.ProjectPath, "CAM 2", image.ImageName)))
                {
                    await CopyFileAsync(Path.Combine(project.ProjectPath, "CAM 2", image.ImageName), Path.Combine(exportFolderPath, "CAM 2", image.ImageName));
                }
            }
            return exportFolderPath;
        }


        public async Task ExecuteExport(int projID)
        {
            project = CrudHelper.GetProjectById(projID);
            codingList = await CrudHelper.LoadProjectCodings(projID);
            codingImagesList = await CrudHelper.FindCodingImagesByProjectId(projID);

            // Define the export folder path
            string exportFolderPath =
                Path.Combine(ConfigManager.GetExportPath(), $"{SanitizePath(project.Section)}-{project.KmStart}-{project.KmEnd}");
            // Create the export folder if it doesn't exist
            if (!Directory.Exists(exportFolderPath))
            {
                Directory.CreateDirectory(exportFolderPath);
            }
            Debug.WriteLine("Export Project");

            // Serialize and save the project data to a JSON file
            string projectJson = JsonConvert.SerializeObject(project, Formatting.Indented);
            string projectFilePath = Path.Combine(exportFolderPath, "project.json");
            await File.WriteAllTextAsync(projectFilePath, projectJson);
            Debug.WriteLine("Export Coding");

            // Serialize and save the coding list to a JSON file
            string codingListJson = JsonConvert.SerializeObject(codingList, Formatting.Indented);
            string codingListFilePath = Path.Combine(exportFolderPath, "codingList.json");
            await File.WriteAllTextAsync(codingListFilePath, codingListJson);
            Debug.WriteLine("Export Images");
            // Serialize and save the coding images list to a JSON file
            string codingImagesListJson = JsonConvert.SerializeObject(codingImagesList, Formatting.Indented);
            string codingImagesListFilePath = Path.Combine(exportFolderPath, "codingImagesList.json");
            await File.WriteAllTextAsync(codingImagesListFilePath, codingImagesListJson);
            Debug.WriteLine("Export Photos");
            await OutputCamFolders(codingImagesList, CamFolderOption.OptionExport, project);
            Debug.WriteLine("Acabou Export");
            ZipFile.CreateFromDirectory(exportFolderPath, exportFolderPath + ".zip");
            Directory.Delete(exportFolderPath, true);
        }

        public async Task<Project?> Import(string zipFilePath)
        {
            // Define the path for the temporary extraction folder in ProgramData
            string tempFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "tempiRAP");
            CrudHelper.RefreshContext();


            try
            {
                // Ensure the temporary directory exists
                if (Directory.Exists(tempFolderPath))
                {
                    Directory.Delete(tempFolderPath, true);
                }
                Directory.CreateDirectory(tempFolderPath);

                // Extract the zip file to the temporary directory
                await Task.Run(() => ZipFile.ExtractToDirectory(zipFilePath, tempFolderPath));

                // Define the import folder path
                string importFolderPath = tempFolderPath;

                // Read and deserialize the project data from the JSON file
                string projectFilePath = Path.Combine(importFolderPath, "project.json");
                string projectJson = await File.ReadAllTextAsync(projectFilePath);
                var project = JsonConvert.DeserializeObject<Project>(projectJson);
                project.ProjectId = null;
                await CrudHelper.dbCtx.AddAsync(project);
                await CrudHelper.dbCtx.SaveChangesAsync();

                Debug.WriteLine($"Saved Project {project.ProjectId}");
                var codingIdMap = new Dictionary<int?, int?>();
                var newCodingList = new List<Coding>();

                // Read and deserialize the coding list from the JSON file
                string codingListFilePath = Path.Combine(importFolderPath, "codingList.json");
                string codingListJson = await File.ReadAllTextAsync(codingListFilePath);
                var codingList = JsonConvert.DeserializeObject<List<Coding>>(codingListJson);
                Debug.WriteLine(CrudHelper.dbCtx.Codings.Count());
                foreach (var coding in codingList)
                {
                    string jsonToCopy = JsonConvert.SerializeObject(coding);
                    var newCoding = JsonConvert.DeserializeObject<Coding>(jsonToCopy);
                    newCoding.CodingId = null;

                    newCoding.ProjectId = (int)project.ProjectId;
                    await CrudHelper.dbCtx.AddAsync(newCoding);
                    await CrudHelper.dbCtx.SaveChangesAsync();
                    codingIdMap[coding.CodingId] = newCoding.CodingId;
                    CrudHelper.RefreshContext();
                }
                Debug.WriteLine(CrudHelper.dbCtx.Codings.Count());

                Debug.WriteLine("Saved Codings");

                // Read and deserialize the coding images list from the JSON file
                string codingImagesListFilePath = Path.Combine(importFolderPath, "codingImagesList.json");
                string codingImagesListJson = await File.ReadAllTextAsync(codingImagesListFilePath);
                var codingImagesList = JsonConvert.DeserializeObject<List<CodingImage>>(codingImagesListJson);
                int counter = 0;
                foreach (var codingImage in codingImagesList)
                {
                    int? tempCodingId = null;
                    codingIdMap.TryGetValue(codingImage.CodingId, out tempCodingId);
                    if (tempCodingId != null)
                    {
                        codingImage.CodingId = codingIdMap[codingImage.CodingId];
                        codingImage.Coding = await CrudHelper.dbCtx.Codings.FindAsync(tempCodingId);
                        counter++;
                        Debug.WriteLine($"{counter}/{codingImagesList.Count}");
                    }
                }



                await CrudHelper.dbCtx.SaveChangesAsync();
                CrudHelper.RefreshContext();


                Debug.WriteLine($"SavedImages");
                var projectFolderPath =
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                    "Pavesys", "iRap", "Projects", $"{SanitizePath(project.Section)}-{project.KmStart}-{project.KmEnd}-" +
                    $"{CrudHelper.FindCodingsByProject((int)project.ProjectId, false).Select(coding => coding.RoadSurveyDate).First().Value.ToString("dd-MM-yyyy", CultureInfo.InvariantCulture)}");
                if (Directory.Exists(projectFolderPath))
                {
                    Directory.Delete(projectFolderPath, true);
                }
                Directory.CreateDirectory(Path.Combine(projectFolderPath, "CAM 1"));
                Directory.CreateDirectory(Path.Combine(projectFolderPath, "CAM 2"));
                var projectInDb = await CrudHelper.dbCtx.Projects.FindAsync(project.ProjectId);
                projectInDb.ProjectPath = projectFolderPath;

                await CrudHelper.dbCtx.SaveChangesAsync();

                if (Directory.Exists(Path.Combine(tempFolderPath, "CAM 1")))
                {
                    var filesCam1 = Directory.GetFiles(Path.Combine(tempFolderPath, "CAM 1"));
                    foreach (var sourceFile in filesCam1)
                    {
                        var fileName = Path.GetFileName(sourceFile);
                        var destinationFile = Path.Combine(Path.Combine(projectFolderPath, "CAM 1"), fileName);
                        await CopyFileAsync(sourceFile, destinationFile);
                    }
                }
                if (Directory.Exists(Path.Combine(tempFolderPath, "CAM 2")))
                {
                    var filesCam2 = Directory.GetFiles(Path.Combine(tempFolderPath, "CAM 2"));
                    foreach (var sourceFile in filesCam2)
                    {
                        var fileName = Path.GetFileName(sourceFile);
                        var destinationFile = Path.Combine(Path.Combine(projectFolderPath, "CAM 2"), fileName);
                        await CopyFileAsync(sourceFile, destinationFile);
                    }
                }

                
                return project;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error importing zip handler");
                return null;
            }
            finally
            {
                // Delete the temporary directory
                if (Directory.Exists(tempFolderPath))
                {
                    Directory.Delete(tempFolderPath, true);
                }
            }
        }

    }
}
