"""Tests for mldash._bundle — metadata building and ZIP creation."""
import json
import os
import zipfile

import pytest

from mldash._bundle import build_metadata, create_bundle
from mldash._serialize import serialize_artifacts
from mldash._version import __version__


class TestBuildMetadata:
    def test_basic_fields(self):
        detected = {
            'task_type': 'classification',
            'framework': 'sklearn',
            'model_class': 'LogisticRegression',
            'hyperparameters': {'C': 1.0},
            'class_labels': ['0', '1'],
            'n_features': 2,
            'feature_names': ['a', 'b'],
        }
        metadata = build_metadata(
            'Test Model', 'A description', detected,
            features={'a': 'numeric', 'b': 'numeric'},
            target={'label': 'categorical'},
            scaler=object(), encoder=None, label_map={'0': 'neg'},
        )
        assert metadata['name'] == 'Test Model'
        assert metadata['description'] == 'A description'
        assert metadata['bundle_version'] == '1'
        assert metadata['package_version'] == __version__
        assert metadata['task_type'] == 'classification'
        assert metadata['framework'] == 'sklearn'
        assert metadata['model_class'] == 'LogisticRegression'
        assert metadata['features'] == {'a': 'numeric', 'b': 'numeric'}
        assert metadata['target'] == {'label': 'categorical'}
        assert metadata['has_scaler'] is True
        assert metadata['has_encoder'] is False
        assert metadata['has_label_map'] is True
        assert metadata['hyperparameters'] == {'C': 1.0}
        assert metadata['class_labels'] == ['0', '1']
        assert metadata['n_features'] == 2
        assert metadata['feature_names'] == ['a', 'b']

    def test_defaults_when_none(self):
        detected = {
            'task_type': None,
            'framework': None,
            'model_class': None,
            'hyperparameters': None,
            'class_labels': None,
            'n_features': None,
            'feature_names': None,
        }
        metadata = build_metadata(
            'Model', '', detected,
            features=None, target=None,
            scaler=None, encoder=None, label_map=None,
        )
        assert metadata['task_type'] == ''
        assert metadata['framework'] == ''
        assert metadata['features'] == {}
        assert metadata['target'] == {}
        assert metadata['has_scaler'] is False
        assert metadata['has_encoder'] is False
        assert metadata['has_label_map'] is False


class TestCreateBundle:
    def test_creates_valid_zip(self, tmp_dir, fitted_classifier):
        artifact_paths = serialize_artifacts(tmp_dir, fitted_classifier)
        metadata = {
            'name': 'Test',
            'task_type': 'classification',
        }
        zip_path = create_bundle(tmp_dir, artifact_paths, metadata)

        assert os.path.isfile(zip_path)
        assert zip_path.endswith('.zip')

        with zipfile.ZipFile(zip_path, 'r') as zf:
            names = zf.namelist()
            assert 'metadata.json' in names
            assert 'model.pkl' in names

    def test_metadata_json_is_valid(self, tmp_dir, fitted_classifier):
        artifact_paths = serialize_artifacts(tmp_dir, fitted_classifier)
        metadata = {'name': 'Test', 'framework': 'sklearn'}
        zip_path = create_bundle(tmp_dir, artifact_paths, metadata)

        with zipfile.ZipFile(zip_path, 'r') as zf:
            data = json.loads(zf.read('metadata.json'))
            assert data['name'] == 'Test'
            assert data['framework'] == 'sklearn'

    def test_includes_all_artifacts(self, tmp_dir, fitted_classifier,
                                    fitted_scaler, sample_label_map):
        artifact_paths = serialize_artifacts(
            tmp_dir, fitted_classifier,
            scaler=fitted_scaler, label_map=sample_label_map,
        )
        metadata = {'name': 'Full'}
        zip_path = create_bundle(tmp_dir, artifact_paths, metadata)

        with zipfile.ZipFile(zip_path, 'r') as zf:
            names = zf.namelist()
            assert 'model.pkl' in names
            assert 'scaler.pkl' in names
            assert 'label_map.pkl' in names
            # encoder not included because it wasn't serialized
            assert 'encoder.pkl' not in names

    def test_zip_is_compressed(self, tmp_dir, fitted_classifier):
        artifact_paths = serialize_artifacts(tmp_dir, fitted_classifier)
        metadata = {'name': 'Compressed'}
        zip_path = create_bundle(tmp_dir, artifact_paths, metadata)

        with zipfile.ZipFile(zip_path, 'r') as zf:
            for info in zf.infolist():
                assert info.compress_type == zipfile.ZIP_DEFLATED
