/*
 * Copyright (c) 2015 Ngewi Fet <ngewif@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gnucash.android.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;

import com.crashlytics.android.Crashlytics;

import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.DatabaseSchema.BookEntry;
import org.gnucash.android.db.adapter.AccountsDbAdapter;
import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.db.adapter.SplitsDbAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Book;
import org.gnucash.android.util.RecursiveMoveFiles;

import java.io.File;
import java.io.IOException;

/**
 * Database helper for managing database which stores information about the books in the application
 * This is a different database from the one which contains the accounts and transaction data because
 * there are multiple accounts/transactions databases in the system and this one will be used to
 * switch between them.
 */
public class BookDbHelper extends SQLiteOpenHelper {

    public static final String LOG_TAG = "BookDbHelper";

    private Context mContext;

    /**
     * Create the books table
     */
    private static final String BOOKS_TABLE_CREATE = "CREATE TABLE " + BookEntry.TABLE_NAME + " ("
            + BookEntry._ID 		         + " integer primary key autoincrement, "
            + BookEntry.COLUMN_UID 		     + " varchar(255) not null UNIQUE, "
            + BookEntry.COLUMN_DISPLAY_NAME  + " varchar(255) not null, "
            + BookEntry.COLUMN_ROOT_GUID     + " varchar(255) not null, "
            + BookEntry.COLUMN_TEMPLATE_GUID + " varchar(255), "
            + BookEntry.COLUMN_ACTIVE        + " tinyint default 0, "
            + BookEntry.COLUMN_SOURCE_URI    + " varchar(255), "
            + BookEntry.COLUMN_LAST_SYNC     + " TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            + BookEntry.COLUMN_CREATED_AT    + " TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
            + BookEntry.COLUMN_MODIFIED_AT   + " TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP "
            + ");" + DatabaseHelper.createUpdatedAtTrigger(BookEntry.TABLE_NAME);

    public BookDbHelper(Context context) {
        super(context, DatabaseSchema.BOOK_DATABASE_NAME, null, DatabaseSchema.BOOK_DATABASE_VERSION);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(BOOKS_TABLE_CREATE);

        if (mContext.getDatabasePath(DatabaseSchema.LEGACY_DATABASE_NAME).exists()){
            Log.d(LOG_TAG, "Legacy database found. Migrating to multibook format");
            DatabaseHelper helper = new DatabaseHelper(GnuCashApplication.getAppContext(),
                    DatabaseSchema.LEGACY_DATABASE_NAME);
            SQLiteDatabase mainDb = helper.getWritableDatabase();
            AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mainDb,
                    new TransactionsDbAdapter(mainDb, new SplitsDbAdapter(mainDb)));

            String rootAccountUID = accountsDbAdapter.getOrCreateGnuCashRootAccountUID();

            Book book = new Book(rootAccountUID);
            book.setActive(true);
            insertBook(db, book);

            String mainDbPath = mainDb.getPath();
            helper.close();

            File src = new File(mainDbPath);
            File dst = new File(src.getParent(), book.getUID());
            try {
                MigrationHelper.moveFile(src, dst);
            } catch (IOException e) {
                String err_msg = "Error renaming database file";
                Crashlytics.log(err_msg);
                Log.e(LOG_TAG, err_msg, e);
            }

            migrateBackupFiles(book.getUID());
        }

        String sql = "SELECT COUNT(*) FROM " + BookEntry.TABLE_NAME;
        SQLiteStatement statement = db.compileStatement(sql);
        long count = statement.simpleQueryForLong();
        if (count == 0) { //no book in the database, create a default one
            Log.i(LOG_TAG, "No books found in database, creating default book");
            Book book = new Book();
            DatabaseHelper helper = new DatabaseHelper(GnuCashApplication.getAppContext(), book.getUID());
            SQLiteDatabase mainDb = helper.getWritableDatabase(); //actually create the db
            AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mainDb,
                    new TransactionsDbAdapter(mainDb, new SplitsDbAdapter(mainDb)));

            String rootAccountUID = accountsDbAdapter.getOrCreateGnuCashRootAccountUID();
            book.setRootAccountUID(rootAccountUID);
            book.setActive(true);
            insertBook(db, book);
        }

    }

    /**
     * Returns the database for the book
     * @param bookUID GUID of the book
     * @return SQLiteDatabase of the book
     */
    public static SQLiteDatabase getDatabase(String bookUID){
        DatabaseHelper dbHelper = new DatabaseHelper(GnuCashApplication.getAppContext(), bookUID);
        return dbHelper.getWritableDatabase();
    }

    /**
     * Inserts the book into the database
     * @param db Book database
     * @param book Book to insert
     */
    private void insertBook(SQLiteDatabase db, Book book) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(BookEntry.COLUMN_UID, book.getUID());
        contentValues.put(BookEntry.COLUMN_ROOT_GUID, book.getRootAccountUID());
        contentValues.put(BookEntry.COLUMN_TEMPLATE_GUID, Book.generateUID());
        contentValues.put(BookEntry.COLUMN_DISPLAY_NAME, new BooksDbAdapter(db).generateDefaultBookName());
        contentValues.put(BookEntry.COLUMN_ACTIVE, book.isActive() ? 1 : 0);

        db.insert(BookEntry.TABLE_NAME, null, contentValues);
    }

    /**
     * Move the backup and export files from the old location (single-book) to the new multi-book
     * backup folder structure. Each book has its own directory as well as backups and exports.
     * <p>This method should be called only once during the initial migration to multi-book support</p>
     * @param activeBookUID GUID of the book for which to migrate the files
     */
    private void migrateBackupFiles(String activeBookUID){

        Log.d(LOG_TAG, "Moving export and backup files to book-specific folders");
        File newBasePath = new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/" + activeBookUID);
        newBasePath.mkdirs();

        File src = new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/backups/");
        File dst = new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/" + activeBookUID + "/backups/");
        new Thread(new RecursiveMoveFiles(src, dst)).start();

        src = new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/exports/");
        dst = new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/" + activeBookUID + "/exports/");
        new Thread(new RecursiveMoveFiles(src, dst)).start();

        File nameFile = new File(newBasePath, "Book 1");
        try {
            nameFile.createNewFile();
        } catch (IOException e) {
            Log.e(LOG_TAG, "Error creating name file for the database: " + nameFile.getName());
            e.printStackTrace();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //nothing to see here yet, move along
    }
}
