"""Tests for certisigma_census.sharing — forensic share tokens."""

from __future__ import annotations

import pytest
from unittest.mock import MagicMock, patch

from certisigma_census.sharing import (
    parse_duration_seconds,
    _parse_attestation_id,
    create_share_token,
    list_share_tokens,
    revoke_share_token,
    get_share_token_info,
    ShareResult,
    ShareInfo,
)


class TestParseDuration:
    def test_minutes(self):
        assert parse_duration_seconds("30m") == 1800

    def test_hours(self):
        assert parse_duration_seconds("24h") == 86400

    def test_days(self):
        assert parse_duration_seconds("7d") == 604800

    def test_plural_forms(self):
        assert parse_duration_seconds("2hours") == 7200
        assert parse_duration_seconds("1day") == 86400
        assert parse_duration_seconds("5days") == 432000

    def test_case_insensitive(self):
        assert parse_duration_seconds("24H") == 86400
        assert parse_duration_seconds("7D") == 604800

    def test_whitespace_stripped(self):
        assert parse_duration_seconds("  24h  ") == 86400

    def test_invalid_format(self):
        with pytest.raises(ValueError, match="Invalid duration"):
            parse_duration_seconds("abc")

    def test_invalid_unit(self):
        with pytest.raises(ValueError, match="Invalid duration"):
            parse_duration_seconds("10x")

    def test_zero_duration(self):
        with pytest.raises(ValueError, match="positive"):
            parse_duration_seconds("0h")


class TestParseAttestationId:
    def test_numeric_string(self):
        assert _parse_attestation_id("42") == 42

    def test_att_prefix(self):
        assert _parse_attestation_id("att_123") == 123

    def test_whitespace_stripped(self):
        assert _parse_attestation_id("  att_7  ") == 7

    def test_invalid_raises(self):
        with pytest.raises(ValueError, match="Invalid attestation ID"):
            _parse_attestation_id("not_a_number")


class TestCreateShareToken:
    @patch("certisigma.CertiSigmaClient")
    def test_success(self, MockClient):
        mock_result = MagicMock()
        mock_result.id = "shr_123"
        mock_result.share_token = "tok_abc"
        mock_result.attestation_ids = [1, 2]
        mock_result.expires_at = "2026-04-01T00:00:00Z"
        MockClient.return_value.create_share_token.return_value = mock_result

        result = create_share_token(
            ["att_1", "att_2"],
            api_key="cs_test",
            expires_in="24h",
            recipient_label="Legal",
            max_uses=5,
        )
        assert result.success is True
        assert result.token_id == "shr_123"
        assert result.share_token == "tok_abc"
        assert len(result.attestation_ids) == 2
        call_args = MockClient.return_value.create_share_token.call_args
        assert call_args[1]["attestation_ids"] == [1, 2]

    def test_empty_attestation_ids(self):
        result = create_share_token([])
        assert result.success is False
        assert "No attestation" in result.error

    def test_invalid_duration(self):
        result = create_share_token(["att_1"], expires_in="garbage")
        assert result.success is False
        assert "Invalid duration" in result.error

    def test_invalid_attestation_id(self):
        result = create_share_token(["not_a_number"])
        assert result.success is False
        assert "Invalid attestation ID" in result.error

    @patch("certisigma.CertiSigmaClient")
    def test_api_error(self, MockClient):
        from certisigma import CertiSigmaError
        MockClient.return_value.create_share_token.side_effect = CertiSigmaError("fail")
        result = create_share_token(["42"], api_key="cs_test")
        assert result.success is False
        assert "fail" in result.error


class TestListShareTokens:
    @patch("certisigma.CertiSigmaClient")
    def test_returns_list(self, MockClient):
        t = MagicMock()
        t.id = "shr_1"
        t.attestation_ids = ["att_1"]
        t.recipient_label = "Legal"
        t.max_uses = 5
        t.use_count = 2
        t.expires_at = "2026-04-01"
        t.revoked_at = None
        t.created_at = "2026-03-01"
        t.active = True
        MockClient.return_value.list_share_tokens.return_value = [t]

        tokens = list_share_tokens(api_key="cs_test")
        assert len(tokens) == 1
        assert tokens[0].token_id == "shr_1"
        assert tokens[0].active is True

    @patch("certisigma.CertiSigmaClient")
    def test_empty_list(self, MockClient):
        MockClient.return_value.list_share_tokens.return_value = []
        tokens = list_share_tokens(api_key="cs_test")
        assert tokens == []

    @patch("certisigma.CertiSigmaClient")
    def test_api_error_returns_empty(self, MockClient):
        from certisigma import CertiSigmaError
        MockClient.return_value.list_share_tokens.side_effect = CertiSigmaError("fail")
        tokens = list_share_tokens(api_key="cs_test")
        assert tokens == []


class TestRevokeShareToken:
    @patch("certisigma.CertiSigmaClient")
    def test_success(self, MockClient):
        MockClient.return_value.revoke_share_token.return_value = None
        result = revoke_share_token("shr_1", api_key="cs_test")
        assert result.success is True
        assert result.token_id == "shr_1"

    @patch("certisigma.CertiSigmaClient")
    def test_error(self, MockClient):
        from certisigma import CertiSigmaError
        MockClient.return_value.revoke_share_token.side_effect = CertiSigmaError("not found")
        result = revoke_share_token("shr_bad", api_key="cs_test")
        assert result.success is False


class TestGetShareTokenInfo:
    @patch("certisigma.CertiSigmaClient")
    def test_returns_info(self, MockClient):
        t = MagicMock()
        t.id = "shr_1"
        t.attestation_ids = ["att_1"]
        t.recipient_label = "Legal"
        t.max_uses = None
        t.use_count = 0
        t.expires_at = "2026-04-01"
        t.revoked_at = None
        t.created_at = "2026-03-01"
        t.active = True
        MockClient.return_value.get_share_token_info.return_value = t

        info = get_share_token_info("shr_1", api_key="cs_test")
        assert isinstance(info, ShareInfo)
        assert info.active is True

    @patch("certisigma.CertiSigmaClient")
    def test_error_returns_share_result(self, MockClient):
        from certisigma import CertiSigmaError
        MockClient.return_value.get_share_token_info.side_effect = CertiSigmaError("gone")
        result = get_share_token_info("shr_bad", api_key="cs_test")
        assert isinstance(result, ShareResult)
        assert result.success is False
