using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Winbooks.TORM.OM;
using Winbooks.TORM.OM.Accounting;
using Winbooks.TORM.DAL;
using Winbooks.TORM.DAL.Accounting;

namespace Winbooks.Apis.Linker
{
    #region Object model for posting sales / purchases
    /// <summary>
    /// Header and footer of the invoice (1 per invoice)
    /// </summary>
    public class InvoiceHeader
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public InvoiceHeader(Cache cache)
        {
            _cache = cache;
        }

        public Journal Journal { get; private set; }
        /// <summary>
        /// Code of the journal. Example: "VENTES", "VERKOPEN"
        /// </summary>
        public string JournalCode { get { return Journal == null ? null : Journal.Code; } set { Journal = _cache.GetJournal(value); } }

        /// <summary>
        /// Number of the invoice. Has to be incremental.
        /// </summary>
        public long DocNumber { get; set; }

        public Customer Customer { get; private set; }
        /// <summary>
        /// Code of the customer for sales or credit note on sales. Has to exist.
        /// </summary>
        public string CustomerCode { get { return Customer == null ? null : Customer.Code; } set { Customer = _cache.GetEntity<Customer>(value); } }

        public Supplier Supplier { get; private set; }
        /// <summary>
        /// Code of the supplier for purchases or credit note on purchases. Has to exist.
        /// </summary>
        public string SupplierCode { get { return Supplier == null ? null : Supplier.Code; } set { Supplier = _cache.GetEntity<Supplier>(value); } }

        /// <summary>
        /// Date of the invoice
        /// </summary>
        public DateTime Date { get; set; }

        /// <summary>
        /// Due date of the invoice
        /// </summary>
        public DateTime DueDate { get; set; }

        public Currency Currency { get; private set; }
        /// <summary>
        /// Code of the currency. Example: "EUR", "USD", ...
        /// </summary>
        public string CurrencyCode { get { return Currency == null ? null : Currency.Code; } set { Currency = _cache.GetEntity<Currency>(value); } }

        /// <summary>
        /// Exchange rate. If the folder's currency is EUR and the invoice is in GBP, 1 EUR = 0.86 GBP (rate = 0.86)
        /// So we have to divide the Currency amount by the rate to have the Amount (in the folder's currency)
        /// </summary>
        public decimal CurrencyRate { get; set; }

        /// <summary>
        /// Total amount to pay. Must be = to the total of base amounts + total of tax amounts (Vats)
        /// Use the sale sign as when you encode it. For a sales journal, + is going on the debit of the customer and - is going on the credit (a negative invoice = credit note)
        /// </summary>
        public decimal TotalAmount { get; set; }

        /// <summary>
        /// Comment which will be on the history of the third party (customer or supplier)
        /// </summary>
        public string Comment { get; set; }

        /// <summary>
        /// Extra comment or VCS payment identification number including the format +++123/1234/12345+++
        /// </summary>
        public string CommentExt { get; set; }

        /// <summary>
        /// List of general accounts + vat codes + base amounts + comments
        /// </summary>
        public List<InvoiceGLAccount> GLAccounts { get; set; }

        /// <summary>
        /// Totals by vat code
        /// </summary>
        public List<InvoiceVat> Vats { get; set; }

        /// <summary>
        /// List of analytical account linked to the amount on the central customer account (for example "400000" in the general ledger of Belgium) 
        /// </summary>
        //public List<InvoiceALAccount> ALAccounts { get; set; }
    }

    /// <summary>
    /// Detail per general account
    /// </summary>
    public class InvoiceGLAccount
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public InvoiceGLAccount(Cache cache)
        {
            _cache = cache;
        }

        public GLAccount GLAccount { get; private set; }
        /// <summary>
        /// Code of the general account. Example "700000".
        /// </summary>
        public string GLAccountCode { get { return GLAccount == null ? null : GLAccount.Code; } set { GLAccount = _cache.GetEntity<GLAccount>(value); } }

        public Vat Vat { get; private set; }
        /// <summary>
        /// Code of the vat. Example: "0", "6", "21", "C" for co-contractant, "E" (for export in EU), "X" (for export outside EU)
        /// </summary>
        public string VatCode { get { return Vat == null ? null : Vat.Code; } set { Vat = _cache.GetEntity<Vat>(value); } }

        /// <summary>
        /// Amount to allocate on the general account (always positive)
        /// </summary>
        public decimal BaseAmount { get; set; }

        private string _sign;
        /// <summary>
        /// "D" for debit, "C" for credit
        /// </summary>
        public string Sign { get { return _sign; } set { if (!value.Equals("D") && !value.Equals("C")) throw new ArgumentException("Sign has to be \"C\" or \"D\""); else _sign = value; } }

        /// <summary>
        /// Comment on the booking of the general account
        /// </summary>
        public string Comment { get; set; }

        /// <summary>
        /// List of analytical account linked to the amount on the general account
        /// </summary>
        public List<DetailALAccount> ALAccounts { get; set; }
    }

    /// <summary>
    /// Totals by vat code
    /// </summary>
    public class InvoiceVat
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public InvoiceVat(Cache cache)
        {
            _cache = cache;
        }

        public Vat Vat { get; private set; }
        /// <summary>
        /// Code of the vat. Example: "0", "6", "21", "C" for co-contractant, "E" (for export in EU), "X" (for export outside EU)
        /// </summary>
        public string VatCode { get { return Vat == null ? null : Vat.Code; } set { Vat = _cache.GetEntity<Vat>(value); } }

        /// <summary>
        /// Base amount with the same sign as when encoded (sum of the Base Amounts of InvoiceGLAccount for the specified vat code)
        /// </summary>
        public decimal BaseAmount { get; set; }

        /// <summary>
        /// Taxe amount with the same signe as when encoded (BaseAmount * Vat rate)
        /// </summary>
        public decimal TaxAmount { get; set; }
    }
    #endregion Object model for posting sales and purchase

    #region Object model for posting miscellaneous operations
    /// <summary>
    /// Header of a miscellaneous operation
    /// </summary>
    public class MiscOpHeader
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public MiscOpHeader(Cache cache)
        {
            _cache = cache;
        }

        public Journal Journal { get; private set; }
        /// <summary>
        /// Code of the journal. Example: "OPDIV", "DIVPOSTEN"
        /// </summary>
        public string JournalCode { get { return Journal == null ? null : Journal.Code; } set { Journal = _cache.GetJournal(value); } }

        /// <summary>
        /// Number of the invoice. Has to be incremental.
        /// </summary>
        public long DocNumber { get; set; }

        /// <summary>
        /// Date of the invoice
        /// </summary>
        public DateTime BookDate { get; set; }

        public List<MiscOpDetail> details { get; set; }
    }

    /// <summary>
    /// Rows of the miscellaneous operation
    /// </summary>
    public class MiscOpDetail
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public MiscOpDetail(Cache cache)
        {
            _cache = cache;
        }

        /// <summary>
        /// Operation date
        /// </summary>
        public DateTime ValueDate { get; set; }

        public GLAccount GLAccount { get; private set; }
        /// <summary>
        /// Code of the general account. Example "700000".
        /// </summary>
        public string GLAccountCode
        {
            get { return GLAccount == null ? null : GLAccount.Code; }
            set
            {
                if (Customer != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.GLAccountCode if the CustomerCode was assigned");
                if (Supplier != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.GLAccountCode if the SupplierCode was assigned");
                GLAccount = _cache.GetEntity<GLAccount>(value);
            }
        }

        public Customer Customer { get; private set; }
        /// <summary>
        /// Code of the customer for sales or credit note on sales. Has to exist.
        /// </summary>
        public string CustomerCode
        {
            get { return Customer == null ? null : Customer.Code; }
            set
            {
                if (GLAccount != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.CustomerCode if the GLAccountCode was assigned");
                if (Supplier != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.CustomerCode if the SupplierCode was assigned");
                Customer = _cache.GetEntity<Customer>(value);
            }
        }

        public Supplier Supplier { get; private set; }
        /// <summary>
        /// Code of the supplier for purchases or credit note on purchases. Has to exist.
        /// </summary>
        public string SupplierCode
        {
            get { return Supplier == null ? null : Supplier.Code; }
            set
            {
                if (GLAccount != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.SupplierCode if the GLAccountCode was assigned");
                if (Customer != null)
                    throw new ArgumentException("You cannot assign the MiscOpDetail.SupplierCode if the CustomerCode was assigned");
                Supplier = _cache.GetEntity<Supplier>(value);
            }
        }

        public Currency Currency { get; private set; }
        /// <summary>
        /// Code of the currency. Example: "EUR", "USD", ...
        /// </summary>
        public string CurrencyCode { get { return Currency == null ? null : Currency.Code; } set { Currency = _cache.GetEntity<Currency>(value); } }

        /// <summary>
        /// Exchange rate. If the folder's currency is EUR and the invoice is in GBP, 1 EUR = 0.86 GBP (rate = 0.86)
        /// So we have to divide the Currency amount by the rate to have the Amount (in the folder's currency)
        /// </summary>
        public decimal CurrencyRate { get; set; }

        /// <summary>
        /// Amount in the currency or in the foder currency if no currency (must be >= 0)
        /// </summary>
        public decimal CurrencyAmount { get; set; }

        private string _sign;
        /// <summary>
        /// Sign "D" or "C"
        /// </summary>
        public string Sign { get { return _sign; } set { if (!value.Equals("D") && !value.Equals("C")) throw new ArgumentException("Sign has to be \"C\" or \"D\""); else _sign = value; } }

        /// <summary>
        /// Amount in the foder currency (must be >= 0)
        /// </summary>
        public decimal Amount { get; set; }

        /// <summary>
        /// Comment
        /// </summary>
        public string Comment { get; set; }

        /// <summary>
        /// Detail of the cost accounts
        /// </summary>
        public List<DetailALAccount> ALAccounts { get; set; }


    }
    #endregion Object model for posting miscellaneous operations

    /// <summary>
    /// Detail per analytical (cost) account
    /// </summary>
    public class DetailALAccount
    {
        private Cache _cache;

        /// <summary>
        /// Constructor save the cache
        /// </summary>
        /// <param name="cache"></param>
        public DetailALAccount(Cache cache)
        {
            _cache = cache;
        }

        public ALDimension ALDimension { get; private set; }
        /// <summary>
        /// Code of the dimension. Example: "PROJECT"
        /// </summary>
        public string ALDimensionCode { get { return ALDimension == null ? null : ALDimension.Code; } set { ALDimension = _cache.GetEntity<ALDimension>(value); } }

        public ALAccount ALAccount { get; private set; }
        /// <summary>
        /// Code of the analytical account. Example: "1002"
        /// </summary>
        public string ALAccountCode
        {
            get
            {
                return ALAccount == null ? null : ALAccount.Code;
            }
            set
            {
                if (ALDimension == null)
                    throw new ArgumentException(string.Format("The ALDimensionCode has to be assigned before the ALAccountCode {0}", value));
                ALAccount = _cache.GetALAccount(ALDimension, value);
            }
        }

        /// <summary>
        /// Amount allocated for the cost account (always positive)
        /// </summary>
        public decimal Amount { get; set; }

        /// <summary>
        /// "D" for debit, "C" for credit
        /// </summary>
        public string Sign { get; set; }

    }

}
