/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * This file is Skrooge plugin for for OFX import / export.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgimportpluginofx.h"

#include <QFileInfo>

#include <KLocale>
#include <KGenericFactory>

#include "skgtraces.h"
#include "skgservices.h"
#include "skgbankincludes.h"
#include "skgimportexportmanager.h"

SKGError SKGImportPluginOfx::m_ofxError;
QList<OfxStatementData> SKGImportPluginOfx::m_ofxInitialBalance;


/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginOfxFactory, registerPlugin<SKGImportPluginOfx>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGImportPluginOfxFactory("skrooge_import_ofx", "skrooge_import_ofx"))

SKGImportPluginOfx::SKGImportPluginOfx(QObject* iImporter, const QVariantList& iArg)
    : SKGImportPlugin(iImporter)
{
    SKGTRACEINFUNC(10);
    Q_UNUSED(iArg);
}

SKGImportPluginOfx::~SKGImportPluginOfx()
{
}

bool SKGImportPluginOfx::isImportPossible()
{
    SKGTRACEINFUNC(10);
    return (!m_importer ? true : m_importer->getFileNameExtension() == "OFX" || m_importer->getFileNameExtension() == "QFX");
}

SKGError SKGImportPluginOfx::importFile()
{
    if (!m_importer) {
        return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    }

    SKGError err;
    SKGTRACEINFUNCRC(2, err);

    if (!QFile(m_importer->getLocalFileName()).exists()) {
        err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().prettyUrl()));
    }
    IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "OFX")))
    IFOK(err) {
        SKGImportPluginOfx::m_ofxError = SKGError();
        SKGImportPluginOfx::m_ofxInitialBalance.clear();

        try {
            LibofxContextPtr ctx = libofx_get_new_context();

            ofx_set_account_cb(ctx, SKGImportPluginOfx::ofxAccountCallback, m_importer);
            ofx_set_statement_cb(ctx, SKGImportPluginOfx::ofxStatementCallback, m_importer);
            ofx_set_transaction_cb(ctx, SKGImportPluginOfx::ofxTransactionCallback, m_importer);
            // ofx_set_security_cb(ctx, ofxSecurityCallback, this);
            int rc = libofx_proc_file(ctx, m_importer->getLocalFileName().toUtf8(), AUTODETECT);
            if (rc) {
                err = SKGError(ERR_FAIL, i18nc("Error message",  "Import OFX file '%1' failed", m_importer->getFileName().prettyUrl()));
            }
            IFOKDO(err, SKGImportPluginOfx::m_ofxError) {
                // This is an option ==> no error management
                SKGError err2;
                int nb = SKGImportPluginOfx::m_ofxInitialBalance.count();
                for (int i = 0; !err2 && i < nb; ++i) {
                    OfxStatementData data = SKGImportPluginOfx::m_ofxInitialBalance.at(i);

                    // Get account
                    SKGAccountObject act;
                    if (!err2) {
                        err2 = getAccount(data.account_ptr, m_importer->getDocument(), act);
                    }

                    // Date of balance
                    QDate date = (data.ledger_balance_date_valid == true ? QDateTime::fromTime_t(data.ledger_balance_date).date() : QDate::currentDate());

                    // Get unit
                    SKGUnitObject unit;
                    if (!err2) {
                        err2 = m_importer->getDefaultUnit(unit);
                    }

                    // Current amount
                    double currentAmount = act.getAmount(date);

                    // Update account
                    if (!err2) {
                        err2 = act.setInitialBalance(data.ledger_balance - currentAmount, unit);
                    }
                    if (!err2) {
                        err2 = act.save();
                    }
                    if (!err2) {
                        err2 = m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set", act.getName()));
                    }
                }
            }
        } catch (...) {  // NOLINT(whitespace/parens)
            err = SKGError(ERR_HANDLE, i18nc("Error message", "OFX import failed"));
        }

        SKGImportPluginOfx::m_ofxInitialBalance.clear();

        SKGENDTRANSACTION(m_importer->getDocument(),  err);
    }
    return err;
}

SKGError SKGImportPluginOfx::getAccount(OfxAccountData* iAccountData, SKGDocumentBank* iDoc, SKGAccountObject& oAccount)
{
    SKGError err;
    QString accountNumber = iAccountData->account_id;
    QString bankNumber = iAccountData->bank_id;
    // Correction BUG 234771 vvvvv
    accountNumber = accountNumber.trimmed();
    bankNumber = bankNumber.trimmed();
    // Correction BUG 234771 ^^^^^
    if (accountNumber.startsWith(bankNumber % ' ')) {
        accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1);
        QStringList splitNumbers = accountNumber.split(' ');
        if (splitNumbers.count() == 2) {
            accountNumber = splitNumbers.at(1);
        }
    }

    // Check if account is already existing
    err = iDoc->getObject("v_account", "t_number='" % accountNumber % '\'', oAccount);

    return err;
}

int SKGImportPluginOfx::ofxStatementCallback(struct OfxStatementData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) {
        return 0;
    }
    SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) {
        return 0;
    }
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) {
        return 0;
    }

    // // Get data
    OfxAccountData* accountData = data.account_ptr;
    if (accountData && data.ledger_balance_valid == true) {
        // Get account
        SKGAccountObject act;
        SKGImportPluginOfx::m_ofxError = getAccount(accountData, doc, act);
        if (!SKGImportPluginOfx::m_ofxError) {
            if (act.getNbOperation() > 1) {
                SKGImportPluginOfx::m_ofxError = doc->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", act.getName()), SKGDocument::Warning);
            } else {
                m_ofxInitialBalance.push_back(data);
            }
        }
    }

    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

int SKGImportPluginOfx::ofxAccountCallback(struct OfxAccountData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) {
        return 0;
    }
    SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) {
        return 0;
    }
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) {
        return 0;
    }

    SKGObjectBase tmp;
    if (data.account_id_valid == true) {
        QString agencyNumber = QString::fromUtf8(data.branch_id);
        QString accountNumber = QString::fromUtf8(data.account_id);
        QString bankNumber = QString::fromUtf8(data.bank_id);
        // Correction BUG 234771 vvvvv
        accountNumber = accountNumber.trimmed();
        bankNumber = bankNumber.trimmed();
        // Correction BUG 234771 ^^^^^
        if (accountNumber.startsWith(bankNumber % ' ')) {
            accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1);
            QStringList splitNumbers = accountNumber.split(' ');
            if (splitNumbers.count() == 2) {
                agencyNumber = splitNumbers.at(0);
                accountNumber = splitNumbers.at(1);
            }
        }

        // Check if account is already existing
        SKGAccountObject account;
        SKGImportPluginOfx::m_ofxError = getAccount(&data, doc, account);
        if (!SKGImportPluginOfx::m_ofxError) {
            // Already existing
            account = tmp;
            SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account);
        } else {
            // Not existing
            QString bankId = (data.bank_id_valid == true ? QString::fromUtf8(data.bank_id) : (data.broker_id_valid == true ? QString::fromUtf8(data.broker_id) : i18nc("Adjective, an unknown item", "Unknown")));
            if (!bankId.isEmpty()) {
                // Check if bank is already existing
                SKGBankObject bank;
                SKGImportPluginOfx::m_ofxError = doc->getObject("v_bank", "t_bank_number='" % bankId % '\'', tmp);
                if (!SKGImportPluginOfx::m_ofxError) {
                    // Already existing
                    bank = tmp;
                } else {
                    // Create new bank
                    bank = SKGBankObject(doc);
                    SKGImportPluginOfx::m_ofxError = bank.setName(bankId);
                    if (!SKGImportPluginOfx::m_ofxError) {
                        SKGImportPluginOfx::m_ofxError = bank.setNumber(QString::fromUtf8(data.bank_id));
                    }
                    if (!SKGImportPluginOfx::m_ofxError) {
                        SKGImportPluginOfx::m_ofxError = bank.save();
                    }
                }

                // Create new account
                QString name = QString::fromUtf8(data.account_name);
                if (name.isEmpty()) {
                    name = QString::fromUtf8(data.account_id);
                } else {
                    name = name.remove("Bank account ");
                }

                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = bank.addAccount(account);
                }
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.setName(name);
                }
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.setNumber(accountNumber);
                }
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.setAgencyNumber(agencyNumber);
                }
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.setComment(QString::fromUtf8(data.account_name));
                }
                SKGAccountObject::AccountType type = SKGAccountObject::CURRENT;
                if (data.account_type_valid == true) {
                    switch (data.account_type) {
                    case OfxAccountData::OFX_CHECKING:
                    case OfxAccountData::OFX_SAVINGS:
                    case OfxAccountData::OFX_CREDITLINE:
                    case OfxAccountData::OFX_CMA:
                        type = SKGAccountObject::CURRENT;
                        break;
                    case OfxAccountData::OFX_MONEYMRKT:
                    case OfxAccountData::OFX_INVESTMENT:
                        type = SKGAccountObject::INVESTMENT;
                        break;
                    case OfxAccountData::OFX_CREDITCARD:
                        type = SKGAccountObject::CREDITCARD;
                        break;
                    default:
                        break;
                    }
                }

                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.setType(type);
                }
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = account.save();
                }

                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account);
                }

                SKGTRACEL(10) << "Account [" << name << "] created" << endl;
            }
        }
    }
    if (data.currency_valid == true) {
        // Check if unit is already existing
        SKGUnitObject unit(doc);
        SKGImportPluginOfx::m_ofxError = doc->getObject("v_unit", "t_name='" % QString::fromUtf8(data.currency) % "' OR t_name like '%(" % QString::fromUtf8(data.currency) % ")%'", tmp);
        if (!SKGImportPluginOfx::m_ofxError) {
            // Already existing
            unit = tmp;
            SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit);
        } else {
            // Create new account
            SKGImportPluginOfx::m_ofxError = unit.setName(QString::fromUtf8(data.currency));
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = unit.setSymbol(QString::fromUtf8(data.currency));
            }
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = unit.save();
            }

            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit);
            }
        }
    }
    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

int SKGImportPluginOfx::ofxTransactionCallback(struct OfxTransactionData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) {
        return 0;
    }
    SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) {
        return 0;
    }
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) {
        return 0;
    }

    // Get account
    SKGAccountObject account;
    SKGImportPluginOfx::m_ofxError = getAccount(data.account_ptr, doc, account);
    if (!SKGImportPluginOfx::m_ofxError) {
        // Get operation date
        QDate date = QDateTime::fromTime_t(data.date_posted_valid == true ? data.date_posted : data.date_initiated).date();

        // Get unit
        SKGUnitObject unit;
        SKGImportPluginOfx::m_ofxError = impotExporter->getDefaultUnit(unit);

        // Create id
        QString ID;
        if (data.fi_id_valid == true) {
            if (QString::fromUtf8(data.fi_id).count('0') != QString::fromUtf8(data.fi_id).count()) {
                ID = QString("ID-") % QString::fromUtf8(data.fi_id);
            }
        } else if (data.reference_number_valid == true) {
            if (QString::fromUtf8(data.reference_number).count('0') != QString::fromUtf8(data.reference_number).count()) {
                ID = QString("REF-") % data.reference_number;
            }
        }

        // Create operation
        SKGOperationObject ope;
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = account.addOperation(ope, true);
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.setDate(date);
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.setUnit(unit);
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.setAttribute("t_imported", "T");
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.setImportID(ID);
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            QString payeeName;
            if (data.payee_id_valid == true) {
                payeeName = QString::fromUtf8(data.payee_id);
            } else if (data.name_valid == true) {
                payeeName = QString::fromUtf8(data.name);
            }
            if (!payeeName.isEmpty()) {
                SKGPayeeObject payeeObj;
                SKGImportPluginOfx::m_ofxError = SKGPayeeObject::createPayee(doc, payeeName, payeeObj);
                if (!SKGImportPluginOfx::m_ofxError) {
                    SKGImportPluginOfx::m_ofxError = ope.setPayee(payeeObj);
                }
            }
        }
        if (!SKGImportPluginOfx::m_ofxError && data.memo_valid == true) {
            SKGImportPluginOfx::m_ofxError = ope.setComment(QString::fromUtf8(data.memo));
        }
        if (!SKGImportPluginOfx::m_ofxError && data.check_number_valid == true) {
            SKGImportPluginOfx::m_ofxError = ope.setNumber(SKGServices::stringToInt(data.check_number));
        }
        if (!SKGImportPluginOfx::m_ofxError && data.invtransactiontype_valid == true) {
            SKGImportPluginOfx::m_ofxError = ope.setMode(i18nc("Noun, the title of an item", "Title"));
        }
        if (!SKGImportPluginOfx::m_ofxError && data.transactiontype_valid == true) {
            QString mode;
            if (data.transactiontype == OFX_CREDIT) {
                mode = i18nc("Noun, type of OFX transaction", "Credit");
            } else if (data.transactiontype == OFX_DEBIT) {
                mode = i18nc("Noun, type of OFX transaction", "Debit");
            } else if (data.transactiontype == OFX_INT) {
                mode = i18nc("Noun, type of OFX transaction", "Interest");
            } else if (data.transactiontype == OFX_DIV) {
                mode = i18nc("Noun, type of OFX transaction", "Dividend");
            } else if (data.transactiontype == OFX_FEE) {
                mode = i18nc("Noun, type of OFX transaction", "FI fee");
            } else if (data.transactiontype == OFX_SRVCHG) {
                mode = i18nc("Noun, type of OFX transaction", "Service charge");
            } else if (data.transactiontype == OFX_DEP) {
                mode = i18nc("Noun, type of OFX transaction", "Deposit");
            } else if (data.transactiontype == OFX_ATM) {
                mode = i18nc("Noun, type of OFX transaction", "ATM");
            } else if (data.transactiontype == OFX_POS) {
                mode = i18nc("Noun, type of OFX transaction", "Point of sale");
            } else if (data.transactiontype == OFX_XFER) {
                mode = i18nc("An operation mode", "Transfer");
            } else if (data.transactiontype == OFX_CHECK) {
                mode = i18nc("An operation mode", "Check");
            } else if (data.transactiontype == OFX_PAYMENT) {
                mode = i18nc("Noun, type of OFX transaction", "Electronic payment");
            } else if (data.transactiontype == OFX_CASH) {
                mode = i18nc("An operation mode", "Withdrawal");
            } else if (data.transactiontype == OFX_DIRECTDEP) {
                mode = i18nc("Noun, type of OFX transaction", "Deposit");
            } else if (data.transactiontype == OFX_REPEATPMT) {
                mode = i18nc("Noun, type of OFX transaction", "Repeating payment");
            } else if (data.transactiontype == OFX_DIRECTDEBIT) {
                mode = i18nc("An operation mode", "Direct debit");
            }

            SKGImportPluginOfx::m_ofxError = ope.setMode(mode);
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.save();
        }

        // Create sub operation
        SKGSubOperationObject subop;
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop);
        }
        if (!SKGImportPluginOfx::m_ofxError && data.amount_valid == true) {
            SKGImportPluginOfx::m_ofxError = subop.setQuantity(data.amount + (data.commission_valid == true ? data.commission : 0));
        }
        if (!SKGImportPluginOfx::m_ofxError && data.memo_valid == true) {
            SKGImportPluginOfx::m_ofxError = subop.setComment(QString::fromUtf8(data.memo));
        }
        if (!SKGImportPluginOfx::m_ofxError) {
            SKGImportPluginOfx::m_ofxError = subop.save();
        }

        // Commission
        if (!SKGImportPluginOfx::m_ofxError && data.commission_valid == true && data.amount_valid == true && data.commission > 0) {
            // Create splitter operation
            SKGSubOperationObject subop2;
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop2);
            }
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = subop2.setComment(i18nc("Noun, a quantity of money taken by a financial institution to perform an operation", "Commission"));
            }
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = subop2.setQuantity(-data.commission);
            }
            if (!SKGImportPluginOfx::m_ofxError) {
                SKGImportPluginOfx::m_ofxError = subop2.save();
            }
        }
    }
    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

QString SKGImportPluginOfx::getMimeTypeFilter() const
{
    return "*.ofx *.qfx|" % i18nc("A file format", "OFX file");
}

#include "skgimportpluginofx.moc"
