using System.Data;
using PropertyFuzeData;
using System.Text.Json;

// ******************************************************************************************************
// This process will go through all the Type 4 SendMailTransactionInvoices and create invoices in the
// Efformance Rent Manager database and the corresponding Type 3 SendMailTransactionInvoices in SQL
// Server.  It will also add any subscription fees to the Rent Manager Invoice.  
//
// It currently creates one RM invoice at a time but we could change it to create all the RM invoices
// at once or all invoices for a client at once.
// ******************************************************************************************************

// ******************************************************************************************************
// Required Changes:
//
//  1. All of the classes in the EfformanceBillingData project are inherited from PropertyFuzeData.  
//     The properties and methods should be added to PropertyFuzeData and then EfformanceBillingData
//     goes away.
//  2. We need the ValueListOrEmpty() method on PFDataResponse
//  3. Need to add PropertyID and Property fields to the sendMail.request.recipients JSON for 
//     Address Import and Address Wizard
// ******************************************************************************************************

// SQL Server Constants
string connectionString = "Data Source=efformance.database.windows.net;Initial Catalog=Efformance_Test_Bryan;User ID=efformance_prod;Password=309JvAr9iA581GSTCfwoUTtPv9asAiLZzy0";

// Rent Manager Constants, which will be retrieved from the database (dbo.SystemContstants)
string EfformanceRentManagerRestURL = "https://efform.api.rentmanager.com";
int EfformanceRentManagerLocationID = 6;
string EfformanceRentManagerUserID = "pfadmin_dev";
string EfformanceRentManagerPassword = "P@ssword1";
int EfformanceMailChargeTypeID = 109;
//int MailFuzeMonthlyFeeChargeTypeID = 108;
int EfformanceRentManagerPropertyID = 379;
string RentManagerPartnerToken = "80b4e701-27c9-4628-8906-c491512a1e73";
string Efformance365RentManagerPartnerToken = "8e5f4b4a-5583-4449-8992-8c2d202f862f";

// This would be your new setting
bool IWantToGroupInvoicesByPropertyCostCode = true;

// The date the program is executed so would probably be DateTime.Today()
DateOnly runDate = new DateOnly(2025, 4, 1);

// The date up to which we will be gathering invoices
DateOnly asOfDate = runDate.AddDays(-1);

// Delcare the variables for creating a Rent Manager invoice
PropertyFuzeData.Invoice rentManagerInvoice;
PFDataResponse rspCreateInvoice;

// Authenticate to Efformance RM Database and get a token for the API
// *** DON'T NEED THIS ANY MORE BECAUSE WE WILL NOW USE THE EfformanceRentManagerPartnerToken
//Security security = new();
//string apiToken = security.AuthenticateRentManagerUser(EfformanceRentManagerRestURL, EfformanceRentManagerLocationID, EfformanceRentManagerUserID, EfformanceRentManagerPassword);

// Get all the recurring charges from Efformance's RM database.
// These are the subscription fees for the clients.
RecurringCharge rc = new();
PFDataResponse<RecurringCharge> rspRecurringCharges = rc.GetRecurringCharges(EfformanceRentManagerRestURL, $"{Efformance365RentManagerPartnerToken}|{EfformanceRentManagerLocationID.ToString()}", new RecurringCharge.GetRecurringChargesParms
{
    // The last of the month of the month before we are executing
    // NEED TO HANDLE PRORATED FIRST/LAST MONTH (PROBABLY ONLY FIRST MONTH) AND, IF IT'S THE FIRST
    // MONTH, WE NEED TO ADD A LINE ITEM FOR THE SETUP FEE (IF APPLICABLE)
    ActiveAsOfDate = asOfDate,
    IncludeChargeType = true
});

// Loop through each active client (note: the code below loops through ALL clients because at the time
// of this writing, the dbo.Client table did not have the "IsActive" column.
Client client = new();
DataTable dtClients = client.GetClients(connectionString);

foreach (DataRow cl in dtClients.Rows)
{
    int clientID = cl.Field<int>("ClientID");
    int accountID = cl.Field<int>("RentManagerAccountID");

    Console.WriteLine($"Client: {cl.Field<string>("CompanyName")} ({clientID})");

    // Initilize this variable for each client.  It will only come into play for those clients
    // that want their invoices broken down by PropertyCostCode so we don't add the 
    // subscription line item to multiple invoices for that client.
    bool clientSubscriptionDetailsAddedToInvoice = false;

    // Create Invoice Detail line items for any subscription fees. These details will be 
    // added to an invoice later but we don't know yet to which invoice they will be added.
    // If there is an invoice created without a PropertyID and without a CostCode, then these 
    // detail lines will be added to that invoice.  If, however, there is no invoice created without
    // a PropertyID and without a CostCode, then we need to create a new invoice purely for
    // the subscription fee(s).
    //List<InvoiceDetail> subscriptionInvoiceDetails = (
    //    from recurringCharge in rspRecurringCharges.ValueListOrEmpty()
    //    where recurringCharge.EntityKeyID == cl.Field<int>("RentManagerAccountID")
    //    select new InvoiceDetail
    //    {
    //        InvoiceDetailID = RentManagerAPIHelper.RMNullValue,
    //        InvoiceID = RentManagerAPIHelper.RMNullValue,
    //        InventoryItemID = RentManagerAPIHelper.RMNullValue,
    //        PropertyID = EfformanceRentManagerPropertyID,
    //        UnitID = RentManagerAPIHelper.RMNullValue,
    //        AccountID = cl.Field<int>("RentManagerAccountID"),
    //        //ChargeTypeID = MailFuzeMonthlyFeeChargeTypeID,
    //        ChargeTypeID = recurringCharge.ChargeTypeID,
    //        Comment = " We need this here: recurringCharge.ChargeType.Description",
    //        UnitCost = recurringCharge.Amount,
    //        Quantity = 1
    //    }).ToList();

    EfformanceBilling.EfformanceBilling efformanceBilling = new EfformanceBilling.EfformanceBilling();
    List<InvoiceDetail> subscriptionInvoiceDetails =
        efformanceBilling.GetSubscriptionInvoiceDetails(clientID, EfformanceRentManagerPropertyID, accountID, asOfDate, rspRecurringCharges.ValueListOrEmpty());

// Get the Type 4 Invoices from SQL Server
List < SendMailTransaction.TransactionInvoiceDetail.PendingTransactionInvoiceDetail> invoiceDetailsType4;
    SendMailTransaction.TransactionInvoiceDetail smtid = new SendMailTransaction.TransactionInvoiceDetail();
    invoiceDetailsType4 = smtid.GetPendingSendMailInvoiceTransactionDetailsForClient(connectionString, cl.Field<int>("ClientID"), runDate);
    
    string clientRentManagerRestURL = cl.Field<string>("RentManagerRestURL");

    // The "IWantToGroupInvoicesByPropertyCostCode" is a new MailFuze setting for the client
    if (IWantToGroupInvoicesByPropertyCostCode)
    {
        // Group the Type 4 records by LocationID and then by PropertyCostCode
        var type4GroupedByLocationPropertyCostCode =
            from invoiceDetailType4 in invoiceDetailsType4
            group invoiceDetailType4 by invoiceDetailType4.LocationID into LocationGroup
            select new
            {
                LocationID = LocationGroup.Key,
                PropertyCostCodeGroups =
                    from PropertyCostCode in LocationGroup
                    group PropertyCostCode by new { PropertyCostCode.PropertyID, PropertyCostCode.Property, PropertyCostCode.CostCode } into PropertyCostCodeGroup
                    select new
                    {
                        PropertyID = PropertyCostCodeGroup.Key.PropertyID,
                        Property = PropertyCostCodeGroup.Key.Property,
                        CostCode = PropertyCostCodeGroup.Key.CostCode,
                        DetailLines = PropertyCostCodeGroup.ToList()
                    }
            };

        // If any of the LocationIDs are valid (i.e. > 0) then we need to retrieve
        // all the locations for this client so we can use the Location Name 
        // on the invoice.
        DataTable dtLocations = new();

        if (type4GroupedByLocationPropertyCostCode.Where(x => x.LocationID > 0).Count() > 0)
        {
            Locations loc = new();
            dtLocations = loc.GetEnabledLocations(clientRentManagerRestURL, $"{RentManagerPartnerToken}|1");
        }

        // Loop through each distinct Rent Manager location
        foreach (var locationGroup in type4GroupedByLocationPropertyCostCode)
        {
            string locationName = string.Empty;

            if (locationGroup.LocationID > 0 & dtLocations.Rows.Count > 0)
            {
                DataRow locationRow = dtLocations.AsEnumerable().FirstOrDefault(x => x.Field<int>("LocationID") == locationGroup.LocationID);
                if (locationRow is not null)
                {
                    locationName = locationRow.Field<string>("LocationFriendlyName");
                }
            }
            Console.WriteLine($"Location: {locationName} ({locationGroup.LocationID.ToString()})");

            // Loop though each distinct Property/CostCode combination
            foreach (var propertyCostCodeGroup in locationGroup.PropertyCostCodeGroups)
            {
                Console.WriteLine($"Property: {propertyCostCodeGroup.Property} ({propertyCostCodeGroup.PropertyID.ToString()}), Cost Code: {propertyCostCodeGroup.CostCode}");

                int numDetailLinesForPropertyCostCode = propertyCostCodeGroup.DetailLines.Count;

                List<InvoiceDetail> byPropertyCostCodeInvoiceDetails = new List<PropertyFuzeData.InvoiceDetail>
                {
                    new InvoiceDetail
                    {
                        InvoiceDetailID = RentManagerAPIHelper.RMNullValue,
                        InvoiceID = RentManagerAPIHelper.RMNullValue,
                        InventoryItemID = RentManagerAPIHelper.RMNullValue,
                        PropertyID = EfformanceRentManagerPropertyID,
                        UnitID = RentManagerAPIHelper.RMNullValue,
                        ChargeTypeID = EfformanceMailChargeTypeID,
                        AccountID = cl.Field<int>("RentManagerAccountID"),
                        Comment = $"MailFuze Mailing Fees: {numDetailLinesForPropertyCostCode.ToString()} mailing{(numDetailLinesForPropertyCostCode != 1 ? 's' : string.Empty)}",
                        UnitCost = propertyCostCodeGroup.DetailLines.Sum(x => x.Quantity * x.UnitCost),
                        Quantity = 1,
                    }
                };

                // If we are working with an invoice that does NOT have a property and does NOT have
                // a Cost Code, then we will add the subscription invoice details to the mailing
                // invoice details so they are all on the same invoice.
                if (propertyCostCodeGroup.PropertyID == -1 & propertyCostCodeGroup.CostCode.Length <= 0)
                {
                    byPropertyCostCodeInvoiceDetails.AddRange(subscriptionInvoiceDetails);
                    clientSubscriptionDetailsAddedToInvoice = true;
                }

                // These are used to create the invoice.comment
                string invoiceCommentLocation = !string.IsNullOrWhiteSpace(locationName) ? $"Location: {locationName}\r\n" : "\r\n";
                string invoiceCommentProperty = !string.IsNullOrWhiteSpace(propertyCostCodeGroup.Property) ? $"Property: {propertyCostCodeGroup.Property}\r\n" : "\r\n";
                string invoiceCommentCostCode = !string.IsNullOrWhiteSpace(propertyCostCodeGroup.CostCode) ? $"Cost Code: {propertyCostCodeGroup.CostCode}\r\n" : "\r\n";

                // Create the RM Invoice object and add the mailingInvoiceDetails
                rentManagerInvoice = new()
                {
                    InvoiceID = RentManagerAPIHelper.RMNullValue,
                    PropertyID = EfformanceRentManagerPropertyID,
                    //UnitID = RentManagerAPIHelper.RMNullValue, //This property needs to be added to PropertyFuzeData.Invoice
                    AccountID = cl.Field<int>("RentManagerAccountID"),
                    InvoiceDate = DateTime.Now,
                    DueDate = DateTime.Now,
                    Comment = invoiceCommentLocation + invoiceCommentProperty + invoiceCommentCostCode,
                    TermID = RentManagerAPIHelper.RMNullValue,
                    InvoiceDetails = byPropertyCostCodeInvoiceDetails
                };

                //rspCreateInvoice = rentManagerInvoice.Create(EfformanceRentManagerRestURL, RentManagerPartnerToken, true);

                // For testing only
                rspCreateInvoice = new PFDataResponse() { ReturnCode = 0 };
                Console.WriteLine(JsonSerializer.Serialize(rentManagerInvoice, new JsonSerializerOptions { WriteIndented = true }));
                // End testing

                if (rspCreateInvoice.ReturnCode == 0)
                {
                    // Create the Type 3 SendMailTransaction and then create corresponding SendMailTransationInvoice
                    // May have to create RM bill depending upon client settings
                    // If so, Create SendMailTransactionBill related to above
                }
                else
                {
                    // Error handling
                };
            }
        }

        // If we are done with this client and we still have not added the subscription invoice
        // details to any invoice, then we need to create a separate invoice purely for the
        // subscription(s).
        if (!clientSubscriptionDetailsAddedToInvoice && subscriptionInvoiceDetails.Count > 0)
        {
            // Create the RM Invoice object and add the mailingInvoiceDetails
            rentManagerInvoice = new()
            {
                InvoiceID = RentManagerAPIHelper.RMNullValue,
                PropertyID = EfformanceRentManagerPropertyID,
                //UnitID = RentManagerAPIHelper.RMNullValue, //This property needs to be added to PropertyFuzeData.Invoice
                AccountID = cl.Field<int>("RentManagerAccountID"),
                InvoiceDate = DateTime.Now,
                DueDate = DateTime.Now,
                Comment ="Subscription Fees",
                TermID = RentManagerAPIHelper.RMNullValue,
                InvoiceDetails = subscriptionInvoiceDetails
            };

            //rspCreateInvoice = rentManagerInvoice.Create(EfformanceRentManagerRestURL, RentManagerPartnerToken, true);

            // For testing only
            rspCreateInvoice = new PFDataResponse() { ReturnCode = 0 };
            Console.WriteLine(JsonSerializer.Serialize(rentManagerInvoice, new JsonSerializerOptions { WriteIndented = true }));
            // End testing

            if (rspCreateInvoice.ReturnCode == 0)
            {
                // Create the Type 3 SendMailTransactionInvoice
            }
            else
            {
                // Error handling
            };
        }
    }
    else
    {
        // Create one invoice for the client
        int numDetailLines = invoiceDetailsType4.Count();
        List<InvoiceDetail> singleInvoiceDetails = new();

        if (numDetailLines > 0)
        {
            // Generate one RM invoice detail for all of the Type 4 invoice details
            singleInvoiceDetails = new List<PropertyFuzeData.InvoiceDetail>
            {
                new InvoiceDetail
                {
                    InvoiceDetailID = RentManagerAPIHelper.RMNullValue,
                    InvoiceID = RentManagerAPIHelper.RMNullValue,
                    InventoryItemID = RentManagerAPIHelper.RMNullValue,
                    PropertyID = EfformanceRentManagerPropertyID,
                    //UnitID = RentManagerAPIHelper.RMNullValue, //This needs to be added to PropertyFuzeData.InvoiceDetail
                    ChargeTypeID = EfformanceMailChargeTypeID,
                    AccountID = cl.Field<int>("RentManagerAccountID"),
                    Comment = $"MailFuze Mailing Fees: {numDetailLines.ToString()} mailing{(numDetailLines != 1 ? 's' : string.Empty)}",
                    UnitCost = invoiceDetailsType4.Sum(x => x.Quantity * x.UnitCost),
                    Quantity = 1
                }
            };
        }

        // Add the line item for the subscription bill
        singleInvoiceDetails.AddRange(subscriptionInvoiceDetails);
        clientSubscriptionDetailsAddedToInvoice = true;

        // Create the invoice object with the details to be generated in Rent Manager if there
        // are detail lines
        if (singleInvoiceDetails.Count > 0)
        {
            rentManagerInvoice = new()
            {
                InvoiceID = RentManagerAPIHelper.RMNullValue,
                PropertyID = EfformanceRentManagerPropertyID,
                //UnitID = RentManagerAPIHelper.RMNullValue, //This property needs to be added to PropertyFuzeData.Invoice
                AccountID = cl.Field<int>("RentManagerAccountID"),
                InvoiceDate = DateTime.Now,
                DueDate = DateTime.Now,
                Comment = "Efformance Monthly Invoice",
                TermID = RentManagerAPIHelper.RMNullValue,
                InvoiceDetails = singleInvoiceDetails
            };

            // Create the invoice in Rent Manager
            //rspCreateInvoice = rentManagerInvoice.Create(EfformanceRentManagerRestURL, RentManagerPartnerToken, true);

            // For testing only
            rspCreateInvoice = new PFDataResponse();
            Console.WriteLine(JsonSerializer.Serialize(rentManagerInvoice, new JsonSerializerOptions { WriteIndented = true }));
            // End testing

            if (rspCreateInvoice.ReturnCode == 0)
            {
                // Create the Type 3 SendMailTransactionInvoice
            }
            else
            {
                // Error handling
            }
        }
    }
}


