# Copyright (c) Alibaba, Inc. and its affiliates.
# yapf: disable

import datetime
import unittest
from unittest.mock import Mock, patch

from modelscope.hub.api import HubApi
from modelscope.hub.info import BlobLfsInfo, DatasetInfo, ModelInfo
from modelscope.utils.constant import (DEFAULT_DATASET_REVISION,
                                       REPO_TYPE_DATASET, REPO_TYPE_MODEL)
from modelscope.utils.repo_utils import DetailedCommitInfo
from modelscope.utils.test_utils import test_level


class HubRepoInfoTest(unittest.TestCase):

    def setUp(self):
        self.api = HubApi()

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    @patch.object(HubApi, 'get_model_files')
    @patch.object(HubApi, 'get_model')
    @patch.object(HubApi, 'list_repo_commits')
    def test_model_info(self, mock_list_repo_commits, mock_get_model, mock_get_model_files):
        # Setup mock responses
        mock_get_model.return_value = {
            'Id': 123,
            'Name': 'demo-model',
            'ChineseName': '测试模型',
            'Description': 'A test model',
            'Tasks': [{
                'Name': 'text-classification',
                'Description': 'A test task'
            }],
            'Tags': ['nlp', 'text']
        }
        mock_get_model_files.return_value = [
            {
                'Path': 'file1.txt',
                'Size': 100,
                'Sha256': '1234567890',
                'Type': 'blob'
            }
        ]

        # Mock commit history response
        commit = DetailedCommitInfo(
            id='abc123',
            short_id='abc12',
            title='Initial commit',
            message='Initial commit',
            author_name='Test User',
            authored_date=None,
            author_email='test@example.com',
            committed_date=None,
            committer_name='Test User',
            committer_email='test@example.com',
            created_at=None)
        commits_response = Mock()
        commits_response.commits = [commit]
        mock_list_repo_commits.return_value = commits_response

        # Call the method
        info = self.api.model_info(
            repo_id='demo/model', revision='master', endpoint=None)

        # Verify results
        self.assertEqual(info.id, 123)
        self.assertEqual(info.name, 'demo-model')
        self.assertEqual(info.author, 'demo')
        self.assertEqual(info.chinese_name, '测试模型')
        self.assertEqual(info.description, 'A test model')
        self.assertEqual(info.tasks, [{
            'Name': 'text-classification',
            'Description': 'A test task'
        }])
        self.assertEqual(info.tags, ['nlp', 'text'])
        self.assertEqual(info.sha, 'abc123')
        self.assertEqual(info.last_commit, commit.to_dict())
        self.assertEqual(info.siblings[0].rfilename, 'file1.txt')
        self.assertEqual(info.siblings[0].size, 100)
        self.assertEqual(info.siblings[0].blob_id, '1234567890')
        self.assertEqual(info.siblings[0].type, 'blob')

        # Verify correct method calls
        mock_get_model.assert_called_once_with(
            model_id='demo/model', revision='master', endpoint=None)
        mock_list_repo_commits.assert_called_once_with(
            repo_id='demo/model',
            repo_type=REPO_TYPE_MODEL,
            revision='master',
            endpoint=None)
        mock_get_model_files.assert_called_once_with(
            model_id='demo/model', revision='master', endpoint=None, recursive=True)

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    @patch.object(HubApi, 'get_dataset_files')
    @patch.object(HubApi, 'get_dataset')
    @patch.object(HubApi, 'list_repo_commits')
    def test_dataset_info(self, mock_list_repo_commits, mock_get_dataset, mock_get_dataset_files):
        # Setup mock responses
        mock_get_dataset.return_value = {
            'Id': 456,
            'Name': 'demo-dataset',
            'ChineseName': '演示数据集',
            'Description': 'A test dataset',
            'Tags': [
                {
                    'Name': 'nlp',
                    'Color': 'blue'
                },
                {
                    'Name': 'text',
                    'Color': 'green'
                }
            ]
        }
        mock_get_dataset_files.return_value = [
            {
                'Path': 'data1.json',
                'Size': 100,
                'Sha256': '1234567890',
                'Type': 'blob'
            }
        ]

        # Mock commit history response
        commits = [
            DetailedCommitInfo(
                id='c1',
                short_id='c1',
                title='Update data',
                message='Update data',
                author_name='Test User',
                authored_date=None,
                author_email='test@example.com',
                committed_date=None,
                committer_name='Test User',
                committer_email='test@example.com',
                created_at=None),
            DetailedCommitInfo(
                id='c2',
                short_id='c2',
                title='Initial commit',
                message='Initial commit',
                author_name='Test User',
                authored_date=None,
                author_email='test@example.com',
                committed_date=1756284063,
                committer_name='Test User',
                committer_email='test@example.com',
                created_at=None)
        ]
        commits_response = Mock()
        commits_response.commits = commits
        mock_list_repo_commits.return_value = commits_response

        # Call the method
        info = self.api.dataset_info('demo/dataset')

        # Verify results
        self.assertEqual(info.id, 456)
        self.assertEqual(info.name, 'demo-dataset')
        self.assertEqual(info.author, 'demo')
        self.assertEqual(info.chinese_name, '演示数据集')
        self.assertEqual(info.description, 'A test dataset')
        self.assertEqual(info.tags, [{
            'Name': 'nlp',
            'Color': 'blue'
        }, {
            'Name': 'text',
            'Color': 'green'
        }])
        self.assertEqual(info.sha, 'c1')
        self.assertEqual(info.last_commit, commits[0].to_dict())
        self.assertEqual(info.siblings[0].rfilename, 'data1.json')
        self.assertEqual(info.siblings[0].size, 100)
        self.assertEqual(info.siblings[0].blob_id, '1234567890')
        self.assertEqual(info.siblings[0].type, 'blob')

        # Verify correct method calls
        mock_get_dataset.assert_called_once_with(
            dataset_id='demo/dataset', revision=None, endpoint=None)
        mock_list_repo_commits.assert_called_once_with(
            repo_id='demo/dataset',
            repo_type=REPO_TYPE_DATASET,
            revision=None,
            endpoint=None)
        mock_get_dataset_files.assert_called_once_with(
            repo_id='demo/dataset', revision=DEFAULT_DATASET_REVISION, endpoint=None, recursive=True)

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    @patch.object(HubApi, 'model_info')
    @patch.object(HubApi, 'dataset_info')
    def test_repo_info_model(self, mock_dataset_info, mock_model_info):
        # Setup mock response
        model_info = ModelInfo(
            id=123, name='demo-model', description='A test model')
        mock_model_info.return_value = model_info

        # Call the method with model type
        info = self.api.repo_info(
            repo_id='demo/model', revision='master', endpoint=None)

        # Verify results
        self.assertEqual(info, model_info)
        mock_model_info.assert_called_once_with(
            repo_id='demo/model', revision='master', endpoint=None)
        mock_dataset_info.assert_not_called()

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    @patch.object(HubApi, 'model_info')
    @patch.object(HubApi, 'dataset_info')
    def test_repo_info_dataset(self, mock_dataset_info, mock_model_info):
        # Setup mock response
        dataset_info = DatasetInfo(
            id=456, name='demo-dataset', description='A test dataset')
        mock_dataset_info.return_value = dataset_info

        # Call the method with dataset type
        info = self.api.repo_info(
            repo_id='demo/dataset',
            repo_type=REPO_TYPE_DATASET,
            revision='master',
            endpoint=None)

        # Verify results
        self.assertEqual(info, dataset_info)
        mock_dataset_info.assert_called_once_with(
            repo_id='demo/dataset', revision='master', endpoint=None)
        mock_model_info.assert_not_called()

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    def test_model_info_class_comprehensive(self):
        """Test ModelInfo class initialization and properties."""
        import zoneinfo
        model_data = {
            'Id': 123,
            'Name': 'demo-model',
            'author': 'demo',
            'ChineseName': '演示模型',
            'Description': 'A test model',
            'Tasks': [{
                'Name': 'text-classification',
                'Description': 'A test task'
            }],
            'Tags': ['nlp', 'text'],
            'CreatedTime': '2023-01-01T00:00:00Z',
            'Visibility': 5,
            'IsPublished': 1,
            'IsOnline': 1,
            'License': 'Apache-2.0',
            'Downloads': 100,
            'Stars': 50,
            'Architectures': ['transformer'],
            'ModelType': ['nlp'],
            'Siblings': [
                {
                    'Path': 'model-1.bin',
                    'Size': 100,
                    'Sha256': '1234567890',
                    'Type': 'blob'
                },
                {
                    'Path': 'model-2.bin',
                    'Size': 200,
                    'Sha256': '1234567891',
                    'Type': 'blob'
                }
            ]
        }

        # Create mock commits
        commit = DetailedCommitInfo(
            id='abc123',
            short_id='abc12',
            title='Initial commit',
            message='Initial commit',
            author_name='Test User',
            authored_date=None,
            author_email='test@example.com',
            committed_date=None,
            committer_name='Test User',
            committer_email='test@example.com',
            created_at=None)
        commits = Mock()
        commits.commits = [commit]

        # Create ModelInfo instance
        model_info = ModelInfo(**model_data, commits=commits)

        # Verify properties
        self.assertEqual(model_info.id, 123)
        self.assertEqual(model_info.name, 'demo-model')
        self.assertEqual(model_info.author, 'demo')
        self.assertEqual(model_info.chinese_name, '演示模型')
        self.assertEqual(model_info.description, 'A test model')
        self.assertEqual(model_info.tasks, [{
            'Name': 'text-classification',
            'Description': 'A test task'
        }])
        self.assertEqual(model_info.tags, ['nlp', 'text'])
        self.assertEqual(
            model_info.created_at,
            datetime.datetime(
                2023, 1, 1, 8, 0, 0).replace(tzinfo=zoneinfo.ZoneInfo('Asia/Shanghai')))
        self.assertEqual(model_info.sha, 'abc123')
        self.assertEqual(model_info.last_commit, commit.to_dict())
        self.assertEqual(model_info.visibility, 5)
        self.assertEqual(model_info.is_published, 1)
        self.assertEqual(model_info.is_online, 1)
        self.assertEqual(model_info.license, 'Apache-2.0')
        self.assertEqual(model_info.downloads, 100)
        self.assertEqual(model_info.likes, 50)
        self.assertEqual(model_info.architectures, ['transformer'])
        self.assertEqual(model_info.model_type, ['nlp'])
        self.assertEqual(model_info.siblings[0].__dict__, {
            'rfilename': 'model-1.bin',
            'size': 100,
            'blob_id': '1234567890',
            'type': 'blob',
            'sha': None,
            'last_modified': None,
            'lfs': BlobLfsInfo(
                size=100,
                sha256='1234567890'
            )
        })
        self.assertEqual(model_info.siblings[1].__dict__, {
            'rfilename': 'model-2.bin',
            'size': 200,
            'blob_id': '1234567891',
            'type': 'blob',
            'sha': None,
            'last_modified': None,
            'lfs': BlobLfsInfo(
                size=200,
                sha256='1234567891'
            )
        })

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    def test_dataset_info_class(self):
        # Test DatasetInfo class initialization and properties
        import zoneinfo
        dataset_data = {
            'Id': 456,
            'Name': 'demo-dataset',
            'Owner': 'demo',
            'ChineseName': '演示数据集',
            'Description': 'A test dataset',
            'Tags': [
                {
                    'Name': 'nlp',
                    'Color': 'blue'
                },
                {
                    'Name': 'text',
                    'Color': 'green'
                }
            ],
            'GmtCreate': 1755752511,
            'Visibility': 5,
            'License': 'MIT',
            'Downloads': 200,
            'Likes': 75
        }
        siblings = [
            {
                'Path': 'data-1.json',
                'Size': 100,
                'Sha256': '1234567890',
                'Type': 'blob'
            }
        ]

        # Create mock commits
        commit = DetailedCommitInfo(
            id='c1',
            short_id='c1',
            title='Initial commit',
            message='Initial commit',
            author_name='Test User',
            authored_date=None,
            author_email='test@example.com',
            committed_date='2024-09-18T06:20:05Z',
            committer_name='Test User',
            committer_email='test@example.com',
            created_at=None)
        commits = Mock()
        commits.commits = [commit]

        # Create DatasetInfo instance
        dataset_info = DatasetInfo(**dataset_data, commits=commits, siblings=siblings)

        # Verify properties
        self.assertEqual(dataset_info.id, 456)
        self.assertEqual(dataset_info.name, 'demo-dataset')
        self.assertEqual(dataset_info.author, 'demo')
        self.assertEqual(dataset_info.chinese_name, '演示数据集')
        self.assertEqual(dataset_info.description, 'A test dataset')
        self.assertEqual(
            dataset_info.tags,
            [
                {
                    'Name': 'nlp',
                    'Color': 'blue'
                },
                {
                    'Name': 'text',
                    'Color': 'green'
                }
            ]
        )
        self.assertEqual(
            dataset_info.created_at,
            datetime.datetime(
                2025, 8, 21, 13, 1, 51).replace(tzinfo=zoneinfo.ZoneInfo('Asia/Shanghai')))
        self.assertEqual(dataset_info.sha, 'c1')
        self.assertEqual(
            dataset_info.last_modified,
            datetime.datetime(
                2024, 9, 18, 14, 20, 5).replace(tzinfo=zoneinfo.ZoneInfo('Asia/Shanghai')))
        self.assertEqual(dataset_info.last_commit, commit.to_dict())
        self.assertEqual(dataset_info.visibility, 5)
        self.assertEqual(dataset_info.license, 'MIT')
        self.assertEqual(dataset_info.downloads, 200)
        self.assertEqual(dataset_info.likes, 75)
        self.assertEqual(dataset_info.siblings[0].__dict__, {
            'rfilename': 'data-1.json',
            'size': 100,
            'blob_id': '1234567890',
            'type': 'blob',
            'sha': None,
            'last_modified': None,
            'lfs': BlobLfsInfo(
                size=100,
                sha256='1234567890'
            )
        })

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    def test_model_info_empty_commits(self):
        """Test ModelInfo with empty commits."""
        model_data = {'Id': 123, 'Name': 'demo-model', 'author': 'demo'}

        # Create ModelInfo with no commits
        model_info = ModelInfo(**model_data, commits=None)

        # Verify commit-related fields are None
        self.assertIsNone(model_info.sha)
        self.assertIsNone(model_info.last_commit)
        self.assertIsNone(model_info.last_modified)

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    def test_dataset_info_empty_commits(self):
        """Test DatasetInfo with empty commits."""
        dataset_data = {'Id': 456, 'Name': 'demo-dataset', 'Owner': 'demo'}

        # Create DatasetInfo with no commits
        dataset_info = DatasetInfo(**dataset_data, commits=None)

        # Verify commit-related fields are None
        self.assertIsNone(dataset_info.sha)
        self.assertIsNone(dataset_info.last_commit)
        self.assertIsNone(dataset_info.last_modified)

    @unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
    def test_detailed_commit_info_to_dict(self):
        """Test DetailedCommitInfo to_dict method."""
        commit = DetailedCommitInfo(
            id='abc123',
            short_id='abc12',
            title='Test commit',
            message='Test commit message',
            author_name='Test Author',
            authored_date=datetime.datetime(2023, 1, 1, 0, 0, 0),
            author_email='test@example.com',
            committed_date=datetime.datetime(2023, 1, 1, 0, 0, 0),
            committer_name='Test Committer',
            committer_email='committer@example.com',
            created_at=datetime.datetime(2023, 1, 1, 0, 0, 0))

        result = commit.to_dict()

        expected = {
            'id': 'abc123',
            'short_id': 'abc12',
            'title': 'Test commit',
            'message': 'Test commit message',
            'author_name': 'Test Author',
            'authored_date': datetime.datetime(2023, 1, 1, 0, 0, 0),
            'author_email': 'test@example.com',
            'committed_date': datetime.datetime(2023, 1, 1, 0, 0, 0),
            'committer_name': 'Test Committer',
            'committer_email': 'committer@example.com',
            'created_at': datetime.datetime(2023, 1, 1, 0, 0, 0)
        }

        self.assertEqual(result, expected)

    @unittest.skipUnless(test_level() >= 1, 'skip test in current test level')
    def test_real_model_repo_info(self):
        """Test getting real model repository information without mocks."""
        # Use a real model repository
        model_repo_id = 'black-forest-labs/FLUX.1-Krea-dev'

        # Get repository information
        info = self.api.repo_info(
            repo_id=model_repo_id, repo_type=REPO_TYPE_MODEL)

        # Basic validation
        self.assertIsNotNone(info)
        self.assertEqual(info.author, 'black-forest-labs')
        self.assertEqual(info.name, 'FLUX.1-Krea-dev')

        # Check commit information
        self.assertIsNotNone(info.sha)
        if hasattr(info, 'last_commit') and info.last_commit:
            self.assertIn('id', info.last_commit)
            self.assertIn('title', info.last_commit)

        # Print some information for debugging
        print(f'\nModel Info for {model_repo_id}:')
        print(f'ID: {info.id}')
        print(f'Name: {info.name}')
        print(f'Author: {info.author}')
        print(f'SHA: {info.sha}')
        if hasattr(info, 'last_modified'):
            print(f'Last Modified: {info.last_modified}')
        if hasattr(info, 'siblings'):
            print(f'Siblings[0]: {info.siblings[0]}')

    @unittest.skipUnless(test_level() >= 1, 'skip test in current test level')
    def test_real_dataset_repo_info(self):
        """Test getting real dataset repository information without mocks."""
        # Use a real dataset repository
        dataset_repo_id = 'swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT'

        # Get repository information
        info = self.api.repo_info(
            repo_id=dataset_repo_id, repo_type=REPO_TYPE_DATASET)

        # Basic validation
        self.assertIsNotNone(info)
        self.assertEqual(info.author, 'swift')
        self.assertTrue('Chinese-Qwen3' in info.name)

        # Check commit information
        self.assertIsNotNone(info.sha)
        if hasattr(info, 'last_commit') and info.last_commit:
            self.assertIn('id', info.last_commit)
            self.assertIn('title', info.last_commit)

        # Print some information for debugging
        print(f'\nDataset Info for {dataset_repo_id}:')
        print(f'ID: {info.id}')
        print(f'Name: {info.name}')
        print(f'Author: {info.author}')
        print(f'SHA: {info.sha}')
        if hasattr(info, 'last_modified'):
            print(f'Last Modified: {info.last_modified}')
        if hasattr(info, 'siblings'):
            print(f'Siblings[0]: {info.siblings[0]}')


if __name__ == '__main__':
    unittest.main()
