/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * 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.
 */

#include <gtest/gtest.h>

#include <string>

#include "rdb_test_common.h"
#include "rdb_errno.h"
#include "rdb_helper.h"
#include "rdb_open_callback.h"

using namespace testing::ext;
using namespace OHOS::NativeRdb;

class RdbUpgradeTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
    void InsertValues(std::shared_ptr<RdbStore> &store);

    static const std::string DATABASE_NAME;
    static std::shared_ptr<RdbStore> store;
};

const std::string RdbUpgradeTest::DATABASE_NAME = RDB_TEST_PATH + "upgrade_test.db";
std::shared_ptr<RdbStore> RdbUpgradeTest::store = nullptr;

class UpgradeTestOpenCallback : public RdbOpenCallback {
public:
    int OnCreate(RdbStore &store) override;
    int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
    static const std::string CREATE_TABLE_TEST;
};

std::string const UpgradeTestOpenCallback::CREATE_TABLE_TEST =
    std::string("CREATE TABLE IF NOT EXISTS test ") + std::string("(id INTEGER PRIMARY KEY AUTOINCREMENT, "
                                                                  "name TEXT NOT NULL, age INTEGER, salary "
                                                                  "REAL, blobType BLOB)");

int UpgradeTestOpenCallback::OnCreate(RdbStore &store)
{
    return store.ExecuteSql(CREATE_TABLE_TEST);
}

int UpgradeTestOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
{
    return E_OK;
}

class UpgradeOpenCallback : public RdbOpenCallback {
public:
    int OnCreate(RdbStore &store) override;
    int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
    static const std::string UPGRADE_TABLE_TEST;
};

std::string const UpgradeOpenCallback::UPGRADE_TABLE_TEST = std::string("ALTER TABLE test ADD COLUMN address TEXT;");

int UpgradeOpenCallback::OnCreate(RdbStore &store)
{
    return E_OK;
}

int UpgradeOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
{
    return store.ExecuteSql(UPGRADE_TABLE_TEST);
}

void RdbUpgradeTest::SetUpTestCase(void)
{
    int errCode = E_OK;
    RdbHelper::DeleteRdbStore(RdbUpgradeTest::DATABASE_NAME);
    RdbStoreConfig config(RdbUpgradeTest::DATABASE_NAME);
    UpgradeTestOpenCallback helper;
    RdbUpgradeTest::store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
    EXPECT_NE(RdbUpgradeTest::store, nullptr);
}

void RdbUpgradeTest::TearDownTestCase(void)
{
    store = nullptr;
    RdbHelper::DeleteRdbStore(RdbUpgradeTest::DATABASE_NAME);
}

void RdbUpgradeTest::SetUp(void)
{
    store->ExecuteSql("DELETE FROM test");
}

void RdbUpgradeTest::TearDown(void)
{
}

/**
 * @tc.name: RdbStore_Upgrade_001
 * @tc.desc: test RdbStore upgrade
 * @tc.type: FUNC
 */
HWTEST_F(RdbUpgradeTest, RdbStore_Upgrade_001, TestSize.Level1)
{
    RdbUpgradeTest::InsertValues(RdbUpgradeTest::store);

    std::shared_ptr<ResultSet> resultSet = RdbUpgradeTest::store->QuerySql("SELECT * FROM test");
    EXPECT_NE(resultSet, nullptr);
    int ret = resultSet->GoToNextRow();
    EXPECT_EQ(ret, E_OK);
    ret = resultSet->GoToNextRow();
    EXPECT_EQ(ret, E_OK);
    ret = resultSet->GoToNextRow();
    EXPECT_EQ(ret, E_OK);
    ret = resultSet->GoToNextRow();
    EXPECT_EQ(ret, E_ROW_OUT_RANGE);
    ret = resultSet->Close();
    EXPECT_EQ(ret, E_OK);
    RdbHelper::ClearCache();

    int errCode = E_OK;
    RdbStoreConfig config(RdbUpgradeTest::DATABASE_NAME);
    UpgradeOpenCallback callBack;
    std::shared_ptr<RdbStore> upgradeStore = RdbHelper::GetRdbStore(config, 2, callBack, errCode);
    EXPECT_NE(upgradeStore, nullptr);
    std::shared_ptr<ResultSet> upgradeResultSet = upgradeStore->QuerySql("SELECT * FROM test");
    EXPECT_NE(upgradeResultSet, nullptr);
    int columnCount;
    ret = upgradeResultSet->GetColumnCount(columnCount);
    EXPECT_EQ(ret, E_OK);
    EXPECT_EQ(columnCount, 6);
    ret = upgradeResultSet->GoToNextRow();
    int columnIndex;
    ret = upgradeResultSet->GetColumnIndex("id", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GetColumnIndex("name", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GetColumnIndex("age", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GetColumnIndex("salary", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GetColumnIndex("blobType", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GetColumnIndex("address", columnIndex);
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GoToNextRow();
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GoToNextRow();
    EXPECT_EQ(ret, E_OK);
    ret = upgradeResultSet->GoToNextRow();
    EXPECT_EQ(ret, E_ROW_OUT_RANGE);
    ret = upgradeResultSet->Close();
    EXPECT_EQ(ret, E_OK);
}

void RdbUpgradeTest::InsertValues(std::shared_ptr<RdbStore> &store)
{
    int64_t id;
    ValuesBucket values;

    values.PutInt("id", 1);
    values.PutString("name", std::string("zhangsan"));
    values.PutInt("age", 18);
    values.PutDouble("salary", 100.5);
    values.PutBlob("blobType", std::vector<uint8_t>{ 1, 2, 3 });
    int ret = store->Insert(id, "test", values);
    EXPECT_EQ(ret, E_OK);
    EXPECT_EQ(1, id);

    values.Clear();
    values.PutInt("id", 2);
    values.PutString("name", std::string("lisi"));
    values.PutInt("age", 19);
    values.PutDouble("salary", 200.5);
    values.PutBlob("blobType", std::vector<uint8_t>{ 4, 5, 6 });
    ret = store->Insert(id, "test", values);
    EXPECT_EQ(ret, E_OK);
    EXPECT_EQ(2, id);

    values.Clear();
    values.PutInt("id", 3);
    values.PutString("name", std::string("wangyjing"));
    values.PutInt("age", 20);
    values.PutDouble("salary", 300.5);
    values.PutBlob("blobType", std::vector<uint8_t>{ 7, 8, 9 });
    ret = store->Insert(id, "test", values);
    EXPECT_EQ(ret, E_OK);
    EXPECT_EQ(3, id);
}