from __future__ import annotations

import json

from aegro.cli import app


class TestFinancialBill:
    def test_get_bill(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = {"_id": "b1", "description": "Conta"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["financial", "bill", "b1"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["description"] == "Conta"
        mock_client.get.assert_called_once_with("/pub/v1/bills/b1", api_key="key-1")

    def test_get_bill_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, ["financial", "bill", "missing"])
        assert result.exit_code == 0
        assert "nao encontrad" in result.stdout.lower()

    def test_get_bill_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(app, ["financial", "bill", "b1"])
        assert result.exit_code != 0


class TestFinancialInstallment:
    def test_get_installment(self, runner, mock_settings, mock_client, temp_state):
        mock_client.get.return_value = {"_id": "i1", "dueDate": "2025-06-01"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["financial", "installment", "i1"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["dueDate"] == "2025-06-01"
        mock_client.get.assert_called_once_with("/pub/v1/installments/i1", api_key="key-1")

    def test_get_installment_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, ["financial", "installment", "missing"])
        assert result.exit_code == 0
        assert "nao encontrad" in result.stdout.lower()

    def test_get_installment_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(app, ["financial", "installment", "i1"])
        assert result.exit_code != 0


class TestFinancialListInstallments:
    def test_list_no_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = [{"_id": "i1", "dueDate": "2025-06-01"}]
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["financial", "installments"])
        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_with_filters(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = []
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "financial",
                "installments",
                "--operation-type",
                "EXPENSE",
                "--status",
                "PAID",
                "--status",
                "NOT_PAID",
                "--due-date-start",
                "2025-01-01",
                "--due-date-end",
                "2025-12-31",
                "--bill-key",
                "b1",
                "--bill-key",
                "b2",
            ],
        )
        assert result.exit_code == 0
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["operationType"] == "EXPENSE"
        assert call_body["statuses"] == ["PAID", "NOT_PAID"]
        assert call_body["dueDateStart"] == "2025-01-01"
        assert call_body["dueDateEnd"] == "2025-12-31"
        assert call_body["billKeys"] == ["b1", "b2"]

    def test_list_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, ["financial", "installments", "--page", "3"])
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["requiredPageNumber"] == 3

    def test_list_empty_result(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, ["financial", "installments"])
        assert result.exit_code == 0
        assert "nenhum" in result.stdout.lower()


class TestFinancialCreateInstallment:
    def test_create_installment(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = {"_id": "i-new", "dueDate": "2025-06-01"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "financial",
                "create-installment",
                "--bill-key",
                "b1",
                "--bank-account-key",
                "ba1",
                "--due-date",
                "2025-06-01",
                "--amount",
                "1500.00",
            ],
        )
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["_id"] == "i-new"
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["billKey"] == "b1"
        assert call_body["bankAccountKey"] == "ba1"
        assert call_body["dueDate"] == "2025-06-01"
        assert call_body["amount"]["amount"] == 1500.00
        assert call_body["amount"]["currency"] == "BRL"

    def test_create_installment_custom_currency(
        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,
            [
                "financial",
                "create-installment",
                "--bill-key",
                "b1",
                "--bank-account-key",
                "ba1",
                "--due-date",
                "2025-06-01",
                "--amount",
                "500",
                "--currency",
                "USD",
            ],
        )
        assert result.exit_code == 0
        assert "Parcela criada" in result.stdout
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["amount"]["currency"] == "USD"

    def test_create_installment_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(
            app,
            [
                "financial",
                "create-installment",
                "--bill-key",
                "b1",
                "--bank-account-key",
                "ba1",
                "--due-date",
                "2025-06-01",
                "--amount",
                "100",
            ],
        )
        assert result.exit_code != 0


class TestFinancialUpdateInstallment:
    def test_update_installment(self, runner, mock_settings, mock_client, temp_state):
        mock_client.put.return_value = {"_id": "i1", "status": "PAID"}
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "financial",
                "update-installment",
                "i1",
                "--bill-key",
                "b1",
                "--bank-account-key",
                "ba1",
                "--number",
                "1",
                "--due-date",
                "2025-06-01",
                "--amount",
                "2000.00",
                "--status",
                "PAID",
            ],
        )
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data["_id"] == "i1"
        mock_client.put.assert_called_once()
        call_body = mock_client.put.call_args.kwargs["json_body"]
        assert call_body["key"] == "i1"
        assert call_body["billKey"] == "b1"
        assert call_body["number"] == 1
        assert call_body["status"] == "PAID"

    def test_update_installment_with_realized(self, runner, mock_settings, mock_client, temp_state):
        mock_client.put.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(
            app,
            [
                "financial",
                "update-installment",
                "i1",
                "--bill-key",
                "b1",
                "--bank-account-key",
                "ba1",
                "--number",
                "1",
                "--due-date",
                "2025-06-01",
                "--amount",
                "1000",
                "--realized-amount",
                "950",
                "--realized-date",
                "2025-05-30",
            ],
        )
        assert result.exit_code == 0
        assert "atualizada" in result.stdout.lower()
        call_body = mock_client.put.call_args.kwargs["json_body"]
        assert call_body["realizedDate"] == "2025-05-30"
        assert call_body["realizedAmount"]["amount"] == 950.0
        assert call_body["realizedAmount"]["currency"] == "BRL"


class TestFinancialDeleteInstallment:
    def test_delete_installment(self, runner, mock_settings, mock_client, temp_state):
        mock_client.delete.return_value = None
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["financial", "delete-installment", "i1"])
        assert result.exit_code == 0
        assert "excluida" in result.stdout.lower()
        assert "Fazenda Sul" in result.stdout
        mock_client.delete.assert_called_once_with("/pub/v1/installments/i1", api_key="key-1")

    def test_delete_installment_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(app, ["financial", "delete-installment", "i1"])
        assert result.exit_code != 0


class TestFinancialRealize:
    def test_realize_installments(self, runner, mock_settings, mock_client, temp_state):
        mock_client.post.return_value = [{"_id": "i1", "status": "PAID"}]
        runner.invoke(app, ["farms", "select", "Fazenda Sul"])
        result = runner.invoke(app, ["financial", "realize", "--key", "i1", "--key", "i2"])
        assert result.exit_code == 0
        data = json.loads(result.stdout)
        assert data[0]["_id"] == "i1"
        call_body = mock_client.post.call_args.kwargs["json_body"]
        assert call_body["list"] == ["i1", "i2"]
        assert mock_client.post.call_args.args[0] == "/pub/v1/installments/realizeList"

    def test_realize_no_result(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, ["financial", "realize", "--key", "i1"])
        assert result.exit_code == 0
        assert "realizadas" in result.stdout.lower()

    def test_realize_no_farm(self, runner, mock_settings, temp_state):
        result = runner.invoke(app, ["financial", "realize", "--key", "i1"])
        assert result.exit_code != 0
