"""Tests for export commands."""

import csv
import json
from io import StringIO

from aceiot_models_cli.cli import cli


class TestPointsExportCommand:
    """Test points list-export command."""

    def test_export_points_csv_stdout(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting all points to CSV (stdout)."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [
                {
                    "id": 1,
                    "name": "site1/temp/sensor1",
                    "site": "site1",
                    "point_type": "bacnet",
                    "collect_enabled": True,
                    "collect_interval": 300,
                    "display_name": "Temperature Sensor 1",
                    "unit": "degF",
                },
                {
                    "id": 2,
                    "name": "site1/hum/sensor1",
                    "site": "site1",
                    "point_type": "bacnet",
                    "collect_enabled": False,
                    "collect_interval": 600,
                    "display_name": "Humidity Sensor 1",
                    "unit": "%RH",
                },
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 2,
        }

        result = runner.invoke(cli, ["points", "list-export"])
        assert result.exit_code == 0

        # Parse CSV output
        csv_reader = csv.DictReader(StringIO(result.output))
        rows = list(csv_reader)
        assert len(rows) == 2
        assert rows[0]["name"] == "site1/temp/sensor1"
        assert rows[0]["display_name"] == "Temperature Sensor 1"
        assert rows[1]["name"] == "site1/hum/sensor1"

    def test_export_points_json_stdout(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting all points to JSON (stdout)."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [
                {
                    "id": 1,
                    "name": "site1/temp/sensor1",
                    "site": "site1",
                    "point_type": "bacnet",
                },
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        result = runner.invoke(cli, ["points", "list-export", "--format", "json"])
        assert result.exit_code == 0

        # Parse JSON output
        data = json.loads(result.output)
        assert len(data) == 1
        assert data[0]["name"] == "site1/temp/sensor1"
        assert data[0]["point_type"] == "bacnet"

    def test_export_points_csv_file(self, runner, mock_load_config, mock_api_client_class, tmp_path):
        """Test exporting all points to CSV file."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [
                {"id": 1, "name": "site1/temp/sensor1", "site": "site1"},
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        output_file = tmp_path / "points.csv"
        result = runner.invoke(cli, ["points", "list-export", "--output", str(output_file)])
        assert result.exit_code == 0
        assert "Successfully exported 1 points" in result.output

        # Verify file contents
        assert output_file.exists()
        with output_file.open() as f:
            csv_reader = csv.DictReader(f)
            rows = list(csv_reader)
            assert len(rows) == 1
            assert rows[0]["name"] == "site1/temp/sensor1"

    def test_export_points_json_file(self, runner, mock_load_config, mock_api_client_class, tmp_path):
        """Test exporting all points to JSON file."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [
                {"id": 1, "name": "site1/temp/sensor1", "site": "site1"},
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        output_file = tmp_path / "points.json"
        result = runner.invoke(
            cli, ["points", "list-export", "--format", "json", "--output", str(output_file)]
        )
        assert result.exit_code == 0
        assert "Successfully exported 1 points" in result.output

        # Verify file contents
        assert output_file.exists()
        with output_file.open() as f:
            data = json.load(f)
            assert len(data) == 1
            assert data[0]["name"] == "site1/temp/sensor1"

    def test_export_points_with_site_filter(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting points filtered by site."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [
                {"id": 1, "name": "site1/temp/sensor1", "site": "site1"},
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        result = runner.invoke(cli, ["points", "list-export", "--site", "site1"])
        assert result.exit_code == 0
        mock_api_client.get_points.assert_called_once_with(page=1, per_page=100, site_name="site1")

    def test_export_points_pagination(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting points with multiple pages."""
        mock_api_client = mock_api_client_class.return_value

        # Mock paginated responses
        def get_points_side_effect(**kwargs):
            page = kwargs.get("page", 1)
            if page == 1:
                return {
                    "items": [
                        {"id": 1, "name": "point1"},
                        {"id": 2, "name": "point2"},
                    ],
                    "page": 1,
                    "pages": 2,
                    "per_page": 2,
                    "total": 3,
                }
            elif page == 2:
                return {
                    "items": [
                        {"id": 3, "name": "point3"},
                    ],
                    "page": 2,
                    "pages": 2,
                    "per_page": 2,
                    "total": 3,
                }
            return {"items": [], "page": page, "pages": 2, "per_page": 2, "total": 3}

        mock_api_client.get_points.side_effect = get_points_side_effect

        result = runner.invoke(cli, ["points", "list-export", "--format", "json"])
        assert result.exit_code == 0

        # Verify all pages were fetched
        data = json.loads(result.output)
        assert len(data) == 3
        assert data[0]["name"] == "point1"
        assert data[1]["name"] == "point2"
        assert data[2]["name"] == "point3"

    def test_export_points_no_data(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting when no points exist."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.return_value = {
            "items": [],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 0,
        }

        result = runner.invoke(cli, ["points", "list-export"])
        assert result.exit_code == 0
        assert "No points found" in result.output

    def test_export_points_error(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting points with API error."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_points.side_effect = Exception("API error")

        result = runner.invoke(cli, ["points", "list-export"])
        assert result.exit_code == 1


class TestSitesPointsExportCommand:
    """Test sites points-export command."""

    def test_export_site_points_csv_stdout(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting site points to CSV (stdout)."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [
                {
                    "id": 1,
                    "name": "site1/temp/sensor1",
                    "site": "site1",
                    "equipment": "AHU-1",
                    "type": "analog",
                    "display_name": "Temperature Sensor 1",
                },
                {
                    "id": 2,
                    "name": "site1/hum/sensor1",
                    "site": "site1",
                    "equipment": "AHU-1",
                    "type": "analog",
                    "display_name": "Humidity Sensor 1",
                },
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 2,
        }

        result = runner.invoke(cli, ["sites", "points-export", "site1"])
        assert result.exit_code == 0

        # Parse CSV output
        csv_reader = csv.DictReader(StringIO(result.output))
        rows = list(csv_reader)
        assert len(rows) == 2
        assert rows[0]["name"] == "site1/temp/sensor1"
        assert rows[0]["equipment"] == "AHU-1"
        assert rows[1]["name"] == "site1/hum/sensor1"

    def test_export_site_points_json_stdout(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting site points to JSON (stdout)."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [
                {
                    "id": 1,
                    "name": "site1/temp/sensor1",
                    "site": "site1",
                    "type": "analog",
                },
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        result = runner.invoke(cli, ["sites", "points-export", "site1", "--format", "json"])
        assert result.exit_code == 0

        # Parse JSON output
        data = json.loads(result.output)
        assert len(data) == 1
        assert data[0]["name"] == "site1/temp/sensor1"
        assert data[0]["type"] == "analog"

    def test_export_site_points_csv_file(
        self, runner, mock_load_config, mock_api_client_class, tmp_path
    ):
        """Test exporting site points to CSV file."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [
                {"id": 1, "name": "site1/temp/sensor1", "site": "site1"},
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        output_file = tmp_path / "site1-points.csv"
        result = runner.invoke(
            cli, ["sites", "points-export", "site1", "--output", str(output_file)]
        )
        assert result.exit_code == 0
        assert "Successfully exported 1 points for site 'site1'" in result.output

        # Verify file contents
        assert output_file.exists()
        with output_file.open() as f:
            csv_reader = csv.DictReader(f)
            rows = list(csv_reader)
            assert len(rows) == 1
            assert rows[0]["name"] == "site1/temp/sensor1"

    def test_export_site_points_json_file(
        self, runner, mock_load_config, mock_api_client_class, tmp_path
    ):
        """Test exporting site points to JSON file."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [
                {"id": 1, "name": "site1/temp/sensor1", "site": "site1"},
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        output_file = tmp_path / "site1-points.json"
        result = runner.invoke(
            cli,
            ["sites", "points-export", "site1", "--format", "json", "--output", str(output_file)],
        )
        assert result.exit_code == 0
        assert "Successfully exported 1 points for site 'site1'" in result.output

        # Verify file contents
        assert output_file.exists()
        with output_file.open() as f:
            data = json.load(f)
            assert len(data) == 1
            assert data[0]["name"] == "site1/temp/sensor1"

    def test_export_site_points_pagination(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting site points with multiple pages."""
        mock_api_client = mock_api_client_class.return_value

        # Mock paginated responses
        def get_site_points_side_effect(site_name, **kwargs):
            page = kwargs.get("page", 1)
            if page == 1:
                return {
                    "items": [
                        {"id": 1, "name": f"{site_name}/point1"},
                        {"id": 2, "name": f"{site_name}/point2"},
                    ],
                    "page": 1,
                    "pages": 2,
                    "per_page": 2,
                    "total": 3,
                }
            elif page == 2:
                return {
                    "items": [
                        {"id": 3, "name": f"{site_name}/point3"},
                    ],
                    "page": 2,
                    "pages": 2,
                    "per_page": 2,
                    "total": 3,
                }
            return {"items": [], "page": page, "pages": 2, "per_page": 2, "total": 3}

        mock_api_client.get_site_points.side_effect = get_site_points_side_effect

        result = runner.invoke(cli, ["sites", "points-export", "site1", "--format", "json"])
        assert result.exit_code == 0

        # Verify all pages were fetched
        data = json.loads(result.output)
        assert len(data) == 3
        assert data[0]["name"] == "site1/point1"
        assert data[1]["name"] == "site1/point2"
        assert data[2]["name"] == "site1/point3"

    def test_export_site_points_no_data(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting when no site points exist."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 0,
        }

        result = runner.invoke(cli, ["sites", "points-export", "site1"])
        assert result.exit_code == 0
        assert "No points found for site 'site1'" in result.output

    def test_export_site_points_error(self, runner, mock_load_config, mock_api_client_class):
        """Test exporting site points with API error."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.side_effect = Exception("Site not found")

        result = runner.invoke(cli, ["sites", "points-export", "nonexistent"])
        assert result.exit_code == 1

    def test_export_preserves_all_fields(self, runner, mock_load_config, mock_api_client_class):
        """Test that export includes all fields from API response."""
        mock_api_client = mock_api_client_class.return_value
        mock_api_client.get_site_points.return_value = {
            "items": [
                {
                    "id": 1,
                    "name": "site1/temp/sensor1",
                    "site": "site1",
                    "display_name": "Temperature Sensor 1",
                    "description": "Room temperature",
                    "unit": "degF",
                    "equipment": "AHU-1",
                    "type": "analog",
                    "collect_enabled": True,
                    "collect_interval": 300,
                    "custom_field": "custom_value",
                },
            ],
            "page": 1,
            "pages": 1,
            "per_page": 100,
            "total": 1,
        }

        result = runner.invoke(cli, ["sites", "points-export", "site1", "--format", "json"])
        assert result.exit_code == 0

        data = json.loads(result.output)
        assert len(data) == 1
        point = data[0]

        # Verify all fields are present
        assert point["id"] == 1
        assert point["name"] == "site1/temp/sensor1"
        assert point["display_name"] == "Temperature Sensor 1"
        assert point["description"] == "Room temperature"
        assert point["unit"] == "degF"
        assert point["equipment"] == "AHU-1"
        assert point["type"] == "analog"
        assert point["collect_enabled"] is True
        assert point["collect_interval"] == 300
        assert point["custom_field"] == "custom_value"
