public static AmazonStatement ReadAmazonCSV(string filepath)
        {
            AmazonStatement ret = new AmazonStatement(filepath);

            string[] lines = File.ReadAllText(filepath).Split('\n');

            AmazonTransaction prevEntry = null;

            foreach (string line in lines)
            {
                List <string> parts = FileHelpers.ParseCSVLine(line);

                if (parts.Count < 30)
                {
                    continue;
                }

                DateTime?date = TryToScanDate(parts[0], null, null);

                if (!date.HasValue)
                {
                    continue;
                }

                string amountStr = parts[29].Substring(1);
                double.TryParse(amountStr, out double amount);

                //Build an entry for this line by itself
                AmazonTransaction subEntry = new AmazonTransaction
                {
                    Date        = date.Value,
                    Amount      = -1 * amount,
                    Description = parts[2],
                    Category    = parts[3],
                    OrderNumber = parts[1]
                };

                //Special code here so we can combine multiple transactions with the same order number into one final transaction with sub-transactions

                AmazonTransaction wrapper = null;
                if (prevEntry != null)
                {
                    //If we had an entry in progress and this entry shares the same order number, add this entry as a sub-transaction
                    if (prevEntry.Items[0].OrderNumber.Equals(subEntry.OrderNumber))
                    {
                        wrapper              = prevEntry;
                        wrapper.Amount      += subEntry.Amount;
                        wrapper.Category    += ";" + subEntry.Category;
                        wrapper.Description += ";" + subEntry.Description;
                    }
                    else
                    {
                        //Commit the previous entry, the new entry starts a new order
                        ret.Transactions.Add(prevEntry);
                        prevEntry = null;
                    }
                }

                if (prevEntry == null)
                {
                    //Create a new wrapper to hold the sub-transactions
                    wrapper = new AmazonTransaction
                    {
                        Date        = date.Value,
                        Amount      = -1 * amount,
                        Description = parts[2],
                        Category    = parts[3],
                        OrderNumber = parts[1],
                    };
                }

                wrapper.Items.Add(subEntry);
                prevEntry = wrapper;
            }

            if (prevEntry != null)
            {
                ret.Transactions.Add(prevEntry);
            }

            return(ret);
        }
        public static List <string> Load(string directory)
        {
            List <string> warnings = new List <string>();

            string[] directories = Directory.GetDirectories(directory);

            Accounts         = new List <Account>();
            AllStatements    = new Dictionary <string, List <Statement> >();
            AmazonStatements = new List <AmazonStatement>();
            foreach (string subDirectory in directories)
            {
                string directoryName = Path.GetFileNameWithoutExtension(subDirectory);
                bool   isAmazon      = directoryName.Equals(Constants.AmazonLabel);

                if (!isAmazon)
                {
                    AllStatements[directoryName] = new List <Statement>();
                }

                foreach (string file in Directory.GetFiles(subDirectory))
                {
                    if (isAmazon)
                    {
                        AmazonStatement statement = ReadAmazonCSV(file);

                        if (statement != null)
                        {
                            AmazonStatements.Add(statement);

                            Console.WriteLine("   Read Amazon statement {0} with {1} transactions", Path.GetFileName(file), statement.Transactions.Count);
                        }
                    }
                    else
                    {
                        Statement statement = LoadStatementFromCache(file) ?? ReadStatementFile(file, directoryName);

                        if (statement == null)
                        {
                            continue;
                        }

                        AllStatements[directoryName].Add(statement);

                        double balance = statement.StartingBalance;
                        foreach (Transaction entry in statement.Transactions)
                        {
                            if (entry.TypeId <= 0)
                            {
                                TransactionType transactionType = TypeManager.Identify(entry.Description);
                                if (transactionType != null)
                                {
                                    entry.TypeId = transactionType.Id;
                                }
                            }

                            balance += entry.CheckingAmount + entry.SavingsAmount + entry.CreditAmount;
                        }

                        if (Math.Abs(balance - statement.EndingBalance) > 0.01)
                        {
                            string warning = $"Ending balance mismatch: {balance} vs. stated {statement.EndingBalance}";
                            warnings.Add(warning);
                            Console.WriteLine(warning);
                        }

                        SaveToCache(statement);

                        Console.WriteLine("   Read statement {0} with {1} transactions (${2} --> ${3}, error={4:0.00})", Path.GetFileName(file), statement.Transactions.Count, statement.StartingBalance, statement.EndingBalance, balance - statement.EndingBalance);
                    }
                }

                if (isAmazon)
                {
                    Console.WriteLine("Finished reading {0} directory", directoryName);
                }
                else
                {
                    Console.WriteLine("Finished reading {0} directory ({1} --> {2})", directoryName, AllStatements[directoryName].First().StartingBalance, AllStatements[directoryName].Last().EndingBalance);
                }
            }

            return(warnings);
        }