using MyAppApi.DataRepository.Helper;
using MyAppApi.Models;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Threading.Tasks;
using MyAppApi.BusinessLayer;
 
namespace MyAppApi.DataRepository
{
     /// <summary>
     /// Works like the Base class for ProductRepository class.
     /// ************************************* Do not make changes to this class *************************************
     /// ** Put your additional code in the ProductRepository class under the DataRepository folder. **
     /// *************************************************************************************************************
     /// </summary>
     public partial class ProductRepository : IProductRepository
     {
         private const CommandType _commandType = CommandType.StoredProcedure;
         private readonly string _connectionString;
 
         internal ProductRepository(string connectionString)
         {
             _connectionString = connectionString;
         }
 
         /// <summary>
         /// Selects a record by primary key(s)
         /// </summary>
         async Task<DataTableIProductRepository.SelectByPrimaryKeyAsync(int productID)
         {
             string storedProcName = "[dbo].[Products_SelectByPrimaryKey]";
             List<SqlParametersqlParamList = new();
 
             // add parameters to the sqlParams
             DatabaseFunctions.AddSqlParameter(sqlParamList"@productID"productID);
 
             // get and return the data
             return await DatabaseFunctions.GetDataTableAsync(_connectionString, storedProcNamesqlParamList, _commandType);
         }
 
         /// <summary>
         /// Gets the total number of records in the Products table
         /// </summary>
         async Task<intIProductRepository.GetRecordCountAsync()
         {
              string storedProcName = "[dbo].[Products_GetRecordCount]";
              return await this.GetRecordCountSharedAsync(storedProcNamenullnull);
         }
 
         /// <summary>
         /// Gets the total number of records in the Products table by SupplierID
         /// </summary>
         async Task<intIProductRepository.GetRecordCountBySupplierIDAsync(intsupplierID)
         {
             string storedProcName = "[dbo].[Products_GetRecordCountBySupplierID]";
             return await this.GetRecordCountSharedAsync(storedProcName"supplierID"supplierID);
         }
 
         /// <summary>
         /// Gets the total number of records in the Products table by CategoryID
         /// </summary>
         async Task<int> IProductRepository.GetRecordCountByCategoryIDAsync(intcategoryID)
         {
             string storedProcName = "[dbo].[Products_GetRecordCountByCategoryID]";
             return await this.GetRecordCountSharedAsync(storedProcName, "categoryID", categoryID);
         }
 
         /// <summary>
         /// Gets the total number of records in the Products table based on search parameters
         /// </summary>
         async Task<int> IProductRepository.GetRecordCountDynamicWhereAsync(intproductIDstring productNameintsupplierIDintcategoryIDstring quantityPerUnitdecimalunitPrice, Int16? unitsInStock, Int16? unitsOnOrder, Int16? reorderLevelbooldiscontinued)
         {
             List<SqlParameter> sqlParamList = new();
             string storedProcName = "[dbo].[Products_GetRecordCountWhereDynamic]";
             int recordCount = 0;
 
             // search parameters
             this.AddSearchCommandParamsShared(sqlParamList, productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued);
 
             // get the data
             DataTable dt = await DatabaseFunctions.GetDataTableAsync(_connectionString, storedProcName, sqlParamList, _commandType);
 
             // convert data to an int - record count
             if (dt is not null && dt.Rows.Count > 0)
                 recordCount = (int)dt.Rows[0]["RecordCount"];
 
             return recordCount;
         }
 
         /// <summary>
         /// Selects Products table records sorted by the sortByExpression and returns records from the startRowIndex with rows (# of rows)
         /// </summary>
         async Task<DataTable> IProductRepository.SelectSkipAndTakeAsync(string sortByExpressionint startRowIndexint rows)
         {
             string storedProcName = "[dbo].[Products_SelectSkipAndTake]";
             return await this.SelectSharedAsync(storedProcName, nullnull, sortByExpression, startRowIndex, rows);
         }
 
         /// <summary>
         /// Selects records by SupplierID as a collection (List) of Product sorted by the sortByExpression.
         /// </summary>
         async Task<DataTable> IProductRepository.SelectSkipAndTakeBySupplierIDAsync(string sortByExpressionint startRowIndexint rowsintsupplierID)
         {
             string storedProcName = "[dbo].[Products_SelectSkipAndTakeBySupplierID]";
             return await this.SelectSharedAsync(storedProcName, "supplierID", supplierID, sortByExpression, startRowIndex, rows);
         }
 
         /// <summary>
         /// Selects records by CategoryID as a collection (List) of Product sorted by the sortByExpression.
         /// </summary>
         async Task<DataTable> IProductRepository.SelectSkipAndTakeByCategoryIDAsync(string sortByExpressionint startRowIndexint rowsintcategoryID)
         {
             string storedProcName = "[dbo].[Products_SelectSkipAndTakeByCategoryID]";
             return await this.SelectSharedAsync(storedProcName, "categoryID", categoryID, sortByExpression, startRowIndex, rows);
         }
 
         /// <summary>
         /// Selects Products table records sorted by the sortByExpression and returns records from the startRowIndex with rows (# of records) based on search parameters
         /// </summary>
         async Task<DataTable> IProductRepository.SelectSkipAndTakeDynamicWhereAsync(intproductIDstring productNameintsupplierIDintcategoryIDstring quantityPerUnitdecimalunitPrice, Int16? unitsInStock, Int16? unitsOnOrder, Int16? reorderLevelbooldiscontinuedstring sortByExpressionint startRowIndexint rows)
         {
             List<SqlParameter> sqlParamList = new();
             string storedProcName = "[dbo].[Products_SelectSkipAndTakeWhereDynamic]";
 
             // select, skip, take, sort parameters
             DatabaseFunctions.AddSelectSkipAndTakeParams(sqlParamList, sortByExpression, startRowIndex, rows);
 
             // search parameters
             this.AddSearchCommandParamsShared(sqlParamList, productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued);
 
             // get and return the data
             return await DatabaseFunctions.GetDataTableAsync(_connectionString,  storedProcName, sqlParamList, _commandType);
         }
 
         /// <summary>
         /// Selects all Products
         /// </summary>
         async Task<DataTable> IProductRepository.SelectAllAsync()
         {
             string storedProcName = "[dbo].[Products_SelectAll]";
             return await this.SelectSharedAsync(storedProcName, String.Empty, nullnullnullnull);
         }
 
         /// <summary>
         /// Selects records based on the passed filters as a collection (List) of Product.
         /// </summary>
         async Task<DataTable> IProductRepository.SelectAllDynamicWhereAsync(intproductIDstring productNameintsupplierIDintcategoryIDstring quantityPerUnitdecimalunitPrice, Int16? unitsInStock, Int16? unitsOnOrder, Int16? reorderLevelbooldiscontinued)
         {
             string storedProcName = "[dbo].[Products_SelectAllWhereDynamic]";
             List<SqlParameter> sqlParamList = new();
 
             // search parameters
             this.AddSearchCommandParamsShared(sqlParamList, productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued);
 
             // get and return the data
             return await DatabaseFunctions.GetDataTableAsync(_connectionString, storedProcName, sqlParamList, _commandType);
         }
 
         /// <summary>
         /// Selects all Products by Suppliers, related to column SupplierID
         /// </summary>
         async Task<DataTable> IProductRepository.SelectProductCollectionBySupplierIDAsync(int supplierID)
         {
             string storedProcName = "[dbo].[Products_SelectAllBySupplierID]";
             return await this.SelectSharedAsync(storedProcName, "supplierID", supplierID, nullnullnull);
         }
 
         /// <summary>
         /// Selects all Products by Categories, related to column CategoryID
         /// </summary>
         async Task<DataTable> IProductRepository.SelectProductCollectionByCategoryIDAsync(int categoryID)
         {
             string storedProcName = "[dbo].[Products_SelectAllByCategoryID]";
             return await this.SelectSharedAsync(storedProcName, "categoryID", categoryID, nullnullnull);
         }
 
         /// <summary>
         /// Selects ProductID and ProductName columns for use with a DropDownList web control
         /// </summary>
         async Task<DataTable> IProductRepository.SelectProductDropDownListDataAsync()
         {
             List<SqlParameter> sqlParamList = new();
             string storedProcName = "[dbo].[Products_SelectDropDownListData]";
 
             // get and return the data
             return await DatabaseFunctions.GetDataTableAsync(_connectionString, storedProcName, sqlParamList, _commandType);
         }
 
         /// <summary>
         /// Deletes a record based on primary key(s)
         /// </summary>
         async Task IProductRepository.DeleteAsync(int productID)
         {
             List<SqlParameter> sqlParamList = new();
             string storedProcName = "[dbo].[Products_Delete]";
 
             DatabaseFunctions.AddSqlParameter(sqlParamList, "@productID", productID);
 
             // delete record
             await DatabaseFunctions.ExecuteSqlCommandAsync(_connectionString, storedProcName, sqlParamList, _commandType, DatabaseOperationType.Delete);
         }
 
         /// <summary>
         /// Inserts a record
         /// </summary>
         async Task<int> IProductRepository.InsertAsync(Product objProduct)
         {
             return await this.InsertUpdateAsync(objProduct, DatabaseOperationType.Create);
         }
 
         /// <summary>
         /// Updates a record
         /// </summary>
         async Task IProductRepository.UpdateAsync(Product objProduct) =>
             await this.InsertUpdateAsync(objProduct, DatabaseOperationType.Update);
 
         /// <summary>
         /// Creates a new Product or Updates an existing Product base on the Operation Type.
         /// </summary>
         /// <param name="objProduct">The data to Create or Update</param>
         /// <param name="operationType">Accepts Create or Update only</param>
         /// <returns>Newly Created ProductID for create, or the ProductID for the record to be updated</returns>
         private async Task<intInsertUpdateAsync(Product objProduct, DatabaseOperationType operationType)
         {
             if (operationType == DatabaseOperationType.RetrieveDataTable || operationType == DatabaseOperationType.Delete)
                 throw new ArgumentException("Invalid DatabaseOperationType!  Acceptable operation types are: Create or Update only.""operationType: " + operationType.ToString());
 
             List<SqlParameter> sqlParamList = new();
 
             // set values for the Product to be created or updated
             int newlyCreatedProductID = objProduct.ProductID;
 
             // get the SQL script for the create or update operation
             string storedProcName;
 
             if (operationType == DatabaseOperationType.Update)
                 storedProcName = "[dbo].[Products_Update]";
             else
                 storedProcName = "[dbo].[Products_Insert]";
 
             object supplierID = objProduct.SupplierID;
             object categoryID = objProduct.CategoryID;
             object quantityPerUnit = objProduct.QuantityPerUnit;
             object unitPrice = objProduct.UnitPrice;
             object unitsInStock = objProduct.UnitsInStock;
             object unitsOnOrder = objProduct.UnitsOnOrder;
             object reorderLevel = objProduct.ReorderLevel;
 
             if (objProduct.SupplierID is null)
                 supplierID = System.DBNull.Value;
 
             if (objProduct.CategoryID is null)
                 categoryID = System.DBNull.Value;
 
             if (String.IsNullOrEmpty(objProduct.QuantityPerUnit))
                 quantityPerUnit = System.DBNull.Value;
 
             if (objProduct.UnitPrice is null)
                 unitPrice = System.DBNull.Value;
 
             if (objProduct.UnitsInStock is null)
                 unitsInStock = System.DBNull.Value;
 
             if (objProduct.UnitsOnOrder is null)
                 unitsOnOrder = System.DBNull.Value;
 
             if (objProduct.ReorderLevel is null)
                 reorderLevel = System.DBNull.Value;
 
             // update parameters
             if (operationType == DatabaseOperationType.Update)
             {
             DatabaseFunctions.AddSqlParameter(sqlParamList"@productID"objProduct.ProductID);
             }
 
             DatabaseFunctions.AddSqlParameter(sqlParamList"@productName"objProduct.ProductName);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@supplierID"supplierID);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@categoryID"categoryID);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@quantityPerUnit"quantityPerUnit);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@unitPrice"unitPrice);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsInStock"unitsInStock);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsOnOrder"unitsOnOrder);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@reorderLevel"reorderLevel);
             DatabaseFunctions.AddSqlParameter(sqlParamList"@discontinued"objProduct.Discontinued);
 
             // create a new record or update an existing record
             if (operationType == DatabaseOperationType.Update)
                 await DatabaseFunctions.ExecuteSqlCommandAsync(_connectionString, storedProcNamesqlParamList, _commandType, DatabaseOperationType.Update);
             else
                 newlyCreatedProductID = (int)await DatabaseFunctions.ExecuteSqlCommandAsync(_connectionString, storedProcNamesqlParamList, _commandType, DatabaseOperationType.Create, false);
 
             return newlyCreatedProductID;
         }
 
         /// <summary>
         /// Gets the Number of Records from the Products table based on the SQL script.
         /// </summary>
         /// <param name="storedProcName">Stored Procedure Name</param>
         /// <param name="parameterName">Parameter Name used to create the SqlParameter</param>
         /// <param name="parameterValue">Parameter Value used to create the SqlParamater</param>
         /// <returns>Record Count - int</returns>
         private async Task<intGetRecordCountSharedAsync(string storedProcNamestring parameterNameobject parameterValue)
         {
             List<SqlParametersqlParamList = new();
 
             // set SqlParameter when parameter name is one of the foreign keys
             if (parameterName is not null)
                 this.SetForeignKeySqlParameter(sqlParamListparameterNameparameterValue);
 
             return await DatabaseFunctions.GetRecordCountAsync(_connectionString, storedProcNamesqlParamList, _commandType);
         }
 
         /// <summary>
         /// Selects records from the database.  Used by other methods that selects data from the database.
         /// </summary>
         /// <param name="storedProcName">Stored Procedure Name</param>
         /// <param name="parameterName">Parameter Name used to create the SqlParameter</param>
         /// <param name="parameterValue">Parameter Value used to create the SqlParamater</param>>
         /// <param name="sortByExpression">Sort by Column Name.  E.g. "ProductID" sorts by ProductID in ascending order.  "ProductID desc" sorts by ProductID in descending order.</param>
         /// <param name="startRowIndex">Optional.  Index of the row to start taking data from.  E.g. 0 will start taking data from the 1st row, 9 will start taking data from the 10th row.</param>
         /// <param name="rows">Optional.  Number of rows to take.</param>
         /// <returns>List of Products</returns>
         private async Task<DataTableSelectSharedAsync(string storedProcNamestring parameterNameobject parameterValuestring sortByExpressionintstartRowIndexintrows)
         {
             List<SqlParametersqlParamList = new();
 
             // select, skip, take, sort sql parameters
             if (!String.IsNullOrEmpty(sortByExpression) && startRowIndex is not null && rows is not null)
                 DatabaseFunctions.AddSelectSkipAndTakeParams(sqlParamListsortByExpressionstartRowIndex.Value, rows.Value);
 
             // set SqlParameter when parameter name is one of the foreign keys
             if (parameterName is not null)
                 this.SetForeignKeySqlParameter(sqlParamListparameterNameparameterValue);
 
             // get and return the data
             return await DatabaseFunctions.GetDataTableAsync(_connectionString, storedProcNamesqlParamList, _commandType);
         }
 
         /// <summary>
         /// Adds search parameters to the List of SqlParameter
         /// </summary>
         private void AddSearchCommandParamsShared(List<SqlParametersqlParamListintproductIDstring productNameintsupplierIDintcategoryIDstring quantityPerUnitdecimalunitPriceInt16unitsInStockInt16unitsOnOrderInt16reorderLevelbooldiscontinued)
         {
              if(productID is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@productID"productID);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@productID", System.DBNull.Value);
 
              if(!String.IsNullOrEmpty(productName))
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@productName"productName);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@productName", System.DBNull.Value);
 
              if(supplierID is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@supplierID"supplierID);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@supplierID", System.DBNull.Value);
 
              if(categoryID is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@categoryID"categoryID);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@categoryID", System.DBNull.Value);
 
              if(!String.IsNullOrEmpty(quantityPerUnit))
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@quantityPerUnit"quantityPerUnit);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@quantityPerUnit", System.DBNull.Value);
 
              if(unitPrice is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitPrice"unitPrice);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitPrice", System.DBNull.Value);
 
              if(unitsInStock is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsInStock"unitsInStock);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsInStock", System.DBNull.Value);
 
              if(unitsOnOrder is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsOnOrder"unitsOnOrder);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@unitsOnOrder", System.DBNull.Value);
 
              if(reorderLevel is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@reorderLevel"reorderLevel);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@reorderLevel", System.DBNull.Value);
 
              if(discontinued is not null)
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@discontinued"discontinued);
              else
                  DatabaseFunctions.AddSqlParameter(sqlParamList"@discontinued", System.DBNull.Value);
 
         }
 
         /// <summary>
         /// Set the SqlParameter for the specific foreign key.
         /// </summary>
         /// <param name="sqlParamList">List of SqlParameters</param>
         /// <param name="parameterName">Parameter Name used to create the SqlParameter</param>
         /// <param name="parameterValue">Parameter Value used to create the SqlParamater</param>
         private void SetForeignKeySqlParameter(List<SqlParametersqlParamListstring parameterNameobject parameterValue)
         {
             // add the (foreign key) sql parameters
             if (!String.IsNullOrEmpty(parameterName))
             {
                 if (parameterValue is null)
                     parameterValue = DBNull.Value;
 
                 switch (parameterName)
                 {
                     case "supplierID":
                         DatabaseFunctions.AddSqlParameter(sqlParamList"@supplierID"parameterValue);
                         break;
                     case "categoryID":
                         DatabaseFunctions.AddSqlParameter(sqlParamList"@categoryID"parameterValue);
                         break;
                     default:
                         break;
                 }
             }
         }
     }
}