using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using EpsonPrinter.Model;
using Microsoft.Extensions.Configuration;

namespace EpsonPrinter.Services
{
    public class EpsonPrintService
    {
        private readonly IConfiguration _config;
        private readonly string _devicePath;

        public EpsonPrintService(IConfiguration config)
        {
            _config = config;
            _devicePath = _config["PrintSettings:DevicePath"] ?? "/dev/usb/lp0";
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        }

        public void PrintReceipt(ReceiptModel model)
        {
            ICommandEmitter e = new EPSON();
            InitAndWrite(e);

            List<byte[][]> printBytes = new List<byte[][]>();
            PrintHeader printHeader = new PrintHeader(e);
            PrintBody printBody = new PrintBody(e, _config);
            PrintFooter printFooter = new PrintFooter(e);

            var header = (model.Header != null && model.Header.Any()) ? model.Header : BuildDefaultHeader();
            var body = model.BodyModel ?? BuildDefaultBody();
            if (string.IsNullOrWhiteSpace(body.Staff))
            {
                body.Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt";
            }
            if (string.IsNullOrWhiteSpace(body.ReceiptTime))
            {
                body.ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss");
            }
            if (body.ReceiptNumber <= 0)
            {
                body.ReceiptNumber = Math.Abs(Environment.TickCount % 1000000);
            }
            body.Products ??= new List<BodyProductModel>();
            if (!body.Products.Any())
            {
                body.Products.Add(new BodyProductModel
                {
                    Product = "Standard vare",
                    NoOfProduct = 1,
                    Price = 99.00m,
                    TotalPrice = 99.00m
                });
            }
            if (body.TotalPrice <= 0)
            {
                body.TotalPrice = body.Products.Sum(x => x.TotalPrice <= 0 ? x.Price * x.NoOfProduct : x.TotalPrice);
            }
            if (body.TotalVat <= 0)
            {
                body.TotalVat = Math.Round(body.TotalPrice * 0.25m, 2);
            }

            var footer = (model.Footer != null && model.Footer.Any()) ? model.Footer : BuildDefaultFooter();

            printBytes.AddRange(printHeader.Print(header));
            printBytes.AddRange(printBody.Print(body));
            printBytes.AddRange(printFooter.Print(footer));

            byte[][] array = printBytes.SelectMany(c => c).ToArray();
            Write(array);
            Write(e.FeedLines(5));
            Write(e.FullCut());
        }

        public void PrintSaleOfDay(SaleOfDayModel model)
        {
            ICommandEmitter e = new EPSON();
            InitAndWrite(e);
            PrintSaleOfDay(model, e);
        }

        private void InitAndWrite(ICommandEmitter e)
        {
            Write(e.Initialize());
            Write(e.Enable());
            Write(e.CodePage(CodePage.ISO8859_15_LATIN9));
            Write(e.EnableAutomaticStatusBack());
        }

        private void PrintSaleOfDay(SaleOfDayModel model, ICommandEmitter e)
        {
            List<byte[][]> data = new List<byte[][]>();

            byte[][] dateBytes =
            {
                e.SetStyles(PrintStyle.DoubleWidth),
                e.LeftAlign(),
                e.PrintLine($"Dato: {model.Date:dd-MM-yyyy}")
            };
            data.Add(dateBytes);

            byte[][] seperater =
            {
                e.SetStyles(PrintStyle.None),
                e.LeftAlign(),
                e.PrintLine("-----------------------------------")
            };
            data.Add(seperater);

            foreach (SaleOfDayDetail saleOfDayDetail in model.SaleOfDayDetail)
            {
                string pS = $"{saleOfDayDetail.Category} : {saleOfDayDetail.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}";
                byte[][] totalPrice =
                {
                    e.SetStyles(PrintStyle.DoubleWidth),
                    e.LeftAlign(),
                    e.PrintLine(pS)
                };
                data.Add(totalPrice);
            }

            data.Add(seperater);

            string totalSale = $"Total salg: {model.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}";
            byte[][] tSBytes =
            {
                e.SetStyles(PrintStyle.DoubleWidth),
                e.LeftAlign(),
                e.PrintLine(totalSale)
            };
            data.Add(tSBytes);

            string totalCustomer = $"Antal kunder: {model.TotalCustomers}";
            byte[][] tCBytes =
            {
                e.SetStyles(PrintStyle.DoubleWidth),
                e.LeftAlign(),
                e.PrintLine(totalCustomer)
            };
            data.Add(tCBytes);

            byte[][] array = data.SelectMany(c => c).ToArray();
            Write(array);
            Write(e.FeedLines(5));
            Write(e.FullCut());
        }

        private List<HeaderModel> BuildDefaultHeader()
        {
            return new List<HeaderModel>
            {
                new HeaderModel
                {
                    Value = _config["PrintSettings:Defaults:StoreName"] ?? "TableTop3D",
                    TextAlignment = Enums.AlignmentEnum.Center,
                    FeedingLines = 0,
                    PrintStyles = new PrintStyleModel { Bold = true, DoubleWidth = true, DoubleHeight = true }
                },
                new HeaderModel
                {
                    Value = _config["PrintSettings:Defaults:AddressLine1"] ?? string.Empty,
                    TextAlignment = Enums.AlignmentEnum.Center,
                    FeedingLines = 0,
                    PrintStyles = new PrintStyleModel { Bold = true }
                },
                new HeaderModel
                {
                    Value = _config["PrintSettings:Defaults:AddressLine2"] ?? string.Empty,
                    TextAlignment = Enums.AlignmentEnum.Center,
                    FeedingLines = 0,
                    PrintStyles = new PrintStyleModel()
                },
                new HeaderModel
                {
                    Value = _config["PrintSettings:Defaults:Cvr"] ?? string.Empty,
                    TextAlignment = Enums.AlignmentEnum.Center,
                    FeedingLines = 1,
                    PrintStyles = new PrintStyleModel()
                }
            }.Where(x => !string.IsNullOrWhiteSpace(x.Value)).ToList();
        }

        private FooterModel CreateFooterLine(string value, bool bold = false)
        {
            return new FooterModel
            {
                Value = value,
                TextAlignment = Enums.AlignmentEnum.Center,
                FeedingLines = 0,
                PrintStyles = new PrintStyleModel { Bold = bold }
            };
        }

        private List<FooterModel> BuildDefaultFooter()
        {
            var values = new[]
            {
                _config["PrintSettings:Defaults:FooterLine1"] ?? "Tak for dit køb!",
                _config["PrintSettings:Defaults:FooterLine2"] ?? string.Empty,
                _config["PrintSettings:Defaults:Phone"] ?? string.Empty
            }.Where(x => !string.IsNullOrWhiteSpace(x)).ToList();

            return values.Select((x, i) => CreateFooterLine(x, i == 0)).ToList();
        }

        private BodyModel BuildDefaultBody()
        {
            return new BodyModel
            {
                ReceiptNumber = Math.Abs(Environment.TickCount % 1000000),
                ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss"),
                Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt",
                Products = new List<BodyProductModel>
                {
                    new BodyProductModel
                    {
                        Product = "Standard vare",
                        NoOfProduct = 1,
                        Price = 99.00m,
                        TotalPrice = 99.00m
                    }
                },
                TotalPrice = 99.00m,
                TotalVat = 24.75m
            };
        }

        private void Write(byte[] data)
        {
            using var stream = new FileStream(_devicePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
            stream.Write(data, 0, data.Length);
            stream.Flush();
        }

        private void Write(byte[][] data)
        {
            foreach (var segment in data)
            {
                Write(segment);
            }
        }
    }
}
