import json

from conftest import BUCKET_NAME, PREFIX, STS_CREDS

from vcm_file_uploader._types import CompressionInfo, UploadResult, UploadSummary
from vcm_file_uploader.manifest import ManifestWriter
from vcm_file_uploader.session import STSUploadSession


def _make_session(**overrides):
    kwargs = {**STS_CREDS, "bucket": BUCKET_NAME, "prefix": PREFIX}
    kwargs.update(overrides)
    return STSUploadSession(**kwargs)


def _make_result(path="file.nc", key=None, size=1024, success=True, compression=None):
    if key is None:
        key = PREFIX + path
    return UploadResult(
        local_path=f"/tmp/{path}",
        s3_key=key,
        size=size,
        success=success,
        compression=compression,
    )


class TestManifestWriter:
    def test_add_result(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        writer.add_result(_make_result())

        manifest = writer.to_dict()
        assert manifest["object_count"] == 1
        assert manifest["objects"][0]["uploaded_key"] == PREFIX + "file.nc"
        assert manifest["objects"][0]["size"] == 1024
        session.close()

    def test_skips_failed_results(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        writer.add_result(_make_result(success=False))

        manifest = writer.to_dict()
        assert manifest["object_count"] == 0
        session.close()

    def test_add_summary(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        summary = UploadSummary(results=[
            _make_result(path="a.nc", size=100),
            _make_result(path="b.nc", size=200, success=False),
            _make_result(path="c.nc", size=300),
        ])
        writer.add_summary(summary)

        manifest = writer.to_dict()
        assert manifest["object_count"] == 2
        session.close()

    def test_compression_metadata(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        compression = CompressionInfo(
            algorithm="gzip",
            level=6,
            original_bytes=200_000_000,
            compressed_bytes=40_000_000,
        )
        writer.add_result(_make_result(compression=compression))

        obj = writer.to_dict()["objects"][0]
        assert obj["compression"]["algorithm"] == "gzip"
        assert obj["compression"]["level"] == 6
        assert obj["compression"]["original_bytes"] == 200_000_000
        assert obj["compression"]["compressed_bytes"] == 40_000_000
        session.close()

    def test_no_compression_key_when_none(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        writer.add_result(_make_result())

        obj = writer.to_dict()["objects"][0]
        assert "compression" not in obj
        session.close()

    def test_manifest_key(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="abc123")
        expected = PREFIX + "manifests/uploader_manifest_abc123.json"
        assert writer.manifest_key == expected
        session.close()

    def test_auto_generated_run_id(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session)
        assert len(writer.run_id) == 12
        session.close()

    def test_to_dict_structure(self, mock_s3):
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        writer.add_result(_make_result())

        manifest = writer.to_dict()
        assert manifest["run_id"] == "test-run"
        assert manifest["bucket"] == BUCKET_NAME
        assert manifest["prefix"] == PREFIX
        assert "created_at" in manifest
        assert "objects" in manifest
        session.close()


class TestManifestWriterUpload:
    def test_write_manifest_to_s3(self, mock_s3):
        bucket_name, client = mock_s3
        session = _make_session()
        writer = ManifestWriter(session, run_id="test-run")
        writer.add_result(_make_result())

        result = writer.write_manifest()

        assert result.success is True
        assert result.s3_key == writer.manifest_key

        # Verify manifest content in S3
        obj = client.get_object(Bucket=bucket_name, Key=writer.manifest_key)
        manifest = json.loads(obj["Body"].read())
        assert manifest["run_id"] == "test-run"
        assert manifest["object_count"] == 1
        session.close()

    def test_write_empty_manifest(self, mock_s3):
        bucket_name, client = mock_s3
        session = _make_session()
        writer = ManifestWriter(session, run_id="empty-run")

        result = writer.write_manifest()

        assert result.success is True
        obj = client.get_object(Bucket=bucket_name, Key=writer.manifest_key)
        manifest = json.loads(obj["Body"].read())
        assert manifest["object_count"] == 0
        assert manifest["objects"] == []
        session.close()
