from __future__ import annotations

import json

from aegro.cli import app


class TestStockItem:
    def test_get_item(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = {"_id": "item-1", "name": "Ureia"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "item", "item-1"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["name"] == "Ureia"
        mock_client.get.assert_called_once_with("/pub/v1/stock-items/item-1", api_key="key-1")

    def test_get_item_not_found(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "item", "missing"])
        assert result.exit_code == 0
        assert "nao encontrad" in result.stdout.lower()

    def test_get_item_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(app, ["stock", "item", "item-1"])
        assert result.exit_code != 0


class TestStockLocation:
    def test_get_location(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = {"_id": "loc-1", "name": "Armazem Central"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "location", "loc-1"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["name"] == "Armazem Central"
        mock_client.get.assert_called_once_with("/pub/v1/stock-locations/loc-1", api_key="key-1")

    def test_get_location_not_found(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "location", "missing"])
        assert result.exit_code == 0
        assert "nao encontrad" in result.stdout.lower()


class TestStockItems:
    def test_list_items_no_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = [{"_id": "i1", "name": "Ureia"}]
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "items"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert len(data) == 1
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["requiredPageNumber"] == 1
        assert call_body["maximumItemsPerPageCount"] == 50

    def test_list_items_with_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = []
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(
            app,
            [
                "stock",
                "items",
                "--location-key",
                "loc1",
                "--element-key",
                "elem1",
                "--crop-key",
                "crop1",
            ],
        )
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["locationKey"] == "loc1"
        assert call_body["elementKey"] == "elem1"
        assert call_body["cropKey"] == "crop1"

    def test_list_items_with_page(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = []
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(app, ["stock", "items", "--page", "3"])
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["requiredPageNumber"] == 3

    def test_list_items_empty(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "items"])
        assert result.exit_code == 0
        assert "nenhum" in result.stdout.lower()


class TestStockLocations:
    def test_list_locations(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = [{"_id": "loc-1", "name": "Armazem"}]
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "locations"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert len(data) == 1
        assert mock_client.post.call_args.args[0] == "/pub/v1/stock-locations/filter"

    def test_list_locations_with_page(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = []
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(app, ["stock", "locations", "--page", "2"])
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["requiredPageNumber"] == 2

    def test_list_locations_empty(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "locations"])
        assert result.exit_code == 0
        assert "nenhum" in result.stdout.lower()


class TestStockLogs:
    def test_list_logs_no_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = [{"_id": "log-1"}]
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "logs"])
        assert result.exit_code == 0
        assert mock_client.post.call_args.args[0] == "/pub/v1/stock-logs/filter"

    def test_list_logs_with_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = []
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(
            app,
            [
                "stock",
                "logs",
                "--element-key",
                "elem1",
                "--start-date",
                "2025-01-01",
                "--end-date",
                "2025-12-31",
                "--source-key",
                "loc1",
                "--dest-key",
                "loc2",
            ],
        )
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["elementKey"] == "elem1"
        assert call_body["startDate"] == "2025-01-01"
        assert call_body["endDate"] == "2025-12-31"
        assert call_body["sourceLocationKey"] == "loc1"
        assert call_body["destinationLocationKey"] == "loc2"

    def test_list_logs_empty(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "logs"])
        assert result.exit_code == 0
        assert "nenhum" in result.stdout.lower()


class TestStockLog:
    def test_get_log(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = {"_id": "log-1", "type": "TRANSFER"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "log", "log-1"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["type"] == "TRANSFER"
        mock_client.get.assert_called_once_with("/pub/v1/stock-logs/log-1", api_key="key-1")

    def test_get_log_not_found(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["stock", "log", "missing"])
        assert result.exit_code == 0
        assert "nao encontrad" in result.stdout.lower()


class TestStockTransfer:
    def test_transfer(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "log-new"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "stock",
                "transfer",
                "--element-key",
                "elem1",
                "--quantity",
                "100",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
                "--dest-key",
                "loc2",
            ],
        )
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["_id"] == "log-new"
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["elementKey"] == "elem1"
        assert call_body["quantity"] == {"magnitude": 100.0, "unit": "kg"}
        assert call_body["occurrenceDate"] == "2025-06-01"
        assert call_body["sourceLocationKey"] == "loc1"
        assert call_body["destinationLocationKey"] == "loc2"
        assert "observations" not in call_body
        assert mock_client.post.call_args.args[0] == "/pub/v1/stock-logs"

    def test_transfer_with_observations(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "log-new"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(
            app,
            [
                "stock",
                "transfer",
                "--element-key",
                "elem1",
                "--quantity",
                "50",
                "--unit",
                "L",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
                "--dest-key",
                "loc2",
                "--observations",
                "Transferencia urgente",
            ],
        )
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["observations"] == "Transferencia urgente"

    def test_transfer_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(
            app,
            [
                "stock",
                "transfer",
                "--element-key",
                "elem1",
                "--quantity",
                "100",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
                "--dest-key",
                "loc2",
            ],
        )
        assert result.exit_code != 0


class TestStockEntry:
    def test_entry(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "log-entry"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "stock",
                "entry",
                "--element-key",
                "elem1",
                "--quantity",
                "50",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--amount",
                "500.00",
                "--dest-key",
                "loc1",
            ],
        )
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["_id"] == "log-entry"
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["elementKey"] == "elem1"
        assert call_body["quantity"] == {"magnitude": 50.0, "unit": "kg"}
        assert call_body["occurrenceDate"] == "2025-06-01"
        assert call_body["amount"] == {"amount": 500.0, "currencyCode": "BRL"}
        assert call_body["destinationLocationKey"] == "loc1"
        assert "observations" not in call_body
        assert mock_client.post.call_args.args[0] == "/pub/v1/stock-logs/manual-entries"

    def test_entry_with_currency_and_observations(
        self, runner, mock_settings, mock_client, temp_state
    ):
        mock_client.post.return_value = {"_id": "log-entry"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(
            app,
            [
                "stock",
                "entry",
                "--element-key",
                "elem1",
                "--quantity",
                "25",
                "--unit",
                "L",
                "--date",
                "2025-06-01",
                "--amount",
                "1200.50",
                "--currency",
                "USD",
                "--dest-key",
                "loc1",
                "--observations",
                "Compra importada",
            ],
        )
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["amount"] == {"amount": 1200.5, "currencyCode": "USD"}
        assert call_body["observations"] == "Compra importada"

    def test_entry_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(
            app,
            [
                "stock",
                "entry",
                "--element-key",
                "elem1",
                "--quantity",
                "50",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--amount",
                "500.00",
                "--dest-key",
                "loc1",
            ],
        )
        assert result.exit_code != 0


class TestStockRemoval:
    def test_removal(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "log-removal"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "stock",
                "removal",
                "--element-key",
                "elem1",
                "--quantity",
                "10",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
            ],
        )
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["_id"] == "log-removal"
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["elementKey"] == "elem1"
        assert call_body["quantity"] == {"magnitude": 10.0, "unit": "kg"}
        assert call_body["occurrenceDate"] == "2025-06-01"
        assert call_body["sourceLocationKey"] == "loc1"
        assert "observations" not in call_body
        assert mock_client.post.call_args.args[0] == "/pub/v1/stock-logs/manual-removals"

    def test_removal_with_observations(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "log-removal"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        runner.invoke(
            app,
            [
                "stock",
                "removal",
                "--element-key",
                "elem1",
                "--quantity",
                "5",
                "--unit",
                "un",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
                "--observations",
                "Perda por avaria",
            ],
        )
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["observations"] == "Perda por avaria"

    def test_removal_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(
            app,
            [
                "stock",
                "removal",
                "--element-key",
                "elem1",
                "--quantity",
                "10",
                "--unit",
                "kg",
                "--date",
                "2025-06-01",
                "--source-key",
                "loc1",
            ],
        )
        assert result.exit_code != 0
