# Copyright (c) ModelScope Contributors. All rights reserved.
import torch
from contextlib import contextmanager
from megatron.core.dist_checkpointing.mapping import ShardedTensorFactory
from megatron.core.dist_checkpointing.strategies.torch import TorchDistSaveShardedStrategy

from swift.utils import get_logger

logger = get_logger()


def patch_torch_dist_shard(thread_count):
    __init__ = TorchDistSaveShardedStrategy.__init__

    def __new_init__(*args, **kwargs):
        kwargs['thread_count'] = thread_count
        return __init__(*args, **kwargs)

    TorchDistSaveShardedStrategy.__init__ = __new_init__


def patch_merge_fn(state_dict_model):
    # https://github.com/NVIDIA/Megatron-LM/issues/1380

    def sh_ten_merge_fn(sub_state_dict):
        with torch.no_grad():
            shared_storage = sub_state_dict[0].untyped_storage()
            if all(shared_storage.data_ptr() == tensor.untyped_storage().data_ptr() for tensor in sub_state_dict):
                element_size = sub_state_dict[0].element_size()
                total_numel = sum(tensor.numel() for tensor in sub_state_dict)
                if shared_storage.nbytes() == total_numel * element_size:
                    dim_0 = sum(tensor.shape[0] for tensor in sub_state_dict)
                    shape = (dim_0, ) + sub_state_dict[0].shape[1:]
                    combined_tensor = torch.empty(
                        shape, dtype=sub_state_dict[0].dtype,
                        device=sub_state_dict[0].device).set_(shared_storage, 0, shape)
                    return combined_tensor
            return torch.cat(sub_state_dict)

    for v in state_dict_model.values():
        if isinstance(v, ShardedTensorFactory) and 'apply_swiglu_sharded_factory' in v.merge_fn.__qualname__:
            v.merge_fn = sh_ten_merge_fn
