/*
 * Copyright (c) 2023-2025 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.
 */

//! This module implements the Asset service.

use ipc::parcel::MsgParcel;
use samgr::manage::SystemAbilityManager;
use std::{
    fs,
    ffi::CStr,
    time::{Duration, Instant},
    sync::{Mutex, MutexGuard, RwLock},
};
use system_ability_fwk::{
    ability::{Ability, Handler},
    cxx_share::SystemAbilityOnDemandReason,
};
use ylong_runtime::builder::RuntimeBuilder;
use ylong_json::JsonValue;
use lazy_static::lazy_static;

use asset_common::{AutoCounter, CallingInfo, ConstAssetBlob, ConstAssetBlobArray, Counter, TaskManager,
    OwnerType, Group, MutAssetBlob, MutAssetBlobArrayChangeable, ProcessInfo
};
use asset_crypto_manager::{crypto_manager::CryptoManager, db_key_operator::get_db_key};
use asset_db_operator::{database_file_upgrade::check_and_split_db, database::{preload_db, clear_db_map}};
use asset_definition::{macros_lib, AssetMap, ErrCode, Result, SyncResult};
use asset_file_operator::{common::DE_ROOT_PATH, de_operator::create_user_de_dir};
use asset_ipc::{SA_ID, deserialize};
use asset_log::{loge, logi};
use asset_plugin::asset_plugin::{AssetContext, AssetPlugin};

mod common_event;
mod data_size_mod;
mod operations;
mod stub;
mod sys_event;
mod trace_scope;
mod upgrade_operator;
mod upgrade_ce;

use sys_event::{upload_system_event, upload_batch_system_event};
use trace_scope::TraceScope;

use crate::data_size_mod::handle_data_size_upload;

struct AssetAbility;

lazy_static! {
    static ref UPGRADE_CE_MUTEX: RwLock<()> = RwLock::new(());
}

trait WantParser<T> {
    fn parse(&self) -> Result<T>;
}

struct PackageInfo {
    user_id: i32,
    app_index: i32,
    app_id: String,
    developer_id: Option<String>,
    group_ids: Option<Vec<String>>,
    bundle_name: String,
}

#[repr(C)]
struct PackageInfoFfi {
    user_id: i32,
    app_index: i32,
    owner: ConstAssetBlob,
    developer_id: ConstAssetBlob,
    group_ids: ConstAssetBlobArray,
    bundle_name: ConstAssetBlob,
}

static DELAYED_UNLOAD_TIME_IN_SEC: i32 = 20;
static DELAYED_UNLOAD_TIME_FOR_COMMON_EVENT_IN_SEC: i32 = 5;
static SEC_TO_MILLISEC: i32 = 1000;
const RSS_SA_EXTENSION: &str = "RssSaExtension";
const PREPARE_FOR_BUNDLE: u32 = 27;
const START_STATUS: i32 = 1;
const DEAD_STATUS: i32 = 0;
const MEMORY_MANAGER_SA_ID: i32 = 1909;

#[derive(PartialEq)]
enum SaStatus {
    Active,
    Idle
}

#[derive(PartialEq)]
enum CriticalStatus {
    NotInit = -1,
    SetTrue = 1,
    SetFalse = 0,
}

struct MemoryMgrInfo {
    sa_status: SaStatus,
    notify_status: bool,
    critical_status: CriticalStatus,
}

impl MemoryMgrInfo {
    pub(crate) fn build() -> Self {
        Self { sa_status: SaStatus::Active, notify_status: false, critical_status: CriticalStatus::NotInit }
    }

    pub(crate) fn set_notify_status(&mut self, status: bool) {
        self.notify_status = status
    }

    pub(crate) fn set_sa_status(&mut self, sa_status: SaStatus) {
        self.sa_status = sa_status
    }

    pub(crate) fn set_critical_status(&mut self, critical_status: CriticalStatus) {
        self.critical_status = critical_status
    }

    pub(crate) fn get_notify_status(&self) -> bool {
        self.notify_status
    }

    pub(crate) fn get_sa_status(&self) -> &SaStatus {
        &self.sa_status
    }

    pub(crate) fn get_critical_status(&self) -> &CriticalStatus {
        &self.critical_status
    }
}

lazy_static::lazy_static! {
    static ref HAS_NOTIFY_MEMORY_MGR_MUTEX: Mutex<MemoryMgrInfo> = Mutex::new(MemoryMgrInfo::build());
}

extern "C" {
    fn CheckMemoryMgr() -> bool;
    fn NotifyStatus(size: i32) -> i32;
    fn SetCritical(critical: bool) -> i32;
}

fn set_notify_info(lock: &mut MutexGuard<MemoryMgrInfo>, notify_info: i32) {
    if lock.get_notify_status() { return; }
    unsafe { NotifyStatus(notify_info); }
    lock.set_notify_status(true);
}

fn set_critical_info(lock: &mut MutexGuard<MemoryMgrInfo>, critical: bool) {
    match lock.get_critical_status() {
        CriticalStatus::NotInit => {
            if !critical {
                loge!("[FATAL]func use error");
                return;
            }
            unsafe { SetCritical(critical); }
            lock.set_critical_status(CriticalStatus::SetTrue);
        },
        CriticalStatus::SetTrue => {
            if critical {
                return;
            }
            unsafe { SetCritical(critical); }
            lock.set_critical_status(CriticalStatus::SetFalse);
        },
        CriticalStatus::SetFalse => {
            if !critical {
                return;
            }
            unsafe { SetCritical(critical); }
            lock.set_critical_status(CriticalStatus::SetTrue);
        },
    }
}

impl PackageInfo {
    fn developer_id(&self) -> &Option<String> {
        &self.developer_id
    }

    fn group_ids(&self) -> &Option<Vec<String>> {
        &self.group_ids
    }
}

pub(crate) fn unload_sa() {
    unload_sa_with_delay(DELAYED_UNLOAD_TIME_IN_SEC);
}

pub(crate) fn unload_sa_with_delay(delay_sec: i32) {
    ylong_runtime::spawn(async move {
        loop {
            ylong_runtime::time::sleep(Duration::from_secs(delay_sec as u64)).await;
            let crypto_manager = CryptoManager::get_instance();
            let max_crypto_expire_duration = crypto_manager.lock().unwrap().max_crypto_expire_duration();
            if max_crypto_expire_duration > 0 {
                continue;
            }
            let counter = Counter::get_instance();
            if counter.lock().unwrap().count() > 0 {
                continue;
            }
            let task_manager = TaskManager::get_instance();
            if !task_manager.lock().unwrap().is_empty() {
                continue;
            }

            SystemAbilityManager::unload_system_ability(SA_ID);
            break;
        }
    });
}

impl Ability for AssetAbility {
    fn on_start_with_reason(&self, reason: SystemAbilityOnDemandReason, handler: Handler) {
        logi!("Start asset, reason_id: {:?}", reason.reason_id);
        if let Err(e) = RuntimeBuilder::new_multi_thread().worker_num(1).max_blocking_pool_size(1).build_global() {
            loge!("[WARNING]Ylong new global thread failed! {}", e);
        };
        let func_name = macros_lib::hisysevent::function!();
        let start = Instant::now();
        let _trace = TraceScope::trace(func_name);
        let calling_info = CallingInfo::new_self();
        let _ = upload_system_event(start_service(handler), &calling_info, start, func_name, &AssetMap::new());
        if let Err(e) = handle_data_size_upload() {
            loge!("Failed to handle data upload: {}", e);
        }
        common_event::handle_common_event(reason);
    }

    fn on_system_ability_load_event(&self, said: i32, device_id: String) {
        logi!("Receive service load event, said:{}, device_id:{}", said, &device_id);
        if said != MEMORY_MANAGER_SA_ID { return; }
        let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
        set_notify_info(&mut lock, START_STATUS);
        if (*lock).get_sa_status() == &SaStatus::Active {
            set_critical_info(&mut lock, true);
        }
    }

    fn on_system_ability_remove_event(&self, said: i32, device_id: String) {
        loge!("[ERROR]Receive service unload event, said:{}, device_id:{}", said, &device_id);
        // if memory manager died, we need to reset status.
        if said == MEMORY_MANAGER_SA_ID {
            let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
            (*lock).set_notify_status(false);
            (*lock).set_critical_status(CriticalStatus::NotInit);
        }
    }

    fn on_active(&self, reason: SystemAbilityOnDemandReason) {
        let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
        (*lock).set_sa_status(SaStatus::Active);
        logi!("Asset on_active.");
        if let Err(e) = handle_data_size_upload() {
            loge!("Failed to handle data upload: {}", e);
        }
        common_event::handle_common_event(reason);
        unsafe { 
            if !CheckMemoryMgr() { return; }
            set_notify_info(&mut lock, START_STATUS);
            set_critical_info(&mut lock, true);
        }
    }

    fn on_idle(&self, _reason: SystemAbilityOnDemandReason) -> i32 {
        clear_db_map();
        let crypto_manager = CryptoManager::get_instance();
        let max_crypto_expire_duration = crypto_manager.lock().unwrap().max_crypto_expire_duration();
        if max_crypto_expire_duration > 0 {
            logi!("Asset on idle not success, delay time: {}s", max_crypto_expire_duration);
            return max_crypto_expire_duration as i32 * SEC_TO_MILLISEC;
        }

        let counter = Counter::get_instance();
        if counter.lock().unwrap().count() > 0 {
            logi!(
                "Asset on idle not success for use_account: {}, delay time: {}s",
                counter.lock().unwrap().count(),
                DELAYED_UNLOAD_TIME_IN_SEC
            );
            return DELAYED_UNLOAD_TIME_IN_SEC * SEC_TO_MILLISEC;
        }
        let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
        (*lock).set_sa_status(SaStatus::Idle);
        logi!("Asset on_idle.");
        unsafe {
            if !CheckMemoryMgr() { return 0; }
            set_notify_info(&mut lock, START_STATUS);
            set_critical_info(&mut lock, true);
            set_critical_info(&mut lock, false);
        }

        0
    }

    fn on_stop(&self) {
        logi!("Asset on_stop");
        let counter = Counter::get_instance();
        counter.lock().unwrap().stop();
        common_event::unsubscribe();
        let lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
        if !(*lock).get_notify_status() { return; }
        unsafe { NotifyStatus(DEAD_STATUS); }
    }

    fn on_extension(&self, extension: String, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 {
        logi!("Asset on_extension, extension is {}", extension);
        if extension == RSS_SA_EXTENSION {
            match on_preload_extension(data) {
                Ok(()) => logi!("process preload extension event success."),
                Err(_) => loge!("process preload extension event failed."),
            }
        } else if let Ok(load) = AssetPlugin::get_instance().load_plugin() {
            match load.on_sa_extension(extension, data, reply) {
                Ok(()) => logi!("process sa extension event success."),
                Err(code) => loge!("process sa extension event failed, code: {}", code),
            };
        }
        logi!("Asset on_extension end");
        0
    }
}

fn get_db_key_and_preload_db(calling_info: &CallingInfo, is_ce: bool) -> Result<()> {
    let db_key = get_db_key(calling_info.user_id(), is_ce)?;
    preload_db(calling_info, db_key)
}

fn get_value_from_json(json_str: String, key: &str) -> String {
    let json = match JsonValue::from_text(json_str) {
        Ok(json) => json,
        Err(_) => {
            loge!("parse json from String failed.");
            JsonValue::from_text("{}").unwrap()
        }
    };
    if json.try_as_object().unwrap().is_empty() {
        loge!("");
        return "".to_string();
    }
    let value: &JsonValue = &json[key];
    if value == &JsonValue::Null {
        return "".to_string();
    }
    value.to_compact_string().unwrap().replace('\"', "")
}

extern "C" {
    fn GetCallingHapGroups(uid: u64, group_ids: *mut MutAssetBlobArrayChangeable, developer_id: *mut MutAssetBlob) -> i32;
}

fn construct_group_calling_infos(user_id: i32, owner: Vec<u8>,uid: u64) -> Vec<CallingInfo> {
    let mut group_id_blobs: Vec<Vec<u8>> = Vec::new();
    let mut group_id_blobs_ptr_record = Vec::new();
    for _i in 0..512 {
        let mut data_blob = vec![0u8; 128];
        let data_blob_ptr = MutAssetBlob { size: data_blob.len() as u32, data: data_blob.as_mut_ptr() };
        group_id_blobs_ptr_record.push(data_blob_ptr);
        group_id_blobs.push(data_blob);
    }
    let mut developer_id = vec![0; 128];
    let mut the_group_ids = MutAssetBlobArrayChangeable { 
        size: group_id_blobs_ptr_record.len() as u32, blobs: group_id_blobs_ptr_record.as_mut_ptr() 
    };
    let mut the_developer_id = MutAssetBlob { size: developer_id.len() as u32, data: developer_id.as_mut_ptr() };
    let use_group_ids = match unsafe { GetCallingHapGroups(uid, &mut the_group_ids, &mut the_developer_id) } {
        0 => {
            let mut group_id_list = Vec::with_capacity(the_group_ids.size as usize);
            developer_id.truncate(the_developer_id.size as usize);
            group_id_blobs.truncate(the_group_ids.size as usize);
            for (idx, group_id) in group_id_blobs.iter().enumerate() {
                let mut use_group_id = group_id.clone();
                unsafe { use_group_id.truncate((*the_group_ids.blobs.add(idx as usize)).size as usize); }
                group_id_list.push(use_group_id);
            }
            group_id_list
        },
        error => {
            loge!("[FATAL]Get GetUninstallGroups failed, res is {}.", error);
            return vec![];
        },
    };
    let mut calling_infos = Vec::with_capacity(use_group_ids.len());
    for group_id in use_group_ids {
        calling_infos.push(CallingInfo::new(
            user_id,
            OwnerType::HapGroup,
            owner.clone(),
            Some(Group { developer_id: developer_id.clone(), group_id: group_id.clone() }),
        ));
    }
    calling_infos
}

fn on_preload_extension(data: &mut MsgParcel) -> Result<()> {
    let res_type = deserialize::<u32>(data)?;
    if res_type != PREPARE_FOR_BUNDLE {
        return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
            ErrCode::InvalidArgument, "res_type is not PREPARE_FOR_BUNDLE");
    }
    let _value = deserialize::<i64>(data)?;

    let json_str = deserialize::<String>(data)?;
    let value = get_value_from_json(json_str, "uid");
    let uid = match value.parse::<u64>() {
        Ok(num) => num,
        Err(_) => return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
            ErrCode::InvalidArgument, "parse uid from json value failed!"),
    };

    let process_info = ProcessInfo::build(None, Some(uid), true)?;
    let calling_info = CallingInfo::build(None, &process_info);
    let _ = get_db_key_and_preload_db(&calling_info, false);
    let _ = get_db_key_and_preload_db(&calling_info, true);
    let group_calling_infos = construct_group_calling_infos(
        calling_info.user_id(), calling_info.owner_info().clone(), uid
    );
    for group_calling_info in &group_calling_infos {
        let _ = get_db_key_and_preload_db(group_calling_info, false);
        let _ = get_db_key_and_preload_db(group_calling_info, true);
    }

    Ok(())
}

async fn execute_upgrade_process() {
    match upgrade_process() {
        Ok(()) => (),
        Err(e) => loge!("upgrade failed, err:[{}]", e.code),
    }
}

fn upgrade_process() -> Result<()> {
    let _counter_user = AutoCounter::new();
    for entry in fs::read_dir(DE_ROOT_PATH)? {
        let entry = entry?;
        if let Ok(user_id) = entry.file_name().to_string_lossy().parse::<i32>() {
            check_and_split_db(user_id)?;
        }
    }
    Ok(())
}

fn start_service(handler: Handler) -> Result<()> {
    let asset_plugin = AssetPlugin::get_instance();
    match asset_plugin.load_plugin() {
        Ok(loader) => {
            let _tr = loader.init(Box::new(AssetContext { user_id: 0 }));
        },
        Err(_) => loge!("load plugin failed."),
    }

    if !handler.publish(AssetService::new(handler.clone())) {
        return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
            ErrCode::IpcError, "Asset publish stub object failed");
    };
    common_event::subscribe();
    handler.add_system_ability_listen(MEMORY_MANAGER_SA_ID);
    let handle = ylong_runtime::spawn(execute_upgrade_process());
    let task_manager = TaskManager::get_instance();
    task_manager.lock().unwrap().push_task(handle);
    Ok(())
}

#[used]
#[link_section = ".init_array"]
static A: extern "C" fn() = {
    extern "C" fn init() {
        let Some(sa) = AssetAbility.build_system_ability(SA_ID, true) else {
            loge!("Create Asset service failed.");
            return;
        };

        sa.register();
    }
    init
};

extern "C" {
    fn GetCeUpgradeInfo() -> *const u8;
}

pub(crate) fn get_ce_upgrade_info() -> &'static [u8] {
    let info = unsafe { GetCeUpgradeInfo() };
    if !info.is_null() {
        let c_str = unsafe { CStr::from_ptr(info as _) };
        if let Ok(result) = c_str.to_str() {
            return result.as_bytes()
        }
    }
    &[]
}

struct AssetService {
    system_ability: system_ability_fwk::ability::Handler,
}

macro_rules! execute {
    ($func:path, $calling_info:expr, $first_arg:expr) => {{
        let func_name = macros_lib::hisysevent::function!();
        let start = Instant::now();
        let _trace = TraceScope::trace(func_name);
        // Create de database directory if not exists.
        create_user_de_dir($calling_info.user_id())?;
        let ce_upgrade_info = get_ce_upgrade_info();
        if ce_upgrade_info == $calling_info.owner_info() {
            let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
            upload_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
        } else {
            upload_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
        }
        
    }};
    ($func:path, $calling_info:expr, $first_arg:expr, $second_arg:expr) => {{
        let func_name = macros_lib::hisysevent::function!();
        let start = Instant::now();
        let _trace = TraceScope::trace(func_name);
        // Create de database directory if not exists.
        create_user_de_dir($calling_info.user_id())?;
        let ce_upgrade_info = get_ce_upgrade_info();
        if ce_upgrade_info == $calling_info.owner_info() {
            let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
            upload_system_event(
                $func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
        } else {
            upload_system_event(
                $func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
        }
    }};
}

macro_rules! execute_batch {
    ($func:path, $calling_info:expr, $first_arg:expr) => {{
        let func_name = macros_lib::hisysevent::function!();
        let start = Instant::now();
        let _trace = TraceScope::trace(func_name);
        // Create de database directory if not exists.
        create_user_de_dir($calling_info.user_id())?;
        let ce_upgrade_info = get_ce_upgrade_info();
        if ce_upgrade_info == $calling_info.owner_info() {
            let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
            upload_batch_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
        } else {
            upload_batch_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
        }
        
    }};
    ($func:path, $calling_info:expr, $first_arg:expr, $second_arg:expr) => {{
        let func_name = macros_lib::hisysevent::function!();
        let start = Instant::now();
        let _trace = TraceScope::trace(func_name);
        // Create de database directory if not exists.
        create_user_de_dir($calling_info.user_id())?;
        let ce_upgrade_info = get_ce_upgrade_info();
        if ce_upgrade_info == $calling_info.owner_info() {
            let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
            upload_batch_system_event(
                $func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
        } else {
            upload_batch_system_event(
                $func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
        }
    }};
}

impl AssetService {
    pub(crate) fn new(handler: system_ability_fwk::ability::Handler) -> Self {
        Self { system_ability: handler }
    }

    fn add(&self, calling_info: &CallingInfo, attributes: &AssetMap) -> Result<()> {
        execute!(operations::add, calling_info, attributes)
    }

    fn remove(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<()> {
        execute!(operations::remove, calling_info, query)
    }

    fn update(&self, calling_info: &CallingInfo, query: &AssetMap, attributes_to_update: &AssetMap) -> Result<()> {
        execute!(operations::update, calling_info, query, attributes_to_update)
    }

    fn pre_query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<Vec<u8>> {
        execute!(operations::pre_query, calling_info, query)
    }

    fn query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<Vec<AssetMap>> {
        execute!(operations::query, calling_info, query)
    }

    fn post_query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<()> {
        execute!(operations::post_query, calling_info, query)
    }

    fn query_sync_result(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<SyncResult> {
        execute!(operations::query_sync_result, calling_info, query)
    }

    fn batch_add(&self, calling_info: &CallingInfo, attributes_array: &[AssetMap]) -> Result<Vec<(u32, u32)>> {
        execute_batch!(operations::batch_add, calling_info, attributes_array)
    }

    fn batch_remove(&self, calling_info: &CallingInfo, attributes_array: &[AssetMap]) -> Result<()> {
        execute_batch!(operations::batch_remove, calling_info, attributes_array)
    }

    fn batch_update(
        &self, calling_info: &CallingInfo,
        attributes_array: &[AssetMap],
        attributes_to_update_array: &[AssetMap]
    ) -> Result<Vec<(u32, u32)>> {
        execute_batch!(operations::batch_update, calling_info, attributes_array, attributes_to_update_array)
    }
}

#[cfg(feature = "AssetTest")]
/// stub for test
pub mod ut_core_service_lib_stub {
    include!{"../../../../test/asset/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"}
}