import hashlib
import json
import gzip
from pathlib import Path
from tempfile import TemporaryDirectory
import base64
import os
import asyncio
from unittest.mock import patch

from nostr import prepare_snapshot, NostrClient
from password_manager.encryption import EncryptionManager


def test_prepare_snapshot_roundtrip():
    data = b"a" * 70000
    manifest, chunks = prepare_snapshot(data, 50000)
    assert len(chunks) == len(manifest.chunks)
    joined = b"".join(chunks)
    assert len(joined) <= len(data)
    assert hashlib.sha256(chunks[0]).hexdigest() == manifest.chunks[0].hash
    assert manifest.chunks[0].id == "seedpass-chunk-0000"
    assert data == gzip.decompress(joined)


class DummyEvent:
    def __init__(self, content):
        self._content = content

    def content(self):
        return self._content


class DummyClient:
    def __init__(self, events):
        self.events = events
        self.pos = 0

    async def add_relays(self, relays):
        pass

    async def add_relay(self, relay):
        pass

    async def connect(self):
        pass

    async def disconnect(self):
        pass

    async def send_event(self, event):
        pass

    async def fetch_events(self, f, timeout):
        ev = self.events[self.pos]
        self.pos += 1

        class E:
            def __init__(self, ev):
                self._ev = ev

            def to_vec(self):
                return [self._ev]

        return E(ev)


def test_fetch_latest_snapshot():
    data = b"seedpass" * 1000
    manifest, chunks = prepare_snapshot(data, 50000)
    for i, m in enumerate(manifest.chunks):
        m.event_id = f"{i:064x}"
    manifest_json = json.dumps(
        {
            "ver": manifest.ver,
            "algo": manifest.algo,
            "chunks": [c.__dict__ for c in manifest.chunks],
            "delta_since": None,
        }
    )
    events = [DummyEvent(manifest_json)] + [
        DummyEvent(base64.b64encode(c).decode()) for c in chunks
    ]

    client = DummyClient(events)
    with TemporaryDirectory() as tmpdir:
        enc_mgr = EncryptionManager(
            base64.urlsafe_b64encode(os.urandom(32)), Path(tmpdir)
        )
        with patch("nostr.client.Client", lambda signer: client), patch(
            "nostr.client.KeyManager"
        ) as MockKM, patch.object(NostrClient, "initialize_client_pool"), patch.object(
            enc_mgr, "decrypt_parent_seed", return_value="seed"
        ):
            km = MockKM.return_value
            km.keys.private_key_hex.return_value = "1" * 64
            km.keys.public_key_hex.return_value = "2" * 64
            nc = NostrClient(enc_mgr, "fp")
            result_manifest, result_chunks = asyncio.run(nc.fetch_latest_snapshot())

    assert manifest == result_manifest
    assert result_chunks == chunks
    assert [c.event_id for c in manifest.chunks] == [
        c.event_id for c in result_manifest.chunks
    ]
