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.database_service import DatabaseService router = APIRouter() database_service = DatabaseService() @router.get("/accounts", response_model=APIResponse) async def get_all_accounts() -> APIResponse: """Get all connected accounts from database""" try: accounts = [] # Get all account details from database db_accounts = await database_service.get_accounts_from_db() # Process accounts found in database for db_account in db_accounts: try: # Get latest balances from database for this account balances_data = await database_service.get_balances_from_db( db_account["id"] ) # Process balances balances = [] for balance in balances_data: balances.append( AccountBalance( amount=balance["amount"], currency=balance["currency"], balance_type=balance["type"], last_change_date=balance.get("timestamp"), ) ) accounts.append( AccountDetails( id=db_account["id"], institution_id=db_account["institution_id"], status=db_account["status"], iban=db_account.get("iban"), name=db_account.get("name"), currency=db_account.get("currency"), created=db_account["created"], last_accessed=db_account.get("last_accessed"), balances=balances, ) ) except Exception as e: logger.error( f"Failed to process database account {db_account['id']}: {e}" ) continue return APIResponse( success=True, data=accounts, message=f"Retrieved {len(accounts)} accounts from database", ) 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 from database""" try: # Get account details from database db_account = await database_service.get_account_details_from_db(account_id) if not db_account: raise HTTPException( status_code=404, detail=f"Account {account_id} not found in database" ) # Get latest balances from database for this account balances_data = await database_service.get_balances_from_db(account_id) # Process balances balances = [] for balance in balances_data: balances.append( AccountBalance( amount=balance["amount"], currency=balance["currency"], balance_type=balance["type"], last_change_date=balance.get("timestamp"), ) ) account = AccountDetails( id=db_account["id"], institution_id=db_account["institution_id"], status=db_account["status"], iban=db_account.get("iban"), name=db_account.get("name"), currency=db_account.get("currency"), created=db_account["created"], last_accessed=db_account.get("last_accessed"), balances=balances, ) return APIResponse( success=True, data=account, message=f"Account details retrieved from database for {account_id}", ) except HTTPException: raise except Exception as e: logger.error(f"Failed to get account details for {account_id}: {e}") raise HTTPException( status_code=500, detail=f"Failed to get account details: {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 from database""" try: # Get balances from database instead of GoCardless API db_balances = await database_service.get_balances_from_db(account_id=account_id) balances = [] for balance in db_balances: balances.append( AccountBalance( amount=balance["amount"], currency=balance["currency"], balance_type=balance["type"], last_change_date=balance.get("timestamp"), ) ) 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 from database for account {account_id}: {e}" ) raise HTTPException( status_code=404, detail=f"Failed to get balances: {str(e)}" ) from e @router.get("/balances", response_model=APIResponse) async def get_all_balances() -> APIResponse: """Get all balances from all accounts in database""" try: # Get all accounts first to iterate through them db_accounts = await database_service.get_accounts_from_db() all_balances = [] for db_account in db_accounts: try: # Get balances for this account db_balances = await database_service.get_balances_from_db( account_id=db_account["id"] ) # Process balances and add account info for balance in db_balances: balance_data = { "id": f"{db_account['id']}_{balance['type']}", # Create unique ID "account_id": db_account["id"], "balance_amount": balance["amount"], "balance_type": balance["type"], "currency": balance["currency"], "reference_date": balance.get( "timestamp", db_account.get("last_accessed") ), "created_at": db_account.get("created"), "updated_at": db_account.get("last_accessed"), } all_balances.append(balance_data) except Exception as e: logger.error( f"Failed to get balances for account {db_account['id']}: {e}" ) continue return APIResponse( success=True, data=all_balances, message=f"Retrieved {len(all_balances)} balances from {len(db_accounts)} accounts", ) except Exception as e: logger.error(f"Failed to get all balances: {e}") raise HTTPException( status_code=500, 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 from database""" try: # Get transactions from database instead of GoCardless API db_transactions = await database_service.get_transactions_from_db( account_id=account_id, limit=limit, offset=offset, ) # Get total count for pagination info total_transactions = await database_service.get_transaction_count_from_db( account_id=account_id, ) 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 db_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 db_transactions ] actual_offset = offset or 0 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 from database for account {account_id}: {e}" ) raise HTTPException( status_code=404, detail=f"Failed to get transactions: {str(e)}" ) from e