"""Unit tests for docker build service."""

import subprocess
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from src.services.build.docker_build import build_image


class TestBuildImage:
    """Tests for build_image function."""

    @patch("subprocess.run")
    def test_builds_image_successfully(self, mock_run) -> None:
        mock_run.return_value = MagicMock(
            returncode=0,
            stdout="Successfully built abc123",
            stderr="",
        )
        
        with tempfile.TemporaryDirectory() as tmpdir:
            result = build_image(tmpdir, "FROM python:3.11", "test-image:latest")
            
            assert result[0] == 0
            assert "Successfully built" in result[1]
            mock_run.assert_called_once()
            call_args = mock_run.call_args
            assert "test-image:latest" in call_args[0][0]
            assert "build" in call_args[0][0]

    @patch("subprocess.run")
    def test_build_fails(self, mock_run) -> None:
        mock_run.return_value = MagicMock(
            returncode=1,
            stdout="",
            stderr="Error: failed to build",
        )
        
        with tempfile.TemporaryDirectory() as tmpdir:
            result = build_image(tmpdir, "FROM invalid:image", "test-image:latest")
            
            assert result[0] == 1
            assert "failed to build" in result[2]

    @patch("subprocess.run")
    def test_build_with_tag(self, mock_run) -> None:
        mock_run.return_value = MagicMock(
            returncode=0,
            stdout="",
            stderr="",
        )
        
        with tempfile.TemporaryDirectory() as tmpdir:
            build_image(tmpdir, "FROM python:3.11", "my-registry/tool:v1.0")
            
            call_args = mock_run.call_args[0][0]
            assert "my-registry/tool:v1.0" in call_args

    @patch("subprocess.run")
    def test_build_command_structure(self, mock_run) -> None:
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            build_image(tmpdir, "FROM python:3.11", "test:latest")
            
            cmd = mock_run.call_args[0][0]
            assert cmd[0] == "docker"
            assert cmd[1] == "build"
            assert "-t" in cmd
            assert "test:latest" in cmd
            assert tmpdir in cmd

    @patch("subprocess.run")
    def test_build_writes_dockerfile(self, mock_run) -> None:
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            dockerfile_content = "FROM python:3.11\\nRUN pip install flask"
            build_image(tmpdir, dockerfile_content, "test:latest")
            
            dockerfile_path = Path(tmpdir) / "Dockerfile"
            assert dockerfile_path.exists()
            assert dockerfile_path.read_text() == dockerfile_content

    @patch("subprocess.run")
    def test_build_writes_context_files(self, mock_run) -> None:
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            build_context = {
                "requirements.txt": "flask==2.0\\nnumpy==1.21",
                "app.py": "from flask import Flask\\napp = Flask(__name__)",
            }
            build_image(tmpdir, "FROM python:3.11", "test:latest", build_context)
            
            req_path = Path(tmpdir) / "requirements.txt"
            app_path = Path(tmpdir) / "app.py"
            assert req_path.exists()
            assert req_path.read_text() == "flask==2.0\\nnumpy==1.21"
            assert app_path.exists()
            assert app_path.read_text() == "from flask import Flask\\napp = Flask(__name__)"

    @patch("subprocess.run")
    def test_build_creates_nested_directories(self, mock_run) -> None:
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            build_context = {
                "src/app.py": "print('hello')",
            }
            build_image(tmpdir, "FROM python:3.11", "test:latest", build_context)
            
            app_path = Path(tmpdir) / "src" / "app.py"
            assert app_path.exists()

    @patch("subprocess.run")
    def test_build_prevents_path_traversal(self, mock_run) -> None:
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            build_context = {
                "../../../etc/passwd": "root:x:0:0",
            }
            
            with pytest.raises(ValueError, match="escapes instance directory"):
                build_image(tmpdir, "FROM python:3.11", "test:latest", build_context)
            
            mock_run.assert_not_called()

    @patch("subprocess.run")
    def test_build_timeout(self, mock_run) -> None:
        mock_run.side_effect = subprocess.TimeoutExpired(cmd=["docker", "build"], timeout=300)
        
        with tempfile.TemporaryDirectory() as tmpdir:
            result = build_image(tmpdir, "FROM python:3.11", "test:latest")
            
            assert result[0] == 1
            assert "timed out" in result[2].lower()

    @patch("subprocess.run")
    def test_build_exception(self, mock_run) -> None:
        mock_run.side_effect = OSError("Docker not available")
        
        with tempfile.TemporaryDirectory() as tmpdir:
            result = build_image(tmpdir, "FROM python:3.11", "test:latest")
            
            assert result[0] == 1
            assert "Docker not available" in result[2]
