from __future__ import annotations

import json
from typing import Optional

import typer

from aegro.cli._config import get_client, require_farm
from aegro.cli._errors import handle_error
from aegro.cli._output import OutputFormat, format_output
from aegro.validation import validate_key

financial_app = typer.Typer(help="Lancamentos financeiros e parcelas.")


def _parse_json_option(value: Optional[str], flag: str) -> Optional[list | dict]:
    if value is None:
        return None
    try:
        return json.loads(value)
    except json.JSONDecodeError as exc:
        typer.echo(f"{flag} contem JSON invalido: {exc}", err=True)
        raise SystemExit(4) from exc


@financial_app.command("bills")
def list_bills(
    operation_type: Optional[str] = typer.Option(
        None, "--operation-type", help="Tipo: REVENUE ou EXPENSE."
    ),
    start_date: Optional[str] = typer.Option(
        None, "--start-date", help="Data inicial (YYYY-MM-DD)."
    ),
    end_date: Optional[str] = typer.Option(None, "--end-date", help="Data final (YYYY-MM-DD)."),
    company_key: Optional[list[str]] = typer.Option(
        None, "--company-key", help="Chave de empresa/fornecedor (repetivel)."
    ),
    crop_key: Optional[list[str]] = typer.Option(
        None, "--crop-key", help="Chave de safra (repetivel)."
    ),
    financial_category_key: Optional[list[str]] = typer.Option(
        None, "--financial-category-key", help="Chave de categoria financeira (repetivel)."
    ),
    bank_account_key: Optional[list[str]] = typer.Option(
        None, "--bank-account-key", help="Chave de conta bancaria (repetivel)."
    ),
    payment_method: Optional[list[str]] = typer.Option(
        None, "--payment-method", help="Metodo de pagamento (repetivel)."
    ),
    receipt: Optional[str] = typer.Option(None, "--receipt", help="Numero do recibo/nota fiscal."),
    page: int = typer.Option(1, "--page", help="Numero da pagina."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Lista lancamentos financeiros (bills) com filtros opcionais.

    EXAMPLES:
      aegro financial bills
      aegro financial bills --operation-type EXPENSE --start-date 2025-01-01 --end-date 2025-12-31
      aegro financial bills --company-key c1 --payment-method INSTALLMENT
    """
    try:
        _, api_key = require_farm()
        client = get_client()
        body: dict = {
            "requiredPageNumber": page,
            "maximumItemsPerPageCount": 50,
        }
        if operation_type is not None:
            body["operationType"] = operation_type
        if start_date is not None:
            body["startDate"] = start_date
        if end_date is not None:
            body["endDate"] = end_date
        if company_key:
            body["companyKeys"] = company_key
        if crop_key:
            body["cropKeys"] = crop_key
        if financial_category_key:
            body["financialCategoryKeys"] = financial_category_key
        if bank_account_key:
            body["bankAccountKeys"] = bank_account_key
        if payment_method:
            body["paymentMethods"] = payment_method
        if receipt is not None:
            body["receipt"] = receipt
        result = client.post("/pub/v1/bills/filter", api_key=api_key, json_body=body)
        if result is None:
            typer.echo("Nenhum lancamento encontrado.")
            return
        typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("bill")
def get_bill(
    bill_key: str = typer.Argument(help="Chave unica do lancamento financeiro."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Retorna os dados de um lancamento financeiro (conta) pelo seu identificador.

    EXAMPLES:
      aegro financial bill abc123
      aegro financial bill abc123 --output table
    """
    try:
        _, api_key = require_farm()
        client = get_client()
        result = client.get(f"/pub/v1/bills/{validate_key(bill_key)}", api_key=api_key)
        if result is None:
            typer.echo("Lancamento financeiro nao encontrado.")
            return
        typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("installment")
def get_installment(
    installment_key: str = typer.Argument(help="Chave unica da parcela."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Retorna os dados de uma parcela pelo seu identificador.

    EXAMPLES:
      aegro financial installment abc123
      aegro financial installment abc123 --output table
    """
    try:
        _, api_key = require_farm()
        client = get_client()
        result = client.get(
            f"/pub/v1/installments/{validate_key(installment_key)}", api_key=api_key
        )
        if result is None:
            typer.echo("Parcela nao encontrada.")
            return
        typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("installments")
def list_installments(
    operation_type: Optional[str] = typer.Option(
        None, "--operation-type", help="Tipo de operacao (REVENUE ou EXPENSE)."
    ),
    status: Optional[list[str]] = typer.Option(
        None, "--status", help="Status (PAID, NOT_PAID). Repetivel."
    ),
    due_date_start: Optional[str] = typer.Option(
        None, "--due-date-start", help="Data de vencimento inicial (YYYY-MM-DD)."
    ),
    due_date_end: Optional[str] = typer.Option(
        None, "--due-date-end", help="Data de vencimento final (YYYY-MM-DD)."
    ),
    bill_key: Optional[list[str]] = typer.Option(
        None, "--bill-key", help="Chave de lancamento (repetivel)."
    ),
    page: int = typer.Option(1, "--page", help="Numero da pagina."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Lista parcelas de lancamentos financeiros com filtros opcionais.

    EXAMPLES:
      aegro financial installments
      aegro financial installments --operation-type EXPENSE --status PAID
      aegro financial installments --due-date-start 2025-01-01 --due-date-end 2025-12-31
      aegro financial installments --bill-key b1 --bill-key b2 --page 2
    """
    try:
        _, api_key = require_farm()
        client = get_client()
        body: dict = {
            "requiredPageNumber": page,
            "maximumItemsPerPageCount": 50,
        }
        if operation_type is not None:
            body["operationType"] = operation_type
        if status:
            body["statuses"] = status
        if due_date_start is not None:
            body["dueDateStart"] = due_date_start
        if due_date_end is not None:
            body["dueDateEnd"] = due_date_end
        if bill_key:
            body["billKeys"] = bill_key
        result = client.post("/pub/v1/installments/filter", api_key=api_key, json_body=body)
        if result is None:
            typer.echo("Nenhuma parcela encontrada.")
            return
        typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("create-installment")
def create_installment(
    bill_key: str = typer.Option(..., "--bill-key", help="Chave do lancamento financeiro."),
    bank_account_key: str = typer.Option(
        ..., "--bank-account-key", help="Chave da conta bancaria."
    ),
    due_date: str = typer.Option(..., "--due-date", help="Data de vencimento (YYYY-MM-DD)."),
    amount: float = typer.Option(..., "--amount", help="Valor da parcela."),
    currency: str = typer.Option("BRL", "--currency", help="Moeda (default: BRL)."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Cria uma nova parcela para um lancamento financeiro.

    EXAMPLES:
      aegro financial create-installment --bill-key b1 --bank-account-key ba1 --due-date 2025-06-01 --amount 1500.00
      aegro financial create-installment --bill-key b1 --bank-account-key ba1 --due-date 2025-06-01 --amount 1500.00 --currency USD
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        body: dict = {
            "billKey": bill_key,
            "bankAccountKey": bank_account_key,
            "dueDate": due_date,
            "amount": {"amount": amount, "currency": currency},
        }
        result = client.post("/pub/v1/installments", api_key=api_key, json_body=body)
        if result is None:
            typer.echo(f"Parcela criada na {farm_name}.")
        else:
            typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("update-installment")
def update_installment(
    key: str = typer.Argument(help="Chave unica da parcela."),
    bill_key: str = typer.Option(..., "--bill-key", help="Chave do lancamento financeiro."),
    bank_account_key: str = typer.Option(
        ..., "--bank-account-key", help="Chave da conta bancaria."
    ),
    number: int = typer.Option(..., "--number", help="Numero sequencial da parcela."),
    due_date: str = typer.Option(..., "--due-date", help="Data de vencimento (YYYY-MM-DD)."),
    amount: float = typer.Option(..., "--amount", help="Valor da parcela."),
    currency: str = typer.Option("BRL", "--currency", help="Moeda (default: BRL)."),
    status: Optional[str] = typer.Option(None, "--status", help="Status (PAID ou NOT_PAID)."),
    realized_date: Optional[str] = typer.Option(
        None, "--realized-date", help="Data de pagamento (YYYY-MM-DD)."
    ),
    realized_amount: Optional[float] = typer.Option(
        None, "--realized-amount", help="Valor realizado."
    ),
    realized_currency: Optional[str] = typer.Option(
        None, "--realized-currency", help="Moeda do valor realizado."
    ),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Atualiza uma parcela existente.

    EXAMPLES:
      aegro financial update-installment i1 --bill-key b1 --bank-account-key ba1 --number 1 --due-date 2025-06-01 --amount 2000.00
      aegro financial update-installment i1 --bill-key b1 --bank-account-key ba1 --number 1 --due-date 2025-06-01 --amount 2000.00 --status PAID --realized-date 2025-05-30
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        body: dict = {
            "key": key,
            "billKey": bill_key,
            "bankAccountKey": bank_account_key,
            "number": number,
            "dueDate": due_date,
            "amount": {"amount": amount, "currency": currency},
        }
        if status is not None:
            body["status"] = status
        if realized_date is not None:
            body["realizedDate"] = realized_date
        if realized_amount is not None:
            body["realizedAmount"] = {
                "amount": realized_amount,
                "currency": realized_currency or currency,
            }
        result = client.put(
            f"/pub/v1/installments/{validate_key(key)}",
            api_key=api_key,
            json_body=body,
        )
        if result is None:
            typer.echo(f"Parcela atualizada na {farm_name}.")
        else:
            typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("delete-installment")
def delete_installment(
    key: str = typer.Argument(help="Chave unica da parcela."),
) -> None:
    """Exclui uma parcela pelo seu identificador.

    EXAMPLES:
      aegro financial delete-installment i1
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        client.delete(f"/pub/v1/installments/{validate_key(key)}", api_key=api_key)
        typer.echo(f"Parcela excluida da {farm_name}.")
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("create-bill")
def create_bill(
    farm_key: str = typer.Option(..., "--farm-key", help="Chave da fazenda."),
    description: str = typer.Option(..., "--description", help="Descricao do lancamento."),
    entry_date: str = typer.Option(..., "--entry-date", help="Data do lancamento (YYYY-MM-DD)."),
    cash_flow: str = typer.Option(..., "--cash-flow", help="Tipo: REVENUE ou EXPENSE."),
    payment_method: str = typer.Option(
        ..., "--payment-method", help="PROMPT|INSTALLMENT|NO_PAYMENT|UNKNOWN."
    ),
    financial_category_key: str = typer.Option(
        ..., "--financial-category-key", help="Chave da categoria financeira."
    ),
    total_amount: float = typer.Option(..., "--total-amount", help="Valor total."),
    currency: str = typer.Option("BRL", "--currency", help="Moeda (default: BRL)."),
    bank_account_key: Optional[str] = typer.Option(
        None, "--bank-account-key", help="Chave da conta bancaria."
    ),
    company_key: Optional[str] = typer.Option(
        None, "--company-key", help="Chave da empresa/fornecedor."
    ),
    receipt: Optional[str] = typer.Option(None, "--receipt", help="Numero do recibo/nota fiscal."),
    nfe_access_key: Optional[str] = typer.Option(
        None, "--nfe-access-key", help="Chave de acesso da NF-e."
    ),
    tags: Optional[list[str]] = typer.Option(None, "--tag", help="Tag (repetivel)."),
    installments: Optional[str] = typer.Option(
        None, "--installments", help="Lista de parcelas como string JSON."
    ),
    inputs: Optional[str] = typer.Option(
        None, "--inputs", help="Lista de insumos como string JSON."
    ),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Cria um novo lancamento financeiro (bill).

    EXAMPLES:
      aegro financial create-bill --farm-key f1 --description "Compra" --entry-date 2025-06-01 \\
        --cash-flow EXPENSE --payment-method PROMPT --financial-category-key fc1 --total-amount 1500.00
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        body: dict = {
            "farmKey": farm_key,
            "description": description,
            "entryDate": entry_date,
            "cashFlow": cash_flow,
            "paymentMethod": payment_method,
            "financialCategoryKey": financial_category_key,
            "totalAmount": {"amount": total_amount, "currency": currency},
        }
        if bank_account_key is not None:
            body["bankAccountKey"] = bank_account_key
        if company_key is not None:
            body["companyKey"] = company_key
        if receipt is not None:
            body["receipt"] = receipt
        if nfe_access_key is not None:
            body["nfeAccessKey"] = nfe_access_key
        if tags:
            body["tags"] = tags
        parsed_installments = _parse_json_option(installments, "--installments")
        if parsed_installments is not None:
            if not isinstance(parsed_installments, list):
                typer.echo("--installments deve ser uma lista JSON.", err=True)
                raise SystemExit(4)
            body["installments"] = parsed_installments
        parsed_inputs = _parse_json_option(inputs, "--inputs")
        if parsed_inputs is not None:
            if not isinstance(parsed_inputs, list):
                typer.echo("--inputs deve ser uma lista JSON.", err=True)
                raise SystemExit(4)
            body["inputs"] = parsed_inputs
        result = client.post("/pub/v1/bills", api_key=api_key, json_body=body)
        if result is None:
            typer.echo(f"Lancamento criado na {farm_name}.")
        else:
            typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("update-bill")
def update_bill(
    bill_key: str = typer.Argument(help="Chave do lancamento financeiro."),
    body_json: str = typer.Option(
        ...,
        "--body",
        help="Corpo da requisicao como JSON. Use a estrutura de BillSaveRequestPublicResource.",
    ),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Atualiza um lancamento financeiro existente (PUT completo).

    O payload segue BillSaveRequestPublicResource (ver swagger).

    EXAMPLES:
      aegro financial update-bill bill::abc --body '{"farmKey":"farm::f1",...}'
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        parsed = _parse_json_option(body_json, "--body")
        if not isinstance(parsed, dict):
            typer.echo("--body deve ser um objeto JSON.", err=True)
            raise SystemExit(4)
        result = client.put(
            f"/pub/v1/bills/{validate_key(bill_key)}", api_key=api_key, json_body=parsed
        )
        if result is None:
            typer.echo(f"Lancamento atualizado na {farm_name}.")
        else:
            typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("closes")
def get_financial_closes(
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Obtem o fechamento financeiro da fazenda selecionada.

    EXAMPLES:
      aegro financial closes
      aegro financial closes --output table
    """
    try:
        _, api_key = require_farm()
        client = get_client()
        result = client.get("/pub/v1/financial-closes", api_key=api_key)
        if result is None:
            typer.echo("Nenhum fechamento financeiro encontrado.")
            return
        typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)


@financial_app.command("realize")
def realize_installments(
    key: list[str] = typer.Option(..., "--key", help="Chave da parcela (repetivel, obrigatorio)."),
    output: OutputFormat = typer.Option(
        OutputFormat.json, "--output", "-o", help="Formato: json|table|csv"
    ),
) -> None:
    """Realiza (paga) uma lista de parcelas de uma vez.

    EXAMPLES:
      aegro financial realize --key i1 --key i2
    """
    try:
        farm_name, api_key = require_farm()
        client = get_client()
        body: dict = {"list": key}
        result = client.post("/pub/v1/installments/realizeList", api_key=api_key, json_body=body)
        if result is None:
            typer.echo(f"Parcelas realizadas na {farm_name}.")
        else:
            typer.echo(format_output(result, output))
    except (SystemExit, KeyboardInterrupt):
        raise
    except Exception as exc:
        handle_error(exc)
