using System;
using System.Collections.Generic;
using Winbooks.TORM.OM;
using Winbooks.TORM.OM.Accounting;
using Winbooks.TORM.DAL;
using Winbooks.TORM.DAL.Accounting;
using Winbooks.Apis.Services;
using Winbooks.Apis.Services.v2;
using Currency = Winbooks.TORM.OM.Currency;


namespace Winbooks.Apis.Demo
{
    public class PostSalesBasic
    {
        private static EntitiesServices _folder;
        private static UserCredential _userCredential;

        public static string MainExecute(EntitiesServices folder, UserCredential userCredential)
        {
            Tools.LogFile = "c:\\temp\\PostSalesBasic.log";
            _folder = folder;
            _userCredential = userCredential;
            return ExecutePostSales();
        }

        private static void Log(string text)
        {
            _userCredential.Log(text+"\n");
        }

        private static Guid GetId(ICriteria criteria, string type, string code)
        {
            Log(string.Format("Getting the {0} Id for code {1} ...", type, code));

            criteria.Add(Condition.Eq("Code", code))
                .SetProjection(Projections.Property("Id"))
                .SetMaxResults(1);
            Newtonsoft.Json.Linq.JArray id = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), type);
            Guid guid = id == null ? Guid.Empty : Guid.Parse(id[0].ToString());

            Log(" -> " + guid.ToString());

            return guid;
        }

        private static string ExecutePostSales()
        {
            int lineOrder = 0;
            string msg;

            Console.WriteLine("Posting Sales operation ...");

            Log("Getting BookPeriod 3/2017");
            BookPeriod bookPeriod = GetEntity.GetBookPeriod(_folder, 2017, 3);
            if (bookPeriod == null)
                return "Book period 2017 3 not found";
            else Log("-> " + bookPeriod.Id);

            Log("Getting Journal VERK for bookyear " + bookPeriod.BookYear_Id);
            Journal journal = GetEntity.GetJournal(_folder, "VERKP", bookPeriod);
            if (journal == null)
                return "Journal VERKP not found";
            else Log("-> " + journal.Id);

            Log("Getting Vat 21");
            Vat vat = GetEntity.GetVat(_folder, "21");
            if (vat == null)
                return "Vat 21 not found";
            else Log("-> " + vat.Id);

            Log("Getting account 400000");
            GLAccount custAccount = GetEntity.GetGLAccount(_folder, "400000", bookPeriod);
            if (custAccount == null)
                return "GLAccount 400000 not found";
            else Log("-> " + custAccount.Id);

            Log("Getting VAT account for CentralAccountType.Id = " + vat.CentralAccountType_Id);
            GLAccount vatAccount = GetEntity.GetGLAccountByCentralAccountTypeId(_folder, vat.CentralAccountType_Id); // it could be null if VAT rate == 0
            if (vatAccount == null)
                Log("-> none");
            else
                Log("-> " + vatAccount.Id);

            Guid currency_Id = GetId(new CurrencyDAO().CreateCriteria(), "Currency", "EUR"); if (currency_Id == Guid.Empty) return "Currency EUR not found";
            Guid customer_Id = GetId(new CustomerDAO().CreateCriteria(), "Customer", "BEBOP"); if (customer_Id == Guid.Empty) return "Customer BEBOP not found";
            Guid allocAccount_Id = GetId(new GLAccountDAO().CreateCriteria(), "GLAccount", "700000"); if (allocAccount_Id == Guid.Empty) return "General account 700000 not found";

            decimal baseAmount = 1000;
            decimal vatAmount = decimal.Round(baseAmount * (decimal)vat.Rate / 100, 2);
            decimal totAmount = baseAmount + vatAmount;

            string comment = " Bla bla bla " + DateTime.Now.ToString();

            // CREATE HEADER
            GLTransHeader header = new GLTransHeader();
            header.BookPeriod_Id = bookPeriod.Id;
            header.Journal_Id = journal.Id;
            header.DocNumber = GetNextDocNumber(journal.Id);
            header.BookDate = DateTime.Parse("2017-03-15T00:00:00");
            header.DueDate = DateTime.Parse("2017-04-15T00:00:00");

            // CREATE TRANSACTION FOR CUSTOMER
            GLTrans glTransCustomer = CreateTransaction(header, bookPeriod.Id, customer_Id, currency_Id);
            glTransCustomer.LineOrder = lineOrder++;
            glTransCustomer.GLAccount_Id = custAccount.Id;      // 400000
            glTransCustomer.CentralAccountType_Id = custAccount.CentralAccountType_Id;
            glTransCustomer.Amount = 1210;
            glTransCustomer.VatBaseAmount = 1000;
            glTransCustomer.TurnOverAmount = 1000;
            glTransCustomer.VatTaxAmount = 210;
            glTransCustomer.CurrencyRate = 1;
            glTransCustomer.CurrencyAmount = 1210;
            glTransCustomer.CurrencyVatBaseAmount = 1000;
            glTransCustomer.CurrencyVatTaxAmount = 210;
            glTransCustomer.ValueDate = header.BookDate;
            glTransCustomer.OldestMatchedDate = header.BookDate;
            glTransCustomer.CommentBooking = "Customer" + comment;
            header.GLTransactions.Add(glTransCustomer);

            // CREATE TRANSACTION FOR 700000
            GLTrans glTransAlloc = CreateTransaction(header, bookPeriod.Id, customer_Id, currency_Id);
            glTransAlloc.LineOrder = lineOrder++;
            glTransAlloc.GLAccount_Id = allocAccount_Id;        // 700000
            glTransAlloc.Amount = -1000;
            glTransAlloc.CurrencyRate = 1;
            glTransAlloc.CurrencyAmount = -1000;
            glTransAlloc.ValueDate = header.BookDate;
            glTransAlloc.OldestMatchedDate = header.BookDate;
            glTransAlloc.CommentBooking = "Turnover"+comment;
            glTransAlloc.Vat_Id = vat.Id;
            header.GLTransactions.Add(glTransAlloc);

            // CREATE TRANSACTION FOR VAT
            GLTrans glTransVat = CreateTransaction(header, bookPeriod.Id, customer_Id, currency_Id);
            glTransVat.LineOrder = lineOrder++;
            if (vatAccount != null)
            {
                glTransVat.GLAccount_Id = vatAccount.Id;        // 451000
                glTransVat.CentralAccountType_Id = vat.CentralAccountType_Id;
            }
            glTransVat.Amount = -210;
            glTransVat.VatBaseAmount = 1000;
            glTransVat.IsVatAllocation = true;
            glTransVat.CurrencyRate = 1;
            glTransVat.CurrencyAmount = -210;
            glTransVat.CurrencyVatBaseAmount = 1000;
            glTransVat.ValueDate = header.BookDate;
            glTransVat.OldestMatchedDate = header.BookDate;
            glTransVat.CommentBooking = "Vat"+comment;
            glTransVat.Vat_Id = vat.Id;
            header.GLTransactions.Add(glTransVat);

            msg = "GLTransHeader + 2 level " + journal.Code + "  " + header.DocNumber.ToString() + " in " + bookPeriod.Name;
            Log("Creating " + msg);
            _folder.Insert<GLTransHeader>(header, 2);
            header = GetEntity.GetGLTransHeader(_folder, journal.Code, header.DocNumber);

            return header == null ? "Transaction header not found" : "OK";
        }

        private static GLTrans CreateTransaction(GLTransHeader header, Guid bookPeriod_Id, Guid customer_Id, Guid currency_Id)
        {
            if (header == null) return null;

            GLTrans glTrans = new GLTrans();
            glTrans.GLTransHeader_Id = header.Id;
            glTrans.BookPeriod_Id = bookPeriod_Id;
            glTrans.Currency_Id = currency_Id;
            glTrans.CurrencyRate = 1;
            glTrans.ValueDate = header.BookDate;
            if (customer_Id != Guid.Empty)
                glTrans.Customer_Id = customer_Id;
            glTrans.IsCoda = false;
            return glTrans;
        }

        private static long GetNextDocNumber(Guid journalId)
        {
            //GET LAST GLTRANSHEADER DOCUMENT NUMBER
            Tools.Log("Getting the last Document Number of journal", true);
            var lastHeaderCriteria = new GLTransHeaderDAO().CreateCriteria()
                .Add(Condition.Eq(GLTransHeader.Names.Journal_Id, journalId))
                .Order(GLTransHeader.Names.DocNumber, false)
                .SetFirstResult(0).SetMaxResults(1);

            GLTransHeader lastHeader = _folder.GetFilter<GLTransHeader>(lastHeaderCriteria);
            long documentNumber = 1;

            if (lastHeader != null)
            {
                Tools.Log("Getting the last Document Number (" + lastHeader.DocNumber + ") : OK");
                documentNumber = lastHeader.DocNumber + 1;
            }
            else
                Tools.Log("Folder has no document in the journal");
            return documentNumber;
        }

        private static void Match(List<GLTrans> glTransList)
        {
            // If the GLMatch does not exist, create it
            string code = "123457";
            GLMatch glMatch;

            glMatch = _folder.GetByCode<GLMatch>(code, true);
            if (glMatch == null)
            {
                glMatch = new GLMatch();
                glMatch.Code = code;
                _folder.Insert<GLMatch>(glMatch);
                glMatch = _folder.GetByCode<GLMatch>(code, true);
            }

            // Put the GLTrans in the GLMatch
            decimal total = 0;
            glMatch.GLTransactions = new List<GLTrans>();
            foreach (var glTrans in glTransList)
            {
                total += glTrans.Amount;
                glMatch.GLTransactions.Add(glTrans);
                glTrans.GLMatch = glMatch;
            }
            if (total == 0)
                glMatch.IsMatched = true;

            Tools.Log("Matching transactions", true);
            _folder.UpdateCollection<GLTrans>(glTransList, 3);
            Tools.Log("Matching transactions done");

            if (total == 0)
            {
                Tools.Log("Updating IsMatch = true", true);
                _folder.UpdateById<GLMatch>(glMatch, 1);
                Tools.Log("Updating IsMatch = true done");
            }
        }

    }
}
