from typing import Optional, List, Union from fastapi import APIRouter, HTTPException, Query from loguru import logger from leggend.api.models.common import APIResponse from leggend.api.models.accounts import ( AccountDetails, AccountBalance, Transaction, TransactionSummary, ) from leggend.services.gocardless_service import GoCardlessService from leggend.services.database_service import DatabaseService router = APIRouter() gocardless_service = GoCardlessService() database_service = DatabaseService() @router.get("/accounts", response_model=APIResponse) async def get_all_accounts() -> APIResponse: """Get all connected accounts""" try: requisitions_data = await gocardless_service.get_requisitions() all_accounts = set() for req in requisitions_data.get("results", []): all_accounts.update(req.get("accounts", [])) accounts = [] for account_id in all_accounts: try: account_details = await gocardless_service.get_account_details( account_id ) balances_data = await gocardless_service.get_account_balances( account_id ) # Process balances balances = [] for balance in balances_data.get("balances", []): balance_amount = balance["balanceAmount"] balances.append( AccountBalance( amount=float(balance_amount["amount"]), currency=balance_amount["currency"], balance_type=balance["balanceType"], last_change_date=balance.get("lastChangeDateTime"), ) ) accounts.append( AccountDetails( id=account_details["id"], institution_id=account_details["institution_id"], status=account_details["status"], iban=account_details.get("iban"), name=account_details.get("name"), currency=account_details.get("currency"), created=account_details["created"], last_accessed=account_details.get("last_accessed"), balances=balances, ) ) except Exception as e: logger.error(f"Failed to get details for account {account_id}: {e}") continue return APIResponse( success=True, data=accounts, message=f"Retrieved {len(accounts)} accounts" ) except Exception as e: logger.error(f"Failed to get accounts: {e}") raise HTTPException( status_code=500, detail=f"Failed to get accounts: {str(e)}" ) from e @router.get("/accounts/{account_id}", response_model=APIResponse) async def get_account_details(account_id: str) -> APIResponse: """Get details for a specific account""" try: account_details = await gocardless_service.get_account_details(account_id) balances_data = await gocardless_service.get_account_balances(account_id) # Process balances balances = [] for balance in balances_data.get("balances", []): balance_amount = balance["balanceAmount"] balances.append( AccountBalance( amount=float(balance_amount["amount"]), currency=balance_amount["currency"], balance_type=balance["balanceType"], last_change_date=balance.get("lastChangeDateTime"), ) ) account = AccountDetails( id=account_details["id"], institution_id=account_details["institution_id"], status=account_details["status"], iban=account_details.get("iban"), name=account_details.get("name"), currency=account_details.get("currency"), created=account_details["created"], last_accessed=account_details.get("last_accessed"), balances=balances, ) return APIResponse( success=True, data=account, message=f"Account details retrieved for {account_id}", ) except Exception as e: logger.error(f"Failed to get account details for {account_id}: {e}") raise HTTPException( status_code=404, detail=f"Account not found: {str(e)}" ) from e @router.get("/accounts/{account_id}/balances", response_model=APIResponse) async def get_account_balances(account_id: str) -> APIResponse: """Get balances for a specific account""" try: balances_data = await gocardless_service.get_account_balances(account_id) balances = [] for balance in balances_data.get("balances", []): balance_amount = balance["balanceAmount"] balances.append( AccountBalance( amount=float(balance_amount["amount"]), currency=balance_amount["currency"], balance_type=balance["balanceType"], last_change_date=balance.get("lastChangeDateTime"), ) ) return APIResponse( success=True, data=balances, message=f"Retrieved {len(balances)} balances for account {account_id}", ) except Exception as e: logger.error(f"Failed to get balances for account {account_id}: {e}") raise HTTPException( status_code=404, detail=f"Failed to get balances: {str(e)}" ) from e @router.get("/accounts/{account_id}/transactions", response_model=APIResponse) async def get_account_transactions( account_id: str, limit: Optional[int] = Query(default=100, le=500), offset: Optional[int] = Query(default=0, ge=0), summary_only: bool = Query( default=False, description="Return transaction summaries only" ), ) -> APIResponse: """Get transactions for a specific account""" try: account_details = await gocardless_service.get_account_details(account_id) transactions_data = await gocardless_service.get_account_transactions( account_id ) # Process transactions processed_transactions = database_service.process_transactions( account_id, account_details, transactions_data ) # Apply pagination total_transactions = len(processed_transactions) actual_offset = offset or 0 actual_limit = limit or 100 paginated_transactions = processed_transactions[ actual_offset : actual_offset + actual_limit ] data: Union[List[TransactionSummary], List[Transaction]] if summary_only: # Return simplified transaction summaries data = [ TransactionSummary( internal_transaction_id=txn["internalTransactionId"], date=txn["transactionDate"], description=txn["description"], amount=txn["transactionValue"], currency=txn["transactionCurrency"], status=txn["transactionStatus"], account_id=txn["accountId"], ) for txn in paginated_transactions ] else: # Return full transaction details data = [ Transaction( internal_transaction_id=txn["internalTransactionId"], institution_id=txn["institutionId"], iban=txn["iban"], account_id=txn["accountId"], transaction_date=txn["transactionDate"], description=txn["description"], transaction_value=txn["transactionValue"], transaction_currency=txn["transactionCurrency"], transaction_status=txn["transactionStatus"], raw_transaction=txn["rawTransaction"], ) for txn in paginated_transactions ] return APIResponse( success=True, data=data, message=f"Retrieved {len(data)} transactions (showing {actual_offset + 1}-{actual_offset + len(data)} of {total_transactions})", ) except Exception as e: logger.error(f"Failed to get transactions for account {account_id}: {e}") raise HTTPException( status_code=404, detail=f"Failed to get transactions: {str(e)}" ) from e