import os
import time
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch
import asyncio
import gzip
import sys
import uuid

import pytest

import base64
import os

sys.path.append(str(Path(__file__).resolve().parents[1]))

from seedpass.core.encryption import EncryptionManager
from seedpass.core.entry_management import EntryManager
from seedpass.core.backup import BackupManager
from seedpass.core.vault import Vault
from seedpass.core.config_manager import ConfigManager
from nostr.client import NostrClient, Kind, KindStandard


@pytest.mark.desktop
@pytest.mark.network
def test_nostr_index_size_limits(pytestconfig: pytest.Config):
    """Manually explore maximum index size for Nostr backups."""
    seed = (
        "abandon abandon abandon abandon abandon abandon abandon "
        "abandon abandon abandon abandon about"
    )
    results = []
    with TemporaryDirectory() as tmpdir:
        key = base64.urlsafe_b64encode(os.urandom(32))
        enc_mgr = EncryptionManager(key, Path(tmpdir))
        with patch.object(enc_mgr, "decrypt_parent_seed", return_value=seed):
            client = NostrClient(
                enc_mgr,
                f"size_test_{uuid.uuid4().hex}",
                relays=["wss://relay.snort.social"],
            )
            npub = client.key_manager.get_npub()
            vault = Vault(enc_mgr, tmpdir)
            cfg_mgr = ConfigManager(vault, Path(tmpdir))
            backup_mgr = BackupManager(Path(tmpdir), cfg_mgr)
            entry_mgr = EntryManager(vault, backup_mgr)

            delay = float(os.getenv("NOSTR_TEST_DELAY", "5"))
            max_entries = pytestconfig.getoption("--max-entries")
            size = 16
            batch = 100
            entry_count = 0
            max_payload = 60 * 1024
            try:
                while max_entries is None or entry_count < max_entries:
                    for _ in range(batch):
                        if max_entries is not None and entry_count >= max_entries:
                            break
                        entry_mgr.add_entry(
                            label=f"site-{entry_count + 1}",
                            length=12,
                            username="u" * size,
                            url="https://example.com/" + "a" * size,
                        )
                        entry_count += 1

                    encrypted = vault.get_encrypted_index()
                    payload_size = len(encrypted) if encrypted else 0
                    asyncio.run(client.publish_snapshot(encrypted or b""))
                    time.sleep(delay)
                    result = asyncio.run(client.fetch_latest_snapshot())
                    retrieved = gzip.decompress(b"".join(result[1])) if result else None
                    retrieved_ok = retrieved == encrypted
                    if not retrieved_ok:
                        print(f"Initial retrieve failed: {client.last_error}")
                        result = asyncio.run(client.fetch_latest_snapshot())
                        retrieved = (
                            gzip.decompress(b"".join(result[1])) if result else None
                        )
                        retrieved_ok = retrieved == encrypted
                    if not retrieved_ok:
                        print("Trying alternate relay")
                        client.update_relays(["wss://relay.damus.io"])
                        result = asyncio.run(client.fetch_latest_snapshot())
                        retrieved = (
                            gzip.decompress(b"".join(result[1])) if result else None
                        )
                        retrieved_ok = retrieved == encrypted
                    results.append((entry_count, payload_size, True, retrieved_ok))
                    if max_entries is not None:
                        if entry_count >= max_entries:
                            break
                    else:
                        if not retrieved_ok or payload_size > max_payload:
                            break

                    size *= 2
            except Exception:
                results.append((entry_count + 1, None, False, False))
            finally:
                client.close_client_pool()

    note_kind = Kind.from_std(KindStandard.TEXT_NOTE).as_u16()
    print(f"\nNostr note Kind: {note_kind}")
    print(f"Nostr account npub: {npub}")
    print("Count | Payload Bytes | Published | Retrieved")
    for cnt, payload, pub, ret in results:
        payload_str = str(payload) if payload is not None else "N/A"
        print(f"{cnt:>5} | {payload_str:>13} | {pub} | {ret}")

    synced = sum(1 for _, _, pub, ret in results if pub and ret)
    print(f"Successfully synced entries: {synced}")
