Xi Li

199 papers B 55C 13Journal 64Unranked 67
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE Trans. Cogn. Commun. Netw.
Kailin Wang, Heli Zhang, Zhi Liu, Xi Li, Dusit Niyato
2026 J jnl
IEEE Internet Things J.
Jiarong Lu, Xi Li, Heli Zhang, Victor C. M. Leung
2025 J jnl
IEEE Internet Things J.
Zhipeng Liu, Xi Li, Hong Ji, Heli Zhang
2025 J jnl
IEEE Trans. Aerosp. Electron. Syst.
Ruiji Duan, Heli Zhang, Xi Li
2025 C conf
CloudCom
Huanran Su, Kailin Wang, Heli Zhang, Xi Li
2025 J jnl
IEEE Trans. Mob. Comput.
Ziqi Li, Jia Hu, Xi Li, Heli Zhang, Geyong Min
2025 J jnl
IEEE Trans. Veh. Technol.
Zhipeng Liu, Xi Li, Hong Ji, Heli Zhang, Victor C. M. Leung
2025 C conf
CloudCom
Zixu Zhu, Huanran Su, Ruiji Duan, Xi Li, Heli Zhang
2025 C conf
CloudCom
Sihan Yuan, Jiarong Lu, Xi Li, Heli Zhang
2025 C conf
CloudCom
Zening Li, Zhipeng Liu, Xi Li, Heli Zhang
2025 B conf
WCNC
Jiarong Lu, Xi Li, Heli Zhang
2024 J jnl
IEEE Trans. Wirel. Commun.
Yueqiang Xu, Heli Zhang, Xi Li, F. Richard Yu, Hong Ji, Victor C. M. Leung
2024 B conf
PIMRC
Zehui Zhao, Heli Zhang, Kailin Wang, Xi Li
2024 J jnl
IEEE Trans. Veh. Technol.
Ziqi Li, Heli Zhang, Xi Li, Hong Ji, Victor C. M. Leung
2024 J jnl
IEEE Trans. Veh. Technol.
Kailin Wang, Xun Shao, Heli Zhang, Kaoru Ota, Xi Li, Mianxiong Dong
2024 B conf
PIMRC
Zhipeng Liu, Xi Li, Heli Zhang
2024 J jnl
IEEE Trans. Consumer Electron.
Lichao Yang, Kailin Wang, Mingyan Xiao, Heli Zhang, Ming Li, Xi Li, Hong Ji
2024 J jnl
IEEE Trans. Netw. Sci. Eng.
Ziqi Li, Heli Zhang, Chunyu Liu, Xi Li, Hong Ji, Victor C. M. Leung
2024 conf
ICCIP
Jingxue Tian, Xi Li, Heli Zhang
2023 B conf
PIMRC
Chang Han, Xi Li, Hong Ji, Heli Zhang
2023 B conf
PIMRC
Wenliang Cheng, Yueqiang Xu, Quansheng Xu, Heli Zhang, Xi Li, Xun Shao
2023 conf
ICC Workshops
Heng Shen, Xi Li, Hong Ji, Heli Zhang
2023 J jnl
Int. J. Commun. Syst.
Mancong Kang, Xi Li, Hong Ji, Heli Zhang
2023 B conf
WCNC
Junjie Qi, Heli Zhang, Xi Li, Hong Ji, Xun Shao
2023 B conf
PIMRC
Mancong Kang, Xi Li, Hong Ji, Heli Zhang
2023 B conf
PIMRC
Zhipeng Liu, Xi Li, Hong Ji, Heli Zhang
2023 conf
iThings/GreenCom/CPSCom/SmartData/Cybermatics
Chang Han, Yaohui Song, Xi Li, Hong Ji, Heli Zhang
2023 conf
IC-NIDC
Qizan Liu, Xi Li, Hong Ji, Heli Zhang
2023 B conf
PIMRC
Lishuang Liu, Xi Li, Hong Ji, Heli Zhang
2023 J jnl
IEEE Trans. Netw. Sci. Eng.
Chunyu Liu, Kailin Wang, Heli Zhang, Xi Li, Hong Ji
2023 J jnl
Future Gener. Comput. Syst.
Yu Huang, Heli Zhang, Xun Shao, Xi Li, Hong Ji
2023 J jnl
IEEE Internet Things J.
Shuming Seng, Changqing Luo, Xi Li, Hong Ji
2023 J jnl
IEEE Trans. Veh. Technol.
Zhipeng Liu, Xi Li, Hong Ji, Heli Zhang, Victor C. M. Leung
2023 J jnl
IEEE Trans. Veh. Technol.
Yueqiang Xu, Heli Zhang, Xi Li, F. Richard Yu, Victor C. M. Leung, Hong Ji
2023 B conf
GLOBECOM
Qizan Liu, Xi Li, Hong Ji, Heli Zhang
2022 J jnl
ACM Comput. Surv.
Xi Li, Zehua Wang, Victor C. M. Leung, Hong Ji, Yiming Liu, Heli Zhang
2022 conf
ICC Workshops
Maosheng Zhu, Xi Li, Hong Ji, Heli Zhang
2022 B conf
GLOBECOM
Maosheng Zhu, Xi Li, Hong Ji, Heli Zhang
2022 B conf
PIMRC
Yaohui Song, Xi Li, Hong Ji, Heli Zhang
2022 J jnl
IEEE Internet Things J.
Yuandong Zhuang, Xi Li, Hong Ji, Heli Zhang
2022 J jnl
IEEE Trans. Green Commun. Netw.
Yuandong Zhuang, Xi Li, Hong Ji, Heli Zhang
2022 C conf
ICCC
Yaohui Song, Xi Li, Hong Ji, Heli Zhang
2022 B conf
WCNC
Yuying Wang, Xi Li, Hong Ji, Heli Zhang
2022 conf
ICC
Shuming Seng, Guang Yang, Changqing Luo, Xi Li, Hong Ji
2022 J jnl
IEEE Trans. Wirel. Commun.
Yueqiang Xu, Heli Zhang, Hong Ji, Lichao Yang, Xi Li, Victor C. M. Leung
2021 conf
ICC Workshops
Hang Wang, Xi Li, Hong Ji, Heli Zhang
2021 B conf
WCNC
Xiaochun Lu, Xi Li, Hong Ji, Heli Zhang
2021 B conf
GLOBECOM
Yutong Zhou, Xi Li, Hong Ji, Heli Zhang
2021 J jnl
CoRR
Xi Li, Zehua Wang, Victor C. M. Leung, Hong Ji, Yiming Liu, Heli Zhang
2021 B conf
GLOBECOM
Yunfei Cui, Heli Zhang, Hong Ji, Xi Li, Xun Shao
2021 J jnl
IEEE Internet Things J.
Lichao Yang, Ming Li, Heli Zhang, Hong Ji, Mingyan Xiao, Xi Li
2021 J jnl
IEEE Internet Things J.
Fengxian Guo, F. Richard Yu, Heli Zhang, Xi Li, Hong Ji, Victor C. M. Leung
2021 J jnl
IEEE Trans. Netw. Sci. Eng.
Shuming Seng, Guang Yang, Xi Li, Hong Ji, Changqing Luo
2021 B conf
WCNC
Xinxin Yang, Heli Zhang, Hong Ji, Xi Li
2021 conf
IC-NIDC
Cong Meng, Heli Zhang, Hong Ji, Xi Li
2021 conf
ICC Workshops
Guoqiang Tang, Xi Li, Hong Ji, Heli Zhang
2021 B conf
WCNC
Xufeng He, Xi Li, Hong Ji, Heli Zhang
2021 conf
ICC Workshops
Xufeng He, Xi Li, Hong Ji, Heli Zhang
2021 B conf
WCNC
Yang Li, Heli Zhang, Hong Ji, Xi Li
2021 J jnl
IEEE Trans. Netw. Sci. Eng.
Shuming Seng, Changqing Luo, Xi Li, Heli Zhang, Hong Ji
2020 J jnl
IEEE Trans. Green Commun. Netw.
Heli Zhang, Yazhou Wang, Hong Ji, Xi Li
2020 J jnl
IEEE Trans. Veh. Technol.
Fengxian Guo, F. Richard Yu, Heli Zhang, Hong Ji, Victor C. M. Leung, Xi Li
2020 J jnl
IEEE Commun. Surv. Tutorials
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2020 B conf
PIMRC
Xinxin Yang, Heli Zhang, Hong Ji, Xi Li
2020 J jnl
Wirel. Networks
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2020 J jnl
Int. J. Commun. Syst.
Xi Li, Changqing Luo, Hong Ji, Yuandong Zhuang, Heli Zhang, Victor C. M. Leung
2020 B conf
GLOBECOM
Shuming Seng, Guang Yang, Xi Li, Hong Ji, Changqing Luo
2020 B conf
GLOBECOM
Chao Chen, Xi Li, Hong Ji, Heli Zhang
2020 B conf
PIMRC
Xinhe Zhang, Heli Zhang, Hong Ji, Xi Li
2020 J jnl
IEEE Trans. Veh. Technol.
Yuandong Zhuang, Xi Li, Hong Ji, Heli Zhang, Victor C. M. Leung
2019 conf
ICC
Shuming Seng, Xi Li, Changqing Luo, Hong Ji, Heli Zhang
2019 conf
iThings/GreenCom/CPSCom/SmartData
Yazhou Wang, Heli Zhang, Hong Ji, Xi Li
2019 B conf
WCNC
JunShi Hu, Heli Zhang, Yiming Liu, Xi Li, Hong Ji
2019 J jnl
Wirel. Networks
Lei Chen, Xi Li, Hong Ji, Victor C. M. Leung
2019 J jnl
IEEE Trans. Veh. Technol.
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2019 conf
GLOBECOM Workshops
Shuying Chen, Xi Li, Changqing Luo, Hong Ji, Heli Zhang
2019 conf
GLOBECOM Workshops
Xiaojing Han, Xi Li, Changqing Luo, Hong Ji, Heli Zhang
2019 B conf
PIMRC
Jiawei Li, Heli Zhang, Hong Ji, Xi Li
2019 conf
ICC Workshops
Songqi Tian, Xi Li, Hong Ji, Heli Zhang
2019 B conf
WCNC
Yuandong Zhuang, Xi Li, Hong Ji, Heli Zhang
2019 J jnl
IEEE Access
Xi Li, Yiming Liu, Hong Ji, Heli Zhang, Victor C. M. Leung
2019 B conf
WCNC
Peng Sun, Heli Zhang, Hong Ji, Xi Li
2019 B conf
PIMRC
Lichao Yang, Heli Zhang, Hong Ji, Xi Li
2019 conf
ChinaCom (1)
JunShi Hu, Heli Zhang, Xi Li, Hong Ji
2018 J jnl
IEEE/ACM Trans. Netw.
Lichao Yang, Heli Zhang, Xi Li, Hong Ji, Victor C. M. Leung
2018 conf
ChinaCom
Zhongming Gao, Xi Li, Hong Ji, Heli Zhang
2018 B conf
WCNC
Bowen Liu, Heli Zhang, Hong Ji, Xi Li
2018 J jnl
IEEE/ACM Trans. Netw.
Fengxian Guo, Heli Zhang, Hong Ji, Xi Li, Victor C. M. Leung
2018 conf
ChinaCom
Chengjia Hu, Xi Li, Hong Ji, Heli Zhang
2018 conf
ChinaCom
Yu Li, Heli Zhang, Hong Ji, Xi Li
2018 C conf
ICCC
Fan Jia, Heli Zhang, Hong Ji, Xi Li
2018 J jnl
IEEE Trans. Veh. Technol.
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2018 conf
ICCC Workshops
Hansong Wang, Xi Li, Hong Ji, Heli Zhang
2018 conf
ICCC Workshops
Wei Chen, Heli Zhang, Hong Ji, Xi Li
2018 conf
ChinaCom
Yuling Zuo, Heli Zhang, Hong Ji, Xi Li
2018 conf
ICC Workshops
Fengxian Guo, Heli Zhang, Hong Ji, Xi Li, Victor C. M. Leung
2018 conf
GLOBECOM Workshops
Hansong Wang, Xi Li, Hong Ji, Heli Zhang
2018 conf
ChinaCom
Rui Shi, Xi Li, Hong Ji, Heli Zhang
2018 conf
INFOCOM Workshops
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2018 conf
ICC Workshops
Shuming Seng, Xi Li, Hong Ji, Heli Zhang
2018 conf
ICC
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Heli Zhang, Victor C. M. Leung
2018 J jnl
IEEE Trans. Veh. Technol.
Fengxian Guo, Heli Zhang, Xi Li, Hong Ji, Victor C. M. Leung
2018 conf
ChinaCom
Wei Chen, Heli Zhang, Hong Ji, Xi Li
2018 conf
INFOCOM Workshops
Fengxian Guo, Liangde Ma, Heli Zhang, Hong Ji, Xi Li
2018 conf
GLOBECOM Workshops
Songqi Tian, Xi Li, Hong Ji, Heli Zhang
2018 conf
ChinaCom
Zhenya Liu, Xi Li, Hong Ji, Heli Zhang
2018 C conf
ICCC
Wenbin Fu, Xi Li, Hong Ji, Heli Zhang
2018 conf
ChinaCom
Rui Shi, Xi Li, Hong Ji, Heli Zhang
2018 B conf
GLOBECOM
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2018 B conf
WCNC
Yiming Liu, F. Richard Yu, Xi Li, Hong Ji, Heli Zhang, Victor C. M. Leung
2018 conf
ICCC Workshops
Peng Sun, Heli Zhang, Hong Ji, Xi Li
2018 J jnl
IEEE Trans. Veh. Technol.
Zhiyuan Tan, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2017 conf
ChinaCom (2)
Jichen Jiang, Xi Li, Hong Ji
2017 J jnl
Int. J. Commun. Syst.
Baokun Shan, Xi Li, Hong Ji, Yi Li
2017 conf
ICC Workshops
Yiming Liu, Xi Li, Hong Ji, Heli Zhang
2017 conf
INFOCOM Workshops
Yiming Liu, Xi Li, Hong Ji, Heli Zhang
2017 J jnl
Int. J. Commun. Syst.
Bowen Liu, Heli Zhang, Hong Ji, Xi Li
2017 B conf
PIMRC
Pengbo Yang, Xi Li, Hong Ji, Heli Zhang
2017 conf
ICC
Bowen Liu, Heli Zhang, Hong Ji, Xi Li, Victor C. M. Leung
2017 C conf
QSHINE
Tianhao Wu, Xi Li, Hong Ji, Heli Zhang
2017 conf
ChinaCom (1)
Hong Feng, Xi Li, Hong Ji
2017 conf
GLOBECOM Workshops
Tianhao Wu, Xi Li, Hong Ji, Heli Zhang
2017 C conf
QSHINE
Shuming Seng, Xi Li, Hong Ji, Heli Zhang
2017 B conf
PIMRC
Teng Yang, Heli Zhang, Hong Ji, Xi Li
2017 conf
INFOCOM Workshops
Heli Zhang, Jun Guo, Lichao Yang, Xi Li, Hong Ji
2017 B conf
PIMRC
Fengxian Guo, Heli Zhang, Xi Li, Hong Ji
2017 conf
GLOBECOM Workshops
Jun Guo, Heli Zhang, Lichao Yang, Hong Ji, Xi Li
2017 J jnl
IEEE J. Sel. Areas Commun.
Yiming Liu, Xi Li, F. Richard Yu, Hong Ji, Heli Zhang, Victor C. M. Leung
2017 C conf
QSHINE
Pengbo Yang, Xi Li, Hong Ji, Heli Zhang
2017 J jnl
CoRR
Zhiyuan Tan, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2017 conf
ChinaCom (2)
Yingshuan Song, Heli Zhang, Xi Li, Chunsheng Zhu, Hong Ji
2017 B conf
WCNC
Zhiyuan Tan, Xi Li, F. Richard Yu, Lei Chen, Hong Ji, Victor C. M. Leung
2017 B conf
GLOBECOM
Zhiyuan Tan, Xi Li, F. Richard Yu, Hong Ji, Victor C. M. Leung
2017 B conf
GLOBECOM
Heli Zhang, Bowen Liu, Ming Li, Hong Ji, Xi Li, Victor C. M. Leung
2017 conf
ChinaCom (1)
Tianming Song, Heli Zhang, Xi Li, Chunsheng Zhu, Hong Ji
2017 C conf
QSHINE
Jichen Jiang, Xi Li, Hong Ji, Heli Zhang
2017 conf
INFOCOM Workshops
Zhiyuan Tan, F. Richard Yu, Xi Li, Hong Ji, Victor C. M. Leung
2016 conf
ICC
Lei Chen, F. Richard Yu, Hong Ji, Victor C. M. Leung, Xi Li, Bo Rong
2016 J jnl
IEEE Trans. Veh. Technol.
Quansheng Xu, Hong Ji, Xi Li, Heli Zhang
2016 conf
WICON
Heli Zhang, Wang Yang, Hong Ji, Xi Li, Victor C. M. Leung, Lichao Yang
2016 conf
ChinaCom (2)
Shanshan Yu, Xi Li, Hong Ji, Yiming Liu
2016 conf
ICC
Lei Chen, F. Richard Yu, Hong Ji, Bo Rong, Xi Li, Victor C. M. Leung
2016 conf
VTC Fall
Xi Li, Hong Ji, Ke Wang, Heli Zhang
2016 J jnl
IEEE J. Sel. Areas Commun.
Lei Chen, F. Richard Yu, Hong Ji, Bo Rong, Xi Li, Victor C. M. Leung
2016 B conf
PIMRC
Bowen Liu, Heli Zhang, Hong Ji, Xi Li, Ke Wang
2016 conf
CITS
Yiming Liu, Xi Li, Hong Ji, Ke Wang, Heli Zhang
2016 B conf
GLOBECOM
Zhi Yu, Ke Wang, Hong Ji, Xi Li, Heli Zhang
2016 J jnl
CoRR
Zhi Yu, Ke Wang, Hong Ji, Xi Li, Heli Zhang
2016 conf
DMIAF
Bowen Liu, Heli Zhang, Hong Ji, Xi Li, Ke Wang
2016 J jnl
IEEE Trans. Veh. Technol.
Ke Wang, Xi Li, Hong Ji, Xiaojiang Du
2016 conf
DMIAF
Zhiyuan Tan, Xi Li, Hong Ji, Ke Wang, Heli Zhang
2016 J jnl
Int. J. Commun. Syst.
Xi Li, Heli Zhang, Ke Wang
2016 J jnl
IEEE Wirel. Commun.
Shanzhi Chen, Fei Qin, Bo Hu, Xi Li, Zhonglin Chen
2015 conf
GLOBECOM Workshops
Zhi Yu, Ke Wang, Hong Ji, Xi Li, Heli Zhang
2015 C conf
CloudCom
Heli Zhang, Hong Ji, Xi Li, Ke Wang, Weidong Wang
2015 J jnl
IEEE Commun. Surv. Tutorials
Gang Liu, F. Richard Yu, Hong Ji, Victor C. M. Leung, Xi Li
2015 J jnl
IEEE Netw.
Gang Liu, F. Richard Yu, Hong Ji, Victor C. M. Leung, Xi Li
2015 J jnl
IEEE Wirel. Commun.
Heli Zhang, Shanzhi Chen, Xi Li, Hong Ji, Xiaojiang Du
2015 J jnl
IET Commun.
Quansheng Xu, Hong Ji, Xi Li
2015 J jnl
IEEE Trans. Veh. Technol.
Ke Wang, Xi Li, Hong Ji, Xiaojiang Du
2015 C conf
CloudCom
Chunsheng Zhu, Xi Li, Hong Ji, Victor C. M. Leung
2015 J jnl
Int. J. Satell. Commun. Netw.
Gang Liu, Hong Ji, Yi Li, Xi Li, Yongbin Wang
2015 conf
INFOCOM Workshops
Heli Zhang, Weidong Wang, Xi Li, Hong Ji
2015 B conf
PIMRC
Han Hao, Ke Wang, Hong Ji, Xi Li, Heli Zhang
2014 B conf
WCNC
Lei Chen, Xi Li, Hong Ji
2014 conf
ICC
Ke Wang, Shanzhi Chen, Xi Li, Hong Ji
2014 J jnl
IEEE Trans. Veh. Technol.
Quansheng Xu, Xi Li, Hong Ji, Xiaojiang Du
2014 conf
ICC
Quansheng Xu, Hong Ji, Xi Li
2014 J jnl
IEEE Commun. Lett.
Ke Wang, Xi Li, Hong Ji
2014 J jnl
Int. J. Commun. Syst.
Wendong Ge, Hong Ji, Xi Li
2014 conf
ICC
Quansheng Xu, Xi Li, Hong Ji
2014 B conf
WCNC
Jian Liu, Xi Li, Hong Ji, Yiwen Tang
2013 conf
ICC
Quansheng Xu, Xi Li, Hong Ji, Liping Yao
2013 B conf
WCNC
Xiaoliang Zhang, Hong Ji, Yi Li, Xi Li
2013 J jnl
Mob. Networks Appl.
Wendong Ge, Shanzhi Chen, Hong Ji, Xi Li, Victor C. M. Leung
2013 J jnl
Int. J. Commun. Syst.
Xi Li, Shanzhi Chen, Dan Chen, Hong Ji, Victor C. M. Leung
2013 B conf
GLOBECOM
Ke Wang, Xi Li, Hong Ji, Xiaoliang Zhang
2013 J jnl
Mob. Networks Appl.
Xi Li, Hong Ji, Yi Li
2013 conf
ICC
Yisheng Zhao, Xi Li, Yi Li, Hong Ji
2013 J jnl
IET Commun.
Wendong Ge, Hong Ji, Xi Li, Victor C. M. Leung
2012 B conf
GLOBECOM
Lanjie Zhai, Hong Ji, Xi Li, Yiwen Tang
2012 B conf
WCNC
Heli Zhang, Hong Ji, Xi Li
2012 B conf
WCNC
Liang Chen, Hong Ji, Yi Li, Xi Li
2012 J jnl
Int. J. Commun. Syst.
Yi Li, Hong Ji, Xi Li, Victor C. M. Leung
2012 B conf
GLOBECOM
Yisheng Zhao, Xi Li, Xiaoliang Zhang, Yi Li, Hong Ji
2012 B conf
WCNC
Heli Zhang, Hong Ji, Xi Li
2012 conf
ICC
Yisheng Zhao, Xi Li, Hong Ji
2011 conf
CHINACOM
Jinghua Guo, Hong Ji, Yi Li, Xi Li
2011 conf
ICC
Dan Chen, Hong Ji, Xi Li
2011 B conf
WCNC
Dan Chen, Hong Ji, Xi Li
2011 B conf
WCNC
Ying Ni, Hong Ji, Xi Li
2011 conf
CHINACOM
Xi Li, Hong Ji, Yi Li
2011 conf
CHINACOM
Liang Chen, Hong Ji, Yi Li, Xi Li
2011 B conf
WCNC
Dan Chen, Hong Ji, Xi Li
2011 conf
CHINACOM
Yisheng Zhao, Xi Li, Hong Ji
2010 B conf
WCNC
Dan Chen, Hong Ji, Xi Li, Kun Zhao
2009 conf
ICC
Xi Li, Hong Ji, F. Richard Yu, Ruiming Zheng
2009 B conf
WCNC
Xi Li, Hong Ji, Ruiming Zheng, Yi Li, F. Richard Yu
2009 B conf
PIMRC
Ming Li, Xi Li, Hong Ji
s3-storage/s3_uploader.py
← Index s3-storage/s3_uploader.py python
# python s3_uploader.py /path/to/files repository_name --notes "Optional notes"
import hashlib
import multiprocessing
from multiprocessing import Pool
import sys
import logging
from logging.handlers import QueueHandler
from datetime import datetime
import os
import time
import json
from enum import Enum
import tempfile
import magic
from typing import Optional, Dict, Any
from dotenv import load_dotenv
from datetime import timezone

# File handling
import py7zr
import pyzipper
from magika import Magika

# S3 and database
from minio import Minio
import clickhouse_connect

load_dotenv(override=True)

class UploadResult(Enum):
    CORRECTLY = 0
    FAILED = 1
    SKIPPED = 2  # For duplicates

class FileNameFormatter(logging.Formatter):
    def format(self, record):
        record.filenameinfo = getattr(record, "filenameinfo", "unknown")
        return super().format(record)

def setup_logger(log_file, filename):
    """Set up a logger for a specific file"""
    logger = logging.getLogger(filename)
    if not logger.handlers:
        if os.getenv("SERVER_ENV") == "prod":
            logger.setLevel(logging.INFO)
        else:
            logger.setLevel(logging.DEBUG)
            
        # Create file handler
        handler = logging.FileHandler(log_file)
        formatter = FileNameFormatter(
            "%(asctime)s - %(filenameinfo)s - %(levelname)s - %(message)s"
        )
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.propagate = False
    return logger

def logger_thread(log_queue, log_file):
    handler = logging.FileHandler(log_file)
    formatter = FileNameFormatter(
        "%(asctime)s - %(filenameinfo)s - %(levelname)s - %(message)s"
    )
    handler.setFormatter(formatter)

    while True:
        try:
            record = log_queue.get()
            if record is None:
                break
            message = formatter.format(record)
            handler.stream.write(message + "\n")
            handler.stream.flush()
        except Exception:
            import traceback
            print("[ERR] Error in logger thread:", file=sys.stderr)
            traceback.print_exc(file=sys.stderr)

def get_s3_key(sha256: str, is_archived: bool, original_ext: str = None) -> str:
    """
    Generate S3 key using sharding pattern with appropriate extension
    
    Args:
        sha256: The file's SHA256 hash
        is_archived: Whether the file was already archived (zip/7z)
        original_ext: The original file extension (if any)
    """
    # If file was already archived, keep its extension
    if is_archived and original_ext and original_ext.lower() in ['.zip', '.7z']:
        extension = original_ext
    else:
        # For files we archived ourselves, use .zip
        extension = '.zip'
        
    return f"{sha256[:2]}/{sha256[2:4]}/{sha256}{extension}"

def process_single_file(args):
    """Independent worker function for processing a single file"""
    filepath, file_number, total_files, config, log_file = args
    filename = os.path.basename(filepath)
    base_logger = setup_logger(log_file, filename)
    extra = {"filenameinfo": filename}
    logger = logging.LoggerAdapter(base_logger, extra)
    s3_client = None
    ch_client = None

    try:
        logger.info(f"Processing file {file_number}/{total_files}: {filepath}")

        # Create connections for this process
        s3_client = Minio(
            config["s3_endpoint"],
            access_key=config["s3_access_key"],
            secret_key=config["s3_secret_key"],
            secure=True
        )
        
        ch_client = clickhouse_connect.get_client(
            host=config['clickhouse_host'],
            port=config['clickhouse_port'],
            username=config['clickhouse_user'],
            password=config['clickhouse_password'],
            database=config['clickhouse_database'],
            verify=config['clickhouse_verify']
        )

        # Process the file and get contents
        ext = os.path.splitext(filepath.lower())[1]
        is_archived = ext in ['.zip', '.7z']

        with tempfile.TemporaryDirectory() as temp_dir:
            try:
                if filename.startswith("."):
                    logger.debug(f"Skipping hidden file: {filename}")
                    return UploadResult.SKIPPED

                elif ext == ".zip":
                    logger.debug(f"Processing ZIP file: {filepath}")
                    with pyzipper.AESZipFile(filepath) as zf:
                        zf.pwd = b"infected"
                        filename = zf.namelist()[0]
                        zf.extractall(temp_dir)
                        extracted_path = os.path.join(temp_dir, filename)
                        
                        with open(extracted_path, "rb") as f:
                            file_content = f.read()
                        with open(filepath, "rb") as f:
                            archive_content = f.read()
                            
                elif ext == ".7z":
                    logger.debug(f"Processing 7z file: {filepath}")
                    with py7zr.SevenZipFile(filepath, mode="r", password="infected") as z:
                        z.extractall(path=temp_dir)
                        # Get first file in the archive
                        for root, _, files in os.walk(temp_dir):
                            if files:
                                filename = files[0]
                                extracted_path = os.path.join(root, filename)
                                break
                                
                        with open(extracted_path, "rb") as f:
                            file_content = f.read()
                        with open(filepath, "rb") as f:
                            archive_content = f.read()
                else:
                    logger.debug(f"Processing non-archived file: {filepath}")
                    # For non-archived files, create password protected zip
                    with open(filepath, "rb") as f:
                        file_content = f.read()
                    filename = os.path.basename(filepath)
                    
                    # Create password protected zip
                    with tempfile.NamedTemporaryFile(delete=False) as temp_zip:
                        with pyzipper.AESZipFile(
                            temp_zip.name,
                            'w',
                            compression=pyzipper.ZIP_LZMA,
                            encryption=pyzipper.WZ_AES
                        ) as zf:
                            zf.pwd = b'infected'
                            zf.writestr(filename, file_content)
                        
                        with open(temp_zip.name, 'rb') as f:
                            archive_content = f.read()
                    
                    os.unlink(temp_zip.name)

                # Calculate hash
                sha256 = hashlib.sha256(file_content).hexdigest()
                # s3_key = f"{sha256[:2]}/{sha256[2:4]}/{sha256}"
                s3_key = get_s3_key(sha256, is_archived, ext if is_archived else None)
                logger.debug(f"Calculated SHA256: {sha256}")

                # Check if exists in S3
                try:
                    s3_client.stat_object(config["s3_bucket"], s3_key)
                    exists = True
                    logger.debug(f"File exists in S3: {s3_key}")
                except:
                    exists = False
                    logger.debug(f"File does not exist in S3: {s3_key}")

                # Prepare metadata
                max_timestamp = int(datetime(9999, 12, 31, 23, 59, 59, tzinfo=timezone.utc).timestamp() * 1000)
                now = datetime.now(timezone.utc)
                now_timestamp = int(now.timestamp() * 1000)
                inverted_timestamp = datetime.fromtimestamp((max_timestamp - now_timestamp)/1000, tz=timezone.utc)


                data = [[
                    sha256,                     # String
                    filename,                   # String
                    config['repository'],       # String
                    len(file_content),         # UInt64
                    magic.from_buffer(file_content),        # String
                    magic.from_buffer(file_content, mime=True),  # String
                    Magika().identify_bytes(file_content).output.ct_label,  # String
                    now,                       # upload_date
                    now,                       # first_seen
                    inverted_timestamp,        # version_date for replacing
                    config['s3_bucket'],       # String
                    s3_key,                    # String
                    config['notes'] if config['notes'] else None  # Nullable(String)
                ]]

                column_names = [
                    'sha256', 'filename', 'repository', 'file_size',
                    'filetype', 'filetype_mime', 'filetype_magika',
                    'upload_date', 'first_seen', 'version_date', 's3_bucket', 's3_key', 'notes'
                ]

                column_type_names = [
                    'String', 'String', 'LowCardinality(String)', 'UInt64',
                    'String', 'String', 'String',
                    'DateTime64(3, \'UTC\')', 'DateTime64(3, \'UTC\')', 
                    'DateTime64(3, \'UTC\')', 'String', 'String', 'Nullable(String)'
                ]
                
                if exists:
                    # Just update database
                    try:
                        ch_client.insert(
                            'samples_catalog',
                            data,
                            column_names=column_names,
                            column_type_names=column_type_names,
                            settings={'input_format_values_interpret_expressions': 0}
                        )
                        logger.info(f"Updated metadata for existing file: {sha256}")
                    except Exception as e:
                        logger.error(f"Error inserting metadata: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                    return UploadResult.SKIPPED
                else:
                    try:    
                        # Upload to S3 and insert metadata
                        import io
                        file_data = io.BytesIO(archive_content)
                        s3_client.put_object(
                            bucket_name=config['s3_bucket'],
                                object_name=s3_key,
                                data=file_data,
                                length=len(archive_content)
                            )
                        logger.info(f"Uploaded to S3: {s3_key}")
                    except Exception as e:
                        logger.error(f"Error uploading to S3: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                        return UploadResult.FAILED
                    
                    try:
                        ch_client.insert(
                            'samples_catalog',
                            data,
                            column_names=column_names,
                            column_type_names=column_type_names,
                            settings={'input_format_values_interpret_expressions': 0}
                        )
                        logger.info(f"Successfully processed file: {sha256}")
                        return UploadResult.CORRECTLY
                    except Exception as e:
                        logger.error(f"Error inserting metadata: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                        return UploadResult.FAILED

            except Exception as e:
                logger.error(f"Error processing file: {str(e)}")
                import traceback
                logger.error(f"Full traceback: {traceback.format_exc()}")
                return UploadResult.FAILED

    except Exception as e:
        logger.error(f"Error in worker: {str(e)}")
        return UploadResult.FAILED
    finally:
        # if s3_client:
        #     s3_client.close()
        if ch_client:
            ch_client.close()
        if base_logger and base_logger.handlers:
            for handler in base_logger.handlers:
                handler.close()
            base_logger.handlers.clear()

class S3Uploader:
    def __init__(self, path: str, repository: str, notes: Optional[str] = None):
        """
        Initialize S3 uploader
        
        Args:
            path: Path to file or directory to process
            repository: Repository name
            notes: Optional notes to add to all files
        """
        self.path = path
        self.repository = repository
        self.notes = notes
        
        # Initialize multiprocessing components
        self.manager = multiprocessing.Manager()
        # self.log_queue = self.manager.Queue()
        self.total_results = self.manager.dict({result: 0 for result in UploadResult})
        
        # Set up logging
        self.today = datetime.today().strftime("%Y%m%dT%H%M%S")
        # self.log_file = os.getenv("LOG_FILE_PATH", "./logs/") + f"{self.today}-{self.repository}-upload.txt"
        log_base_path = os.getenv("LOG_FILE_PATH")
        if not log_base_path:
            log_base_path = os.path.join(os.getcwd(), "logs")  # Default to ./logs directory
        self.log_file = os.path.join(log_base_path, f"{self.today}-{self.repository}-upload.txt")
        os.makedirs(os.path.dirname(self.log_file), exist_ok=True)

        # Initialize S3 client
        self.s3_client = Minio(
            os.getenv("S3_ENDPOINT"),
            access_key=os.getenv("S3_ACCESS_KEY"),
            secret_key=os.getenv("S3_SECRET_KEY"),
            secure=True  # Set to False if not using HTTPS
        )
        self.bucket_name = os.getenv("S3_BUCKET")

        # Initialize ClickHouse client
        self.ch_client = clickhouse_connect.get_client(
            host=os.getenv('CLICKHOUSE_HOST'),
            port=os.getenv('CLICKHOUSE_PORT'),
            username=os.getenv('CLICKHOUSE_USER'),
            password=os.getenv('CLICKHOUSE_PASSWORD'),
            database=os.getenv('CLICKHOUSE_DATABASE'),
            verify=os.getenv('CLICKHOUSE_ENFORCE_SSL', 'False')
        )
        
        # Ensure database table exists
        self._create_table()

    def _create_table(self):
        """Create the file catalog table if it doesn't exist"""
        create_table_query = """
            CREATE TABLE IF NOT EXISTS samples_catalog (
                sha256 String,
                filename String,
                repository LowCardinality(String),
                file_size UInt64,
                filetype String,
                filetype_mime String,
                filetype_magika String,
                upload_date DateTime64(3, 'UTC'),
                first_seen DateTime64(3, 'UTC'),
                version_date DateTime64(3, 'UTC'),  -- This will be inverted timestamp
                s3_bucket String,
                s3_key String,
                notes Nullable(String),
                PRIMARY KEY (sha256, repository)
            ) ENGINE = ReplacingMergeTree(version_date)
            ORDER BY (sha256, repository)
        """
        self.ch_client.command(create_table_query)

    # # def upload(self):
    # #     """Main upload function with multiprocessing support"""
    # #     start_time = time.time()

    # #     config = {
    # #         "s3_endpoint": os.getenv("S3_ENDPOINT"),
    # #         "s3_access_key": os.getenv("S3_ACCESS_KEY"),
    # #         "s3_secret_key": os.getenv("S3_SECRET_KEY"),
    # #         "s3_bucket": os.getenv("S3_BUCKET"),
    # #         "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
    # #         "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
    # #         "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
    # #         "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
    # #         "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
    # #         "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
    # #         "repository": self.repository,
    # #         "notes": self.notes
    # #     }

    # #     try:
    # #         # Get list of files
    # #         if os.path.isfile(self.path):
    # #             files = [self.path] if not self.path.endswith('.txt') else [
    # #                 line.strip() for line in open(self.path) 
    # #                 if line.strip() and not os.path.basename(line.strip()).startswith('.')
    # #             ]
    # #         elif os.path.isdir(self.path):
    # #             files = [
    # #                 os.path.join(root, file)
    # #                 for root, _, files in os.walk(self.path)
    # #                 for file in files
    # #                 if not file.startswith('.')
    # #             ]
    # #         else:
    # #             print(f"[ERR] Invalid path: {self.path}")
    # #             return

    # #         total_files = len(files)
    # #         print(f"Found {total_files} files to process")

    # #         with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
    # #             results = pool.map(
    # #                 process_single_file,
    # #                 [(f, i + 1, total_files, config, self.log_file) for i, f in enumerate(files)]
    # #             )

    # #         # Update statistics
    # #         for result in results:
    # #             if result is not None:
    # #                 self.total_results[result] += 1

    # #         # Generate summary
    # #         end_time = time.time()
    # #         elapsed_time = end_time - start_time
    # #         elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

    # #         summary = (
    # #             f"\n\nUpload finished for {self.path}"
    # #             f"\nTime required: {elapsed_time_pretty}"
    # #             f"\nResults:"
    # #             f"\n- Total processed: {sum(self.total_results.values())}"
    # #             f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
    # #             f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
    # #             f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
    # #         )

    # #         with open(self.log_file, "a") as f:
    # #             f.write(summary)

    # #         print(summary)

    # #     except Exception as e:
    # #         print(f"[ERR] Error in upload process: {str(e)}")

    # def upload(self):
    #     """Main upload function with multiprocessing support"""
    #     start_time = time.time()
    #     BATCH_SIZE = 1000  # Process files in batches of 1000

    #     config = {
    #         "s3_endpoint": os.getenv("S3_ENDPOINT"),
    #         "s3_access_key": os.getenv("S3_ACCESS_KEY"),
    #         "s3_secret_key": os.getenv("S3_SECRET_KEY"),
    #         "s3_bucket": os.getenv("S3_BUCKET"),
    #         "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
    #         "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
    #         "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
    #         "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
    #         "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
    #         "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
    #         "repository": self.repository,
    #         "notes": self.notes
    #     }

    #     try:
    #         # Get list of files
    #         if os.path.isfile(self.path):
    #             files = [self.path] if not self.path.endswith('.txt') else [
    #                 line.strip() for line in open(self.path) 
    #                 if line.strip() and not os.path.basename(line.strip()).startswith('.')
    #             ]
    #         elif os.path.isdir(self.path):
    #             files = [
    #                 os.path.join(root, file)
    #                 for root, _, files in os.walk(self.path)
    #                 for file in files
    #                 if not file.startswith('.')
    #             ]
    #         else:
    #             print(f"[ERR] Invalid path: {self.path}")
    #             return

    #         total_files = len(files)
    #         print(f"Found {total_files} files to process")

    #         # Process files in batches
    #         for i in range(0, total_files, BATCH_SIZE):
    #             batch_files = files[i:i + BATCH_SIZE]
    #             batch_start = i + 1
    #             batch_end = min(i + BATCH_SIZE, total_files)
    #             print(f"\nProcessing batch {batch_start}-{batch_end} of {total_files}")
                
    #             with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
    #                 batch_results = pool.map(
    #                     process_single_file,
    #                     [(f, j + 1, total_files, config, self.log_file) 
    #                         for j, f in enumerate(batch_files, start=i)]
    #                 )
                    
    #                 # Update statistics for this batch
    #                 for result in batch_results:
    #                     if result is not None:
    #                         self.total_results[result] += 1

    #                 # Print intermediate summary
    #                 print(f"Batch {batch_start}-{batch_end} completed:")
    #                 print(f"- Successfully uploaded: {sum(1 for r in batch_results if r == UploadResult.CORRECTLY)}")
    #                 print(f"- Skipped (exists): {sum(1 for r in batch_results if r == UploadResult.SKIPPED)}")
    #                 print(f"- Failed: {sum(1 for r in batch_results if r == UploadResult.FAILED)}")

    #         # Generate final summary
    #         end_time = time.time()
    #         elapsed_time = end_time - start_time
    #         elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

    #         summary = (
    #             f"\n\nUpload finished for {self.path}"
    #             f"\nTime required: {elapsed_time_pretty}"
    #             f"\nResults:"
    #             f"\n- Total processed: {sum(self.total_results.values())}"
    #             f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
    #             f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
    #             f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
    #         )

    #         with open(self.log_file, "a") as f:
    #             f.write(summary)

    #         print(summary)

    #     except Exception as e:
    #         print(f"[ERR] Error in upload process: {str(e)}")

    def upload(self):
        """Main upload function with multiprocessing support"""
        start_time = time.time()
        BATCH_SIZE = 1000  # Process files in batches of 1000

        config = {
            "s3_endpoint": os.getenv("S3_ENDPOINT"),
            "s3_access_key": os.getenv("S3_ACCESS_KEY"),
            "s3_secret_key": os.getenv("S3_SECRET_KEY"),
            "s3_bucket": os.getenv("S3_BUCKET"),
            "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
            "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
            "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
            "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
            "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
            "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
            "repository": self.repository,
            "notes": self.notes
        }

        try:
            # Get all existing filenames for this repository
            query = f"SELECT filename FROM samples_catalog WHERE repository = '{self.repository}'"
            # existing_files = set()
            # for row in self.ch_client.query(query).result_rows:
            #     filename = os.path.splitext(row[0])[0]  # Strip extension
            #     existing_files.add(filename)
            existing_files = set(row[0] for row in self.ch_client.query(query).result_rows)
            print(f"Found {len(existing_files)} existing files in repository")

            # Get list of files to process
            if os.path.isfile(self.path):
                files = [self.path] if not self.path.endswith('.txt') else [
                    line.strip() for line in open(self.path) 
                    if line.strip() and not os.path.basename(line.strip()).startswith('.')
                ]
            elif os.path.isdir(self.path):
                files = [
                    os.path.join(root, file)
                    for root, _, files in os.walk(self.path)
                    for file in files
                    if not file.startswith('.')
                ]
            else:
                print(f"[ERR] Invalid path: {self.path}")
                return

            # Filter out files that are already in the repository
            files_to_process = []
            for f in files:
                basename = os.path.splitext(os.path.basename(f))[0]
                if basename not in existing_files:
                    files_to_process.append(f)

            total_files = len(files_to_process)
            skipped_files = len(files) - total_files
            print(f"Found {total_files} new files to process (skipped {skipped_files} existing files)")

            if total_files == 0:
                print("No new files to process")
                return

            # Process files in batches
            for i in range(0, total_files, BATCH_SIZE):
                batch_files = files_to_process[i:i + BATCH_SIZE]
                batch_start = i + 1
                batch_end = min(i + BATCH_SIZE, total_files)
                print(f"\nProcessing batch {batch_start}-{batch_end} of {total_files}")
                
                with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
                    batch_results = pool.map(
                        process_single_file,
                        [(f, j + 1, total_files, config, self.log_file) 
                        for j, f in enumerate(batch_files, start=i)]
                    )
                    
                    # Update statistics for this batch
                    for result in batch_results:
                        if result is not None:
                            self.total_results[result] += 1

                    # Print intermediate summary
                    print(f"Batch {batch_start}-{batch_end} completed:")
                    print(f"- Successfully uploaded: {sum(1 for r in batch_results if r == UploadResult.CORRECTLY)}")
                    print(f"- Skipped (exists): {sum(1 for r in batch_results if r == UploadResult.SKIPPED)}")
                    print(f"- Failed: {sum(1 for r in batch_results if r == UploadResult.FAILED)}")

            # Generate final summary
            end_time = time.time()
            elapsed_time = end_time - start_time
            elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

            summary = (
                f"\n\nUpload finished for {self.path}"
                f"\nTime required: {elapsed_time_pretty}"
                f"\nResults:"
                f"\n- Files already in repository: {skipped_files}"
                f"\n- New files processed: {total_files}"
                f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
                f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
                f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
            )

            with open(self.log_file, "a") as f:
                f.write(summary)

            print(summary)

        except Exception as e:
            print(f"[ERR] Error in upload process: {str(e)}")
            import traceback
            print(traceback.format_exc())

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='Upload malware samples to S3 and catalog them.')
    parser.add_argument('path', help='Path to file or directory to process')
    parser.add_argument('repository', help='Repository name')
    parser.add_argument('--notes', help='Optional notes to add to all files', default=None)
    
    args = parser.parse_args()
    
    uploader = S3Uploader(args.path, args.repository, args.notes)
    uploader.upload()