#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
#  This file is part of the AgentSDK project.
# Copyright (c) 2026 Huawei Technologies Co.,Ltd.
#
# AgentSDK is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#           http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# -------------------------------------------------------------------------


from datetime import datetime

import pytest
from pydantic import ValidationError

from skillhub.models.skill import (
    SkillManifest,
    InstalledSkill,
    DiscoveredSkill,
    ResolvedSkill,
    InstallResult,
)
from skillhub.models.source import Source, SourceType
from skillhub.models.cache import CacheOptions, CacheStats
from skillhub.models.security import InstallEvent, SandboxOptions, SandboxResult
from skillhub.models.credential import TokenInfo, TokenValidation
from skillhub.models.repository import Repository, Release, Tag, ContentItem, RateLimit


class TestSkillManifest:
    """Tests for SkillManifest model."""

    def test_manifest_creation_valid(self, sample_manifest: SkillManifest):
        """Test creating a valid manifest."""
        assert sample_manifest.name == "test-skill"
        assert sample_manifest.description == "A test skill for testing"
        assert sample_manifest.version == "1.0.0"
        assert sample_manifest.author == "test-author"
        assert sample_manifest.tags == ["test", "demo"]
        assert sample_manifest.license == "MIT"

    def test_manifest_name_validation_empty(self):
        """Test that empty name raises ValidationError."""
        with pytest.raises(ValidationError):
            SkillManifest(name="", description="Test")

    def test_manifest_name_validation_short(self):
        """Test that short name raises ValidationError."""
        with pytest.raises(ValidationError):
            SkillManifest(name="x", description="Test")

    def test_manifest_name_normalization(self):
        """Test that name is normalized to lowercase and hyphens."""
        manifest = SkillManifest(name="Test Skill Name", description="Test")
        assert manifest.name == "test-skill-name"

    def test_manifest_minimal_fields(self):
        """Test creating manifest with minimal required fields."""
        manifest = SkillManifest(name="minimal", description="Minimal skill")
        assert manifest.name == "minimal"
        assert manifest.description == "Minimal skill"
        assert manifest.version is None
        assert manifest.tags == []

    def test_manifest_to_dict(self, sample_manifest: SkillManifest):
        """Test converting manifest to dictionary."""
        data = sample_manifest.model_dump()
        assert isinstance(data, dict)
        assert data["name"] == "test-skill"

    def test_manifest_from_dict(self):
        """Test creating manifest from dictionary."""
        data = {
            "name": "from-dict",
            "description": "From dict",
            "version": "0.1.0",
        }
        manifest = SkillManifest(**data)
        assert manifest.name == "from-dict"

    def test_manifest_equality(self, sample_manifest: SkillManifest):
        """Test manifest equality comparison."""
        manifest2 = SkillManifest(
            name="test-skill",
            description="A test skill for testing",
            version="1.0.0",
        )
        assert sample_manifest.name == manifest2.name


class TestInstalledSkill:
    """Tests for InstalledSkill model."""

    def test_installed_skill_creation(self, sample_installed_skill: InstalledSkill):
        """Test creating an installed skill record."""
        assert sample_installed_skill.name == "test-skill"
        assert sample_installed_skill.version == "1.0.0"
        assert sample_installed_skill.source_id == "github"
        assert sample_installed_skill.checksum == "abc123def456"

    def test_installed_skill_timestamps(self, sample_installed_skill: InstalledSkill):
        """Test that timestamps are set correctly."""
        assert sample_installed_skill.installed_at is not None
        assert sample_installed_skill.updated_at is not None

    def test_installed_skill_config_default(self):
        """Test that config defaults to empty dict."""
        installed = InstalledSkill(
            name="skill",
            version="1.0",
            source_id="test",
            source_type="github",
            repository="test/repo",
            ref="v1.0",
            install_path="/path",
            checksum="abc",
        )
        assert installed.config == {}

    def test_installed_skill_model_dump(self, sample_installed_skill: InstalledSkill):
        """Test converting installed skill to dict."""
        data = sample_installed_skill.model_dump()
        assert data["name"] == "test-skill"


class TestDiscoveredSkill:
    """Tests for DiscoveredSkill model."""

    def test_discovered_skill_creation(self, sample_discovered_skill: DiscoveredSkill):
        """Test creating a discovered skill."""
        assert sample_discovered_skill.name == "demo-skill"
        assert sample_discovered_skill.version == "latest"
        assert sample_discovered_skill.tags == ["demo"]

    def test_discovered_skill_source_dict(self, sample_discovered_skill: DiscoveredSkill):
        """Test that source is a dict."""
        assert isinstance(sample_discovered_skill.source, dict)
        assert sample_discovered_skill.source["id"] == "github"

    def test_discovered_skill_repository_dict(self, sample_discovered_skill: DiscoveredSkill):
        """Test that repository is a dict."""
        assert isinstance(sample_discovered_skill.repository, dict)
        assert sample_discovered_skill.repository["owner"] == "demo"


class TestResolvedSkill:
    """Tests for ResolvedSkill model."""

    def test_resolved_skill_creation(self, sample_resolved_skill: ResolvedSkill):
        """Test creating a resolved skill."""
        assert sample_resolved_skill.name == "test-skill"
        assert sample_resolved_skill.ref == "v1.0.0"
        assert sample_resolved_skill.manifest is not None

    def test_resolved_skill_subpath_optional(self, sample_resolved_skill: ResolvedSkill):
        """Test that subpath is optional."""
        assert sample_resolved_skill.subpath is None

    def test_resolved_skill_with_subpath(self, sample_manifest: SkillManifest):
        """Test resolved skill with subpath."""
        resolved = ResolvedSkill(
            name="skill",
            version="1.0",
            repository="test/repo",
            ref="v1.0",
            manifest=sample_manifest,
            source={"id": "test"},
            download_url="https://example.com/download",
            subpath="skills/skill",
        )
        assert resolved.subpath == "skills/skill"


class TestInstallResult:
    """Tests for InstallResult model."""

    def test_install_result_success(self):
        """Test creating successful install result."""
        installed = InstalledSkill(
            name="skill",
            version="1.0",
            source_id="test",
            source_type="github",
            repository="test/repo",
            ref="v1.0",
            install_path="/path",
            checksum="abc",
        )
        result = InstallResult(
            success=True,
            skill=installed,
            installed_dependencies=["dep1"],
            warnings=["warn1"],
            errors=[],
            duration=1.5,
        )
        assert result.success is True
        assert len(result.installed_dependencies) == 1


class TestSource:
    """Tests for Source model."""

    def test_source_creation(self, sample_source: Source):
        """Test creating a source."""
        assert sample_source.id == "test-source"
        assert sample_source.name == "Test Source"
        assert sample_source.type == SourceType.GITHUB

    def test_source_default_id(self):
        """Test that source id is auto-generated."""
        source = Source(
            name="Auto ID",
            type=SourceType.GITHUB,
            url="https://github.com/test",
        )
        assert source.id is not None
        assert len(source.id) == 8

    def test_source_default_priority(self):
        """Test that priority defaults to 0."""
        source = Source(
            name="Default Priority",
            type=SourceType.GITHUB,
            url="https://github.com/test",
        )
        assert source.priority == 0

    def test_source_enabled_default(self):
        """Test that enabled defaults to True."""
        source = Source(
            name="Default Enabled",
            type=SourceType.GITHUB,
            url="https://github.com/test",
        )
        assert source.enabled is True

    def test_source_types(self):
        """Test all source types."""
        assert SourceType.GITHUB == "github"
        assert SourceType.GITEE == "gitee"
        assert SourceType.GITCODE == "gitcode"
        assert SourceType.MANIFEST == "manifest"

    def test_source_model_dump(self, sample_source: Source):
        """Test converting source to dict."""
        data = sample_source.model_dump()
        assert data["type"] == "github"


class TestCacheOptions:
    """Tests for CacheOptions model."""

    def test_cache_options_creation(self, sample_cache_options: CacheOptions):
        """Test creating cache options."""
        assert sample_cache_options.ttl == 3600
        assert sample_cache_options.tags == ["test"]

    def test_cache_options_defaults(self):
        """Test cache options defaults."""
        options = CacheOptions()
        assert options.ttl is None
        assert options.tags == []


class TestCacheStats:
    """Tests for CacheStats model."""

    def test_cache_stats_creation(self):
        """Test creating cache stats."""
        stats = CacheStats(
            size=100,
            hit_rate=0.8,
            miss_rate=0.2,
            total_size=1024,
            oldest_entry=None,
            newest_entry=None,
        )
        assert stats.size == 100
        assert stats.hit_rate == 0.8


class TestInstallEvent:
    """Tests for InstallEvent model."""

    def test_install_event_creation(self, sample_install_event: InstallEvent):
        """Test creating install event."""
        assert sample_install_event.skill == "test-skill"
        assert sample_install_event.success is True

    def test_install_event_success_values(self):
        """Test install event with different success values."""
        for success in [True, False]:
            event = InstallEvent(
                timestamp=datetime.utcnow(),
                skill="skill",
                version="1.0",
                source="github",
                repository="test/repo",
                ref="v1.0",
                checksum="abc",
                success=success,
            )
            assert event.success == success


class TestSandboxOptions:
    """Tests for SandboxOptions model."""

    def test_sandbox_options_creation(self, sample_sandbox_options: SandboxOptions):
        """Test creating sandbox options."""
        assert sample_sandbox_options.timeout == 300
        assert sample_sandbox_options.network is False

    def test_sandbox_options_defaults(self):
        """Test sandbox options defaults."""
        options = SandboxOptions()
        assert options.timeout == 300
        assert options.network is False


class TestSandboxResult:
    """Tests for SandboxResult model."""

    def test_sandbox_result_success(self):
        """Test successful sandbox result."""
        result = SandboxResult(
            exit_code=0,
            stdout="output",
            stderr="",
            duration=1.0,
        )
        assert result.exit_code == 0

    def test_sandbox_result_failure(self):
        """Test failed sandbox result."""
        result = SandboxResult(
            exit_code=1,
            stdout="",
            stderr="error",
            duration=1.0,
        )
        assert result.exit_code == 1
        assert result.stderr == "error"


class TestTokenInfo:
    """Tests for TokenInfo model."""

    def test_token_info_creation(self, sample_token_info: TokenInfo):
        """Test creating token info."""
        assert sample_token_info.platform == "github"
        assert sample_token_info.has_token is True

    def test_token_info_no_token(self):
        """Test token info with no token."""
        info = TokenInfo(
            platform="gitee",
            type="pat",
            has_token=False,
        )
        assert info.has_token is False


class TestTokenValidation:
    """Tests for TokenValidation model."""

    def test_token_validation_valid(self):
        """Test valid token validation."""
        validation = TokenValidation(
            valid=True,
            scopes=["repo"],
            rate_limit=None,
            message="Token is valid",
        )
        assert validation.valid is True

    def test_token_validation_invalid(self):
        """Test invalid token validation."""
        validation = TokenValidation(
            valid=False,
            scopes=[],
            rate_limit=None,
            message="Invalid token",
        )
        assert validation.valid is False


class TestRepository:
    """Tests for Repository model."""

    def test_repository_creation(self, sample_repository: Repository):
        """Test creating repository."""
        assert sample_repository.name == "test-repo"
        assert sample_repository.full_name == "test/test-repo"
        assert sample_repository.is_private is False


class TestRelease:
    """Tests for Release model."""

    def test_release_creation(self, sample_release: Release):
        """Test creating release."""
        assert sample_release.tag_name == "v1.0.0"
        assert sample_release.draft is False


class TestTag:
    """Tests for Tag model."""

    def test_tag_creation(self, sample_tag: Tag):
        """Test creating tag."""
        assert sample_tag.name == "v1.0.0"
        assert sample_tag.commit["sha"] == "abc123"


class TestRateLimit:
    """Tests for RateLimit model."""

    def test_rate_limit_creation(self, sample_rate_limit: RateLimit):
        """Test creating rate limit."""
        assert sample_rate_limit.limit == 5000
        assert sample_rate_limit.remaining == 4999
        assert sample_rate_limit.used == 1


class TestContentItem:
    """Tests for ContentItem model."""

    def test_content_item_file(self):
        """Test content item as file."""
        item = ContentItem(
            type="file",
            name="file.txt",
            path="path/file.txt",
            sha="abc123",
            size=100,
            url="https://example.com/file",
            content=None,
        )
        assert item.type == "file"

    def test_content_item_dir(self):
        """Test content item as directory."""
        item = ContentItem(
            type="dir",
            name="dir",
            path="path/dir",
            sha="abc123",
            size=0,
            url="https://example.com/dir",
        )
        assert item.type == "dir"