using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using FarmmapsApi;
using FarmmapsApi.Models;
using FarmmapsApi.Services;
using FarmmapsDownloadCL.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static FarmmapsApiSamples.Constants;

namespace FarmmapsDownloadCL
{
    public class DownloadCLApplication : IApplication
    {

        private readonly ILogger<DownloadCLApplication> _logger;
        private readonly FarmmapsApiService _farmmapsApiService;
        private readonly DownloadCLService _DownloadCLService;
        private readonly GeneralService _generalService;
        public readonly Dictionary<string, List<string>> _dictCl;
        string _itemcode;
        public DownloadCLApplication(ILogger<DownloadCLApplication> logger, FarmmapsApiService farmmapsApiService,
            GeneralService generalService, DownloadCLService DownloadCLService)
        {
            _logger = logger;
            _farmmapsApiService = farmmapsApiService;
            _generalService = generalService;
            _DownloadCLService = DownloadCLService;
            CodelistsClasses clc = new CodelistsClasses();
             _itemcode = clc.itemcode;
            _dictCl = clc.dictCl;
        }

        public async Task RunAsync()
        {
            var fieldsInputJson = File.ReadAllText("DownloadCLInput.json");


            DownloadCLInput clInput = JsonConvert.DeserializeObject<DownloadCLInput>(fieldsInputJson);

            // !! this call is needed the first time an api is called with a fresh clientid and secret !!
            await _farmmapsApiService.GetCurrentUserCodeAsync();
            var roots = await _farmmapsApiService.GetCurrentUserRootsAsync();

            //Where to write the output
            string downloadFolder = clInput.DownloadFolder;
            if (string.IsNullOrEmpty(downloadFolder))
            {
                downloadFolder = "Downloads";
            }
            if (!Directory.Exists(downloadFolder))
                Directory.CreateDirectory(downloadFolder);
            
            //Get the most recent codelists
            foreach (string codelist in clInput.codelists)
            {
                try
                {
                    await Process(roots, codelist, downloadFolder);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex.Message);
                }
            }
        }

        private async Task Process(List<UserRoot> roots, string codelistname, string downloadFolder)
        {
            List<Item> codelist;
            string itemtype;
            string className;
            string body;
            string header;
            string value;
            string[] aboutArray = null; 
            string[] headerArray = null;
            PropertyInfo[] props; 
            PropertyInfo prop;
            List<string> dataList;
            string clJson = Path.Combine(downloadFolder, $"{codelistname}.json");
            string clCsv = Path.Combine(downloadFolder, $"{codelistname}.csv");

            try
            {
                itemtype = _dictCl[codelistname][0];
                className = _dictCl[codelistname][1];
                codelist = await _farmmapsApiService.GetItemChildrenAsync(_itemcode, itemtype);

                //Write full codelist in json format to clJson.
                body = JsonConvert.SerializeObject(codelist);
                File.WriteAllText(clJson, body);
                _logger.LogInformation($"Downloaded file {clJson}");

                //Write full list in csv format to clCsv.
                StreamWriter sw = new StreamWriter(clCsv);
                //Write metadata in top: when downloaded
                sw.WriteLine($"Editeelt codelist {codelistname} downloaded on {DateTime.Now} with the FarmmapsDownloadCL application");

                //Generic, for any Codelist as long as it is also defined in FarmmapsDownloadCL.Models in 
                string typerequest = "FarmmapsDownloadCL.Models." + className + ", FarmmapsDownloadCL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
                Type objType = Type.GetType(typerequest);
                object clitem = Activator.CreateInstance(objType);
                props = clitem.GetType().GetProperties();

                //Lookup about info and write to top
                foreach (PropertyInfo pi in props)
                {
                    if (pi.Name == "about")
                    {
                        aboutArray = (string[])pi.GetValue(clitem);
                        foreach (string about in aboutArray)
                        {
                            //Add quotes
                            value = "\"" + about + "\"";
                            //Write each about to a separate line in the csv file
                            sw.WriteLine(value);
                        }

                    }
                }
                //Write an empty line 
                sw.WriteLine();

                //Look up the headerArray and write the header comma separated
                int i = 0;
                while (headerArray == null)
                {
                    prop = props[i];
                    if (prop.Name == "headers")
                    {
                        headerArray = (string[])prop.GetValue(clitem);
                    }
                    i++;
                }
                header = string.Join(",", headerArray);
                sw.WriteLine(header);

                //Add quotes because a value for field 'description' could be '21% Cl, 16.5%CaO, 1330 g/l' and without the quotes that
                //would show up as separate columns. So per definition we will add quotes, except if we have a fieldname in the listNotToQuote, then value for that fieldname will not be given quotes
                //e.g. not code "3" but code 3.
                List<string> listNotToQuote = new List<string>() { "code", "n", "p", "k", "quantity", "cropGroupCode", "cultivationGroupCode" };
                //Loop through all items in the codelist
                foreach (Item item in codelist)
                {
                    dataList = new List<string> { };
                    clitem = JsonConvert.DeserializeObject(item.Data.ToString(), objType);

                    //Add values of the cLitem to the dataList in the same order as the header
                    foreach (string h in headerArray)
                    {
                        value = (string)clitem.GetType().GetProperty(h).GetValue(clitem, null);
                        if(listNotToQuote.Contains(h) == false)
                            value = "\"" +value + "\"";

                        dataList.Add(value);
                    }
                    string dataLine = string.Join(",", dataList);
                    sw.WriteLine(string.Join(",", dataList));
                }

                sw.Close();
                _logger.LogInformation($"Downloaded file {clCsv}");

            }
            catch
            {
                _logger.LogWarning($"Missing lookup information on codelist {codelistname}");
            }
        }
    }
}