using System;
using System.Collections.Generic;
using System.IO;
using LicenseSpring;

namespace ManagementConsoleSample
{
    internal class ManagementSample
    {
        public ManagementSample()
        {
            ManagementConfiguration configuration = LSConfig.CreateManagementConfiguration();
            _service = new ManagementService(configuration);
            // Create customer
            _customer = new Customer()
            {
                CompanyName = "TestCompany",
                Email = "someone@test.com"
            };
        }

        public string OrderKeyBasedLicenses()
        {
            Console.WriteLine("Generating keys.");

            // Your product code goes here
            string productCode = "kp";

            // Generate 2 license keys
            string[] licenseKeys = _service.LicenseService.GenerateLicenseKey(productCode, 2);
            foreach (string key in licenseKeys)
            {
                Console.WriteLine(key);
            }

            // Create licenses
            LicenseDetails license1 = new LicenseDetails();
            license1.LicenseKey = licenseKeys[0];
            license1.EnableMaintenancePeriod = true;
            license1.MaintenanceDuration = "3m";
            license1.Metadata = @"{""Metadata key"": ""For a single license.""}";
            license1.ProductFeatures = CreateProductFeatures();

            LicenseDetails license2 = new LicenseDetails
            {
                LicenseKey = licenseKeys[1],
                EnableMaintenancePeriod = true,
                MaintenancePeriod = "2027-12-31",
                Type = LicenseType.TimeLimited,
                ValidityDuration = "14m"
            };

            List<LicenseDetails> licenses = new List<LicenseDetails>
            {
                license1,
                license2
            };

            // Order 2 licenses
            Order orderInfo = new Order(_customer);
            orderInfo.Metadata = @"{""This is"": ""A key from the management SDK""}";
            string keyBasedOrderId = _service.OrderService.CreateOrder(licenses.ToArray(), productCode, orderInfo);

            // Append one more license to existing order
            licenseKeys = _service.LicenseService.GenerateLicenseKey(productCode);
            LicenseDetails license3 = new LicenseDetails
            {
                LicenseKey = licenseKeys[0],
                Type = LicenseType.Consumption,
                MaxConsumptions = 100,
                AllowOverages = true,
                MaxOverages = 50,
                ResetConsumption = true,
                ConsumptionPeriod = ConsumptionPeriod.Weekly
            };
            licenses.Clear();
            licenses.Add(license3);

            orderInfo = new Order(keyBasedOrderId, _customer);
            orderInfo.Note = "This is a note for the order. It will be added to license3.";
            keyBasedOrderId = _service.OrderService.CreateOrder(licenses.ToArray(), productCode, orderInfo);

            // Add a license with a license policy
            string licenseKey = _service.LicenseService.GenerateLicenseKey(productCode)[0];
            LicenseDetails license4 = new LicenseDetails
            {
                LicenseKey = licenseKey,
            };

            // Your policy code
            string policyCode = "PolicyCode";
            license4.SetPolicyCode(policyCode);

            orderInfo = new Order(keyBasedOrderId, _customer);
            keyBasedOrderId = _service.OrderService.CreateOrder(new LicenseDetails[] { license4 }, productCode, orderInfo);

            Console.WriteLine("Order was created successfully.");
            Console.WriteLine("Order ID: {0}", keyBasedOrderId);
            return keyBasedOrderId;
        }

        /// <summary>
        /// Creates an array of example <see cref="ProductFeature" /> objects. <br /><b> Note: </b>
        /// Product features with invalid codes are ignored by the API. If a non-existent feature
        /// code is provided, the license will be created without that feature. <br /><b> Note 2:
        /// </b><see cref="ProductFeature" /> objects passed to the
        /// <see cref="LicenseDetails.ProductFeatures" /> array are decoupled, meaning you can't
        /// edit them once you pass them to the setter. If you need to update the values always
        /// create and pass a new array.
        /// </summary>
        /// <returns>
        /// An array of product features with predefined values.
        /// </returns>
        private ProductFeature[] CreateProductFeatures()
        {
            return new ProductFeature[]
            {
                new ProductFeature(code: "TF1")
                {
                    MaxConsumption = 0,
                    AllowUnlimitedConsumptions = false,
                    AllowNegativeConsumptions = true,
                    AllowOverages = false,
                    MaxOverages = 0,
                    ResetConsumption = false,
                    ConsumptionPeriod = ConsumptionPeriod.None,
                    ExpiryDate = default,
                    ValidityDurationValue = 0,
                    ValidityDurationUnit = ValidityDurationUnit.None,
                    Metadata = "{ \"This\": \"Is test product feature 1 metadata\" }",
                    IsOfflineFloating = false,
                    IsFLoatingCloud = false,
                    FloatingTimeout = 120,
                    FLoatingUsers = 1
                },
                new ProductFeature(code: "APV")
                {
                    MaxConsumption = 0,
                    AllowUnlimitedConsumptions = false,
                    AllowNegativeConsumptions = true,
                    AllowOverages = false,
                    MaxOverages = 0,
                    ResetConsumption = false,
                    ConsumptionPeriod = ConsumptionPeriod.None,
                    ExpiryDate = default,
                    ValidityDurationValue = 0,
                    ValidityDurationUnit = ValidityDurationUnit.None,
                    Metadata = "{}",
                    IsOfflineFloating = false,
                    IsFLoatingCloud = false,
                    FloatingTimeout = 120,
                    FLoatingUsers = 1
                },
                new ProductFeature(code: "FCF")
                {
                    MaxConsumption = 0,
                    AllowUnlimitedConsumptions = false,
                    AllowNegativeConsumptions = true,
                    AllowOverages = false,
                    MaxOverages = 0,
                    ResetConsumption = false,
                    ConsumptionPeriod = ConsumptionPeriod.None,
                    ExpiryDate = new DateTime(2028, 01, 31),
                    ValidityDurationValue = 0,
                    ValidityDurationUnit = ValidityDurationUnit.None,
                    Metadata = "{}",
                    IsOfflineFloating = false,
                    IsFLoatingCloud = true,
                    FloatingTimeout = 240,
                    FLoatingUsers = 5
                },
                new ProductFeature(code: "OFF")
                {
                    MaxConsumption = 1,
                    AllowUnlimitedConsumptions = true,
                    AllowNegativeConsumptions = true,
                    AllowOverages = true,
                    MaxOverages = 6,
                    ResetConsumption = true,
                    ConsumptionPeriod = ConsumptionPeriod.Weekly,
                    ExpiryDate = default,
                    ValidityDurationValue = 5,
                    ValidityDurationUnit = ValidityDurationUnit.Days,
                    Metadata = "{}",
                    IsOfflineFloating = true,
                    IsFLoatingCloud = false,
                    FloatingTimeout = 120,
                    FLoatingUsers = 3
                },
                new ProductFeature(code: "TF2")
                {
                    MaxConsumption = 1,
                    AllowUnlimitedConsumptions = true,
                    AllowNegativeConsumptions = true,
                    AllowOverages = false,
                    MaxOverages = 0,
                    ResetConsumption = false,
                    ConsumptionPeriod = ConsumptionPeriod.Daily,
                    ExpiryDate = default,
                    ValidityDurationValue = 0,
                    ValidityDurationUnit = ValidityDurationUnit.None,
                    Metadata = "{ \"Also this\": \"Is test product feature 2 metadata\" }",
                    IsOfflineFloating = false,
                    IsFLoatingCloud = false,
                    FloatingTimeout = 120,
                    FLoatingUsers = 1
                },
                new ProductFeature(code: "TF3")
                {
                    MaxConsumption = 0,
                    AllowUnlimitedConsumptions = false,
                    AllowNegativeConsumptions = true,
                    AllowOverages = false,
                    MaxOverages = 0,
                    ResetConsumption = false,
                    ConsumptionPeriod = ConsumptionPeriod.None,
                    ExpiryDate = default,
                    ValidityDurationValue = 2,
                    ValidityDurationUnit = ValidityDurationUnit.Months,
                    Metadata = "{}",
                    IsOfflineFloating = false,
                    IsFLoatingCloud = false,
                    FloatingTimeout = 120,
                    FLoatingUsers = 1
                }
            };
        }

        public string OrderUserBasedLicense()
        {
            string productCode = "up";

            LicenseDetails license = new LicenseDetails
            {
                EnableMaintenancePeriod = true,
                MaintenanceDuration = "7m", // 7 months
                Type = LicenseType.Consumption,
                MaxConsumptions = 10,
                LicenseUsers = new LicenseUser[0],
                MaxActivations = 2
            };

            Order order = new Order(_customer);

            string userBasedOrderId = _service.OrderService.CreateOrder(new LicenseDetails[] { license }, productCode, order);

            Console.WriteLine("Order was created successfully.");
            Console.WriteLine("Order ID: {0}", userBasedOrderId);
            return userBasedOrderId;
        }

        public LicenseUser AssignUserToLicense(License license)
        {
            LicenseUser user = new LicenseUser()
            {
                Email = "example@email.com",
                IsManager = false
            };
            _service.LicenseService.AssignUser(license.Id, user);

            //Check if the user is assigned
            ListLicensesRequestDto dto = new ListLicensesRequestDto()
            {
                IdIn = license.Id
            };
            License[] licenses = _service.LicenseService.ListLicenses(dto, out uint count);

            if (count == 1 && licenses != null)
            {
                if (licenses[0].LicenseUsers.Count > 0)
                {
                    foreach (LicenseUser licenseUser in licenses[0].LicenseUsers)
                    {
                        if (licenseUser.TrueEmail == user.Email)
                        {
                            Console.WriteLine("User successfully added to the license: " + user.Email);
                            return licenseUser;
                        }
                    }
                }
                else if (licenses[0].LicenseUser != null && licenses[0].LicenseUser.TrueEmail == user.Email)
                {
                    Console.WriteLine("User successfully added to the license: " + user.Email);
                    return licenses[0].LicenseUser;
                }
                Console.WriteLine("User was not added to the license.");
            }
            else
            {
                Console.WriteLine("License not found.");
            }
            return null;
        }

        public LicenseUser EditLicenseUser(LicenseUser user)
        {
            user.FirstName = "SomeOtherName";
            LicenseUser updated = _service.LicenseService.UpdateLicenseUser(user);

            if (updated != null && updated.FirstName == user.FirstName)
            {
                Console.WriteLine("User successfully updated");
            }
            else
            {
                Console.WriteLine("User was not updated");
            }

            string newPassword = "new_password";
            _service.LicenseService.SetUserPassword(user.Id, newPassword);

            //check if the password was set
            ListUsersRequestDto dto = new ListUsersRequestDto()
            {
                EmailContains = user.TrueEmail
            };
            LicenseUser[] ret = _service.LicenseService.ListLicenseUsers(dto, out uint count);

            if (count == 1 && ret != null)
            {
                if (ret[0].Password == newPassword)
                {
                    Console.WriteLine("Password was successfully updated.");
                    return ret[0];
                }
                Console.WriteLine("Password was not updated.");
            }
            else
            {
                Console.WriteLine("License user not found.");
            }

            return null;
        }

        public License[] GetOrderLicenses(string orderStoreId)
        {
            //Get order by store id
            ListOrdersRequestDto dto = new ListOrdersRequestDto()
            {
                ClientOrderId = orderStoreId
            };
            Order[] ret = _service.OrderService.ListOrders(dto, out uint count);

            if (count == 1 && ret != null)
            {
                return _service.OrderService.ListOrderLicenses(ret[0].Id);
            }
            else
            {
                Console.WriteLine("Order not found.");
            }

            return null;
        }

        public License GetLicenseById(ulong licenseId)
        {
            ListLicensesRequestDto listLicensesDto = new ListLicensesRequestDto()
            {
                IdIn = licenseId
            };
            License[] ret = _service.LicenseService.ListLicenses(listLicensesDto, out uint count);

            if (count == 1 && ret != null)
            {
                return ret[0];
            }
            else
            {
                Console.WriteLine("License not found.");
            }

            return null;
        }

        public License GetLicenseByKey(string licenseKey)
        {
            ListLicensesRequestDto listLicensesDto = new ListLicensesRequestDto()
            {
                LicenseKeyContains = licenseKey
            };
            License[] ret = _service.LicenseService.ListLicenses(listLicensesDto, out uint count);

            if (count == 1 && ret != null)
            {
                return ret[0];
            }
            else
            {
                Console.WriteLine("License not found.");
            }

            return null;
        }

        public License UpdateLicense(License license)
        {
            int maxActivations = (int)(license.MaxActivations + 10);
            UpdateLicenseRequestDto dto = new UpdateLicenseRequestDto()
            {
                MaxActivations = maxActivations,
                ValidityPeriod = "2020-10-15",
                IsTrial = false,
                Metadata = @"{""Updated value"": ""of the metadata, from management SDK""}"
            };
            _service.LicenseService.UpdateLicense(dto, license.Id);

            AddLicenseProductFeatureDto addProductFeatureDto = new AddLicenseProductFeatureDto()
            {
                ProductFeature = "abc"
            };

            // This will throw a LicenseSpringException if product feature "abc" doesn't exist.
            _service.LicenseService.UpdateLicenseProductFeatures(new AddLicenseProductFeatureDto[1] { addProductFeatureDto }, license.Id);

            //Check if license is updated
            ListLicensesRequestDto listLicensesDto = new ListLicensesRequestDto()
            {
                IdIn = license.Id
            };
            License[] ret = _service.LicenseService.ListLicenses(listLicensesDto, out uint count);

            if (count == 1 && ret != null)
            {
                if (ret[0].MaxActivations == maxActivations)
                {
                    Console.WriteLine("License successfully updated.");
                }
                else
                {
                    Console.WriteLine("License was not updated.");
                }
                return ret[0];
            }
            else
            {
                Console.WriteLine("License not found.");
            }

            return null;
        }

        public void CreateUpdateFileByKey(string licenseKey)
        {
            Console.WriteLine("Saving license information to a file.");
            string filePath = "";//The directory where the file will be saved
            string fileName = "";//File name without extension
            string path = Path.Combine(filePath, fileName);
            if (!String.IsNullOrEmpty(path))
            {
                _service.LicenseService.SaveLicenseByKey(licenseKey, path);
                Console.WriteLine("File {0} created in directory {1}", fileName, filePath);
            }
        }

        public Device[] GetLicenseDevices(License license)
        {
            if (license == null)
            {
                return null;
            }
            ulong licenseId = license.Id;
            List<Device> licenseDevices = new List<Device>();
            int limit = 20;
            int offset = 0;
            ListDevicesRequestDto dto = new ListDevicesRequestDto()
            {
                License = licenseId
            };
            uint count;
            do
            {
                dto.Limit = limit;
                dto.Offset = offset;
                Device[] range = _service.DeviceService.ListDevices(dto, out count);
                if (range != null)
                    licenseDevices.AddRange(range);
                offset += limit;
            } while (offset < count);
            return licenseDevices.ToArray();
        }

        public void UpdateDeviceVariables(Device device)
        {
            if (device == null)
            {
                Console.WriteLine("No device found.");
                return;
            }
            CreateDeviceVariableDto dto = new CreateDeviceVariableDto()
            {
                Device = device.Id,
                Variable = "TestVariable",
                Value = "TestValue"
            };
            DeviceVariable variable = _service.DeviceService.CreateDeviceVariable(dto);

            if (variable.Value == dto.Value && variable.Variable == dto.Variable)
            {
                Console.WriteLine("Device variable created: " + variable.Variable + "  " + variable.Value);
            }
            else
            {
                Console.WriteLine("Device variable was not created");
            }

            dto.Value = "Updated value";
            DeviceVariable updated = _service.DeviceService.UpdateDeviceVariable(dto, variable.ID);

            if (updated.Value == dto.Value)
            {
                Console.WriteLine("Device variable updated: " + updated.Variable + "  " + updated.Value);
            }
            else
            {
                Console.WriteLine("Device variable was not updated");
            }

            _service.DeviceService.DeleteDeviceVariable(updated.ID);

            List<DeviceVariable> variables = new List<DeviceVariable>();
            int limit = 20;
            int offset = 0;
            ListDeviceVariablesRequestDto listDeviceVariablesDto = new ListDeviceVariablesRequestDto()
            {
                Device = device.Id
            };
            uint count;
            do
            {
                listDeviceVariablesDto.Limit = limit;
                listDeviceVariablesDto.Offset = offset;
                DeviceVariable[] range = _service.DeviceService.ListDeviceVariables(listDeviceVariablesDto, out count);
                if (range != null)
                {
                    variables.AddRange(range);
                }
                offset += limit;
            } while (offset < count);
            if (variables.Find(c => c.Variable == variable.Variable && c.Value == variable.Value) == null)
            {
                Console.WriteLine("Device variable deleted");
            }
            else
            {
                Console.WriteLine("Device variable was not deleted");
            }
        }

        public CustomField CreateCustomField(License license)
        {
            //create product custom field
            CreateProductCustomFieldDto dto = new CreateProductCustomFieldDto()
            {
                Product = license.Product.Id,
                Name = "Test Custom Field",
                DefaultValue = "Default"
            };
            ProductCustomField productCustomField = _service.ProductService.CreateProductCustomField(dto);
            if (productCustomField != null)
            {
                Console.WriteLine("Product custom field created: " + productCustomField.Name + "  " + productCustomField.DefaultValue);
            }
            else
            {
                return null;
            }

            //assign it to the license
            CreateCustomFieldDto customFieldDto = new CreateCustomFieldDto()
            {
                ProductCustomFieldId = productCustomField.Id,
                LicenseId = license.Id,
                Value = "New Value"
            };
            CustomField customField = _service.LicenseService.CreateLicenseCustomField(customFieldDto);

            //check if the field was assigned
            ListLicensesRequestDto listLicensesDto = new ListLicensesRequestDto()
            {
                IdIn = license.Id
            };
            License[] licenses = _service.LicenseService.ListLicenses(listLicensesDto, out uint count);
            if (count == 1 && licenses != null)
            {
                if (licenses[0].CustomFields != null)
                {
                    foreach (CustomField f in licenses[0].CustomFields)
                    {
                        if (f.Name == customField.Name && f.Value == customField.Value)
                        {
                            Console.WriteLine("License custom field added: " + customField.Name + "  " + customField.Value);
                            return customField;
                        }
                    }
                }
                Console.WriteLine("License custom field was not added.");
            }
            else
            {
                Console.WriteLine("License not found.");
            }
            return null;
        }

        public void ListCustomers()
        {
            ListCustomersRequestDto dto = new ListCustomersRequestDto();
            Customer[] customers = _service.CustomerService.ListCustomers(dto, out uint count);

            Console.WriteLine("Customers count: " + customers.Length);
            for (int i = 0; i < customers.Length; i++)
            {
                Customer customer = customers[i];
                Console.WriteLine($"Customer {i}:");
                Console.WriteLine($"Name: {customer.FirstName} {customer.LastName}");
                Console.WriteLine($"Email: {customer.Email}");
                Console.WriteLine($"Metadata: {customer.Metadata}");
                Console.WriteLine("====================");
            }

            // Filter customers by exact email
            string email = customers[0].Email;
            ListCustomersRequestDto dtoFiltered = new ListCustomersRequestDto()
            {
                EmailExact = email
            };
            Customer[] filteredCustomers = _service.CustomerService.ListCustomers(dtoFiltered, out uint countFiltered);
        }

        public void ListProducts()
        {
            ListProductsRequestDto dto = new ListProductsRequestDto();
            Product[] products = _service.ProductService.ListProducts(dto, out uint count);

            Console.WriteLine("Products count: " + products.Length);
            for (int i = 0; i < products.Length; i++)
            {
                Product product = products[i];
                Console.WriteLine($"Product {i}");
                Console.WriteLine($"Name: {product.ProductName}");
                Console.WriteLine($"Short code: {product.ShortCode}");
                Console.WriteLine($"Is active: {product.Active}");
                Console.WriteLine($"Metadata: {product.Metadata}");
                foreach (var feature in product.ProductFeatures)
                {
                    Console.WriteLine($"Product feature code: {feature.Code}");
                    Console.WriteLine($"Product feature type: {feature.FeatureType}");
                    Console.WriteLine($"Product feature metadata: {feature.Metadata}");
                    Console.WriteLine("====================");
                }
                Console.WriteLine("====================");
            }
        }

        public uint ListLicensesCount()
        {
            ListLicensesRequestDto dto = new ListLicensesRequestDto();
            dto.LicenseKey = "some-key";
            //dto.IdIn = 1234567890123456;
            //dto.CustomerAccountCode = "ACCD";
            //dto.OrderCustomerAccount = "ORDACC";

            // This call can take a long time number of licenses is large.
            License[] licenses = _service.LicenseService.ListLicenses(dto, out uint counter); // This can return null.
            return counter;
        }

        private Customer _customer;

        private ManagementService _service;
    }
}