"""Tests for mldash._upload — bundle upload to ML-Dash server."""
import json
import os
import tempfile

import pytest
from unittest.mock import MagicMock, patch

from mldash._errors import MLDashAuthError, MLDashUploadError
from mldash._upload import upload_bundle


@pytest.fixture
def fake_zip(tmp_path):
    """Create a fake ZIP file for upload testing."""
    p = tmp_path / 'bundle.zip'
    p.write_bytes(b'PK\x03\x04fake zip content')
    return str(p)


class TestUploadBundle:
    @patch('mldash._upload.requests.post')
    def test_successful_upload(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 201
        mock_resp.json.return_value = {
            'model': {'id': 42, 'name': 'Test', 'slug': 'test'},
            'message': 'Model imported successfully',
        }
        mock_post.return_value = mock_resp

        result = upload_bundle(
            fake_zip, 'http://localhost:8000', 'mldash_key123', 'Test',
            description='desc', task_type='classification', framework='sklearn',
            project_id=5,
        )

        assert result['model']['id'] == 42
        mock_post.assert_called_once()
        call_kwargs = mock_post.call_args
        assert call_kwargs.kwargs['headers'] == {'X-API-Key': 'mldash_key123'}
        assert call_kwargs.kwargs['data']['name'] == 'Test'
        assert call_kwargs.kwargs['data']['description'] == 'desc'
        assert call_kwargs.kwargs['data']['task_type'] == 'classification'
        assert call_kwargs.kwargs['data']['project'] == '5'

    @patch('mldash._upload.requests.post')
    def test_omits_empty_optional_fields(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 201
        mock_resp.json.return_value = {'model': {'id': 1}}
        mock_post.return_value = mock_resp

        upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_key', 'Name')

        data = mock_post.call_args.kwargs['data']
        assert 'description' not in data
        assert 'task_type' not in data
        assert 'framework' not in data
        assert 'project' not in data

    @patch('mldash._upload.requests.post')
    def test_auth_error_401(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 401
        mock_resp.json.return_value = {'detail': 'Invalid API key'}
        mock_post.return_value = mock_resp

        with pytest.raises(MLDashAuthError, match='Invalid API key'):
            upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_bad', 'Test')

    @patch('mldash._upload.requests.post')
    def test_auth_error_403(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 403
        mock_resp.json.return_value = {'error': 'Forbidden'}
        mock_post.return_value = mock_resp

        with pytest.raises(MLDashAuthError, match='Forbidden'):
            upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_bad', 'Test')

    @patch('mldash._upload.requests.post')
    def test_server_error_500(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 500
        mock_resp.json.return_value = {'error': 'Internal server error'}
        mock_post.return_value = mock_resp

        with pytest.raises(MLDashUploadError, match='500'):
            upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_key', 'Test')

    @patch('mldash._upload.requests.post')
    def test_connection_error(self, mock_post, fake_zip):
        import requests as req
        mock_post.side_effect = req.ConnectionError('refused')

        with pytest.raises(MLDashUploadError, match='Could not connect'):
            upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_key', 'Test')

    @patch('mldash._upload.requests.post')
    def test_request_exception(self, mock_post, fake_zip):
        import requests as req
        mock_post.side_effect = req.RequestException('timeout')

        with pytest.raises(MLDashUploadError, match='Upload failed'):
            upload_bundle(fake_zip, 'http://localhost:8000', 'mldash_key', 'Test')

    @patch('mldash._upload.requests.post')
    def test_sends_correct_endpoint(self, mock_post, fake_zip):
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {'model': {}}
        mock_post.return_value = mock_resp

        upload_bundle(fake_zip, 'http://localhost:8000/', 'mldash_key', 'Test')

        # Should strip trailing slash and append /api/models/import/
        call_args = mock_post.call_args
        assert call_args.args[0] == 'http://localhost:8000/api/models/import/'
