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 Winbooks.Apis.Linker;


namespace Winbooks.Apis.Demo
{
    /// <summary>
    /// This is an example of your code to post sales invoices / credit notes
    /// It's using the classes in the directory Linker
    /// </summary>
    public class PostViaLinker
    {
        public static string MainExecute(EntitiesServices folder, UserCredential userCredential)
        {
            const string JOURNAL_CODE_SALES = "VERKP";
            const string JOURNAL_CODE_MISCOP = "OPDIV";

            string ret = "OK";
            try
            {
                Settings settings = new Settings() { FolderCurrency = "EUR", NumberOfDecimals = 2, ALBookingMethod = ALBookingMethod.SameAmountForAllDimensions };
                Cache cache = new Cache(folder, userCredential);
                BookPeriod bookPeriod = cache.SetBookPeriod(3, 2017);

                SalesPurchasesLinker saleslinker = new SalesPurchasesLinker(folder, settings, cache);
                MiscOpLinker miscOpLinker = new MiscOpLinker(folder, settings, cache);

                // Sales
                long salesNumber = cache.GetNextDocNumber(JOURNAL_CODE_SALES);
                InvoiceHeader invHead = GetOneInvoice(cache, JOURNAL_CODE_SALES, salesNumber);
                saleslinker.PostInvoice(invHead, bookPeriod);
                GLTrans glTransSalesCustomer = cache.GetGLTrans0(invHead.Journal, salesNumber);

                //// Miscellaneous operations
                //long miscOpNumber = cache.GetNextDocNumber(JOURNAL_CODE_MISCOP);
                //MiscOpHeader moHead = GetOneMiscOp(cache, JOURNAL_CODE_MISCOP, miscOpNumber, JOURNAL_CODE_SALES + "-" + salesNumber.ToString());
                //Guid idMiscOp = miscOpLinker.PostMiscOp(moHead, bookPeriod);
                //GLTrans glTransMiscOpCustomer = GetGLTrans(folder, idMiscOp, moHead.details[0].Customer.Id, moHead.details[0].Comment);

                //MatchCustomer(folder, glTransSalesCustomer, glTransMiscOpCustomer);
            }
            catch (ArgumentException e)
            {
                ret = "Error " + e.Message;
            }
            return ret;
        }

        private static InvoiceHeader GetOneInvoice(Cache cache, string journalCode, long docNumber)
        {
            InvoiceHeader head = new InvoiceHeader(cache)
            {
                JournalCode = journalCode,
                DocNumber = docNumber,
                Date = DateTime.Parse("2017-03-15T00:00:00"),
                DueDate = DateTime.Parse("2017-04-15T00:00:00"),
                CustomerCode = "BEBOP",
                CurrencyCode = "EUR",
                CurrencyRate = 1.00m,
                TotalAmount = 1210.00m,
                Comment = "Test " + docNumber.ToString(),
                CommentExt = "+++123/1234/12345+++",
                GLAccounts = new List<InvoiceGLAccount>(),
                Vats = new List<InvoiceVat>()
            };
            // GENERAL ACCOUNTS
            head.GLAccounts.Add(new InvoiceGLAccount(cache)
            {
                GLAccountCode = "700000",
                VatCode = "21",
                BaseAmount = 1000.00m,
                Sign = "C",
                Comment = "First item",
                ALAccounts = new List<DetailALAccount>()
            });
            // COST ACCOUNTS
            head.GLAccounts[0].ALAccounts.Add(new DetailALAccount(cache)
            {
                ALDimensionCode = "DIM1",
                ALAccountCode = "AAAA",
                Amount = 1000.00m,
                Sign = "C"
            });
            //head.GLAccounts[0].ALAccounts.Add(new DetailALAccount(cache)
            //{
            //    ALDimensionCode = "DIM1",
            //    ALAccountCode = "BBBB",
            //    Amount = 400.00m,
            //    Sign = "C"
            //});
            head.GLAccounts[0].ALAccounts.Add(new DetailALAccount(cache)
            {
                ALDimensionCode = "DIM2",
                ALAccountCode = "1001",
                Amount = 1000.00m,
                Sign = "C"
            });
            // VAT
            head.Vats.Add(new InvoiceVat(cache)
            {
                VatCode = "21",
                BaseAmount = 1000.00m,
                TaxAmount = 210.00m
            });
            return head;
        }

        private static MiscOpHeader GetOneMiscOp(Cache cache, string journalCode, long docNumber, string comment)
        {
            MiscOpHeader head = new MiscOpHeader(cache)
            {
                JournalCode = journalCode,
                DocNumber = docNumber,
                BookDate = DateTime.Parse("2017-03-15T00:00:00"),
                details = new List<MiscOpDetail>()
            };
            head.details.Add(new MiscOpDetail(cache)
            {
                ValueDate = DateTime.Parse("2017-03-15T00:00:00"),
                CustomerCode = "BEBOP",
                CurrencyCode = "EUR",
                CurrencyRate = 1.00m,
                CurrencyAmount = 1210.00m,
                Sign = "C",
                Amount = 1210.00m,
                Comment = comment
            });
            head.details.Add(new MiscOpDetail(cache)
            {
                ValueDate = DateTime.Parse("2017-03-15T00:00:00"),
                GLAccountCode = "580000",
                CurrencyCode = "EUR",
                CurrencyRate = 1.00m,
                CurrencyAmount = 1210.00m,
                Sign = "D",
                Amount = 1210.00m,
                Comment = comment
            });
            return head;
        }

        private static GLTrans GetGLTrans(EntitiesServices folder, Guid glTransHeader_Id, Guid customer_Id, string comment)
        {
            var criteria = new GLTransDAO().CreateCriteria()
                .CreateAlias("header", typeof(GLTransHeader), GLTransHeader.Names.Id, GLTrans.Names.GLTransHeader_Id)
                .Add(Condition.Eq("header." + GLTransHeader.Names.Id, glTransHeader_Id))
                .Add(Condition.Eq(GLTrans.Names.Customer_Id, customer_Id))
                .Add(Condition.Eq(GLTrans.Names.CommentBooking, comment))
                .SetFirstResult(0)
                .SetMaxResults(1);

            GLTrans glTrans = folder.GetFilter<GLTrans>(criteria);
            return glTrans;
        }

        private static void MatchCustomer(EntitiesServices folder, GLTrans glTrans1, GLTrans glTrans2)
        {
            if (glTrans1 == null || glTrans2 == null)
                return;
            if (glTrans1.Customer_Id != glTrans1.Customer_Id)
                return;

            GLMatch glMatch;

            DateTime oldesMatchDate = glTrans1.ValueDate < glTrans2.ValueDate ? glTrans1.ValueDate : glTrans2.ValueDate;
            bool isMatched = glTrans1.CurrencyAmount + glTrans2.CurrencyAmount == 0;

            // code has to be unique
            // application generates a random unique code between 100000 and 999999
            // so the external app can generate a unique code between 000000 and 099999
            // for example, if the invoice number < 99999, it could be the invoice number so we do not need to search for a free code
            string code = GetUniqueCode(folder);
            glMatch = new GLMatch() { Code = code, OldestMatchedDate = oldesMatchDate, IsMatched = isMatched };
            folder.Insert(glMatch);
            glMatch = folder.GetByCode<GLMatch>(code);
            glMatch.GLTransactions = new List<GLTrans>();

            List<GLTrans> glTranss = new List<GLTrans>() { glTrans1, glTrans2 };

            foreach (var glTrans in glTranss)
            {
                glMatch.GLTransactions.Add(glTrans);
                glTrans.GLMatch = glMatch;
            }

            folder.UpdateCollection<GLTrans>(glTranss, 3);
        }

        private static string GetUniqueCode(EntitiesServices folder)
        {
            // find the bigest number between 000000 and 099999
            var criteria = new GLMatchDAO().CreateCriteria()
                .Add(Condition.Ge(GLMatch.Names.Code, "000000"))
                .Add(Condition.Lt(GLMatch.Names.Code, "099999"))
                .Order(GLMatch.Names.Code, false)
                .SetFirstResult(0)
                .SetMaxResults(1);

            GLMatch glMatch = folder.GetFilter<GLMatch>(criteria);
            if (glMatch == null)
                return "000000";
            else
                return (int.Parse(glMatch.Code) + 1).ToString().PadLeft(6, '0');
        }
    }
}
