from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db.models import Q, Sum
from django.db import transaction as db_transaction
from datetime import datetime, date
from savings.models import SavingAccount, SavingAccountTransactions
from savings.savings_bal_helper import get_account_balance
from savings.savings_helper import (
    assert_savings_account_can_credit,
    assert_savings_account_can_debit,
    mark_savings_account_for_aml_deposit,
)
from ledgers.models import SystemTransactions, OrganisationSubAccount
from ledgers.ledgers_helper import generate_reference_no
from customers.models import Customer
from questbanker_api.utils import get_current_user


class OfficerSavingsAccountsListView(APIView):
    """Get all savings accounts for officer"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        organisation_id = get_current_user(request, 'organisation_id', None)
        branch_id = request.GET.get('branch_id')
        account_status = request.GET.get('status', 'active')
        search = request.GET.get('search', '')

        filters = {'status': account_status}
        if organisation_id:
            filters['customer_branch__branch_organisation__id'] = organisation_id
        if branch_id:
            filters['customer_branch__id'] = branch_id

        accounts = SavingAccount.objects.filter(**filters).select_related(
            'account_customer', 'account_product', 'customer_branch'
        )

        if search:
            accounts = accounts.filter(
                Q(account_no__icontains=search) |
                Q(account_customer__name__icontains=search) |
                Q(account_customer__member_number__icontains=search)
            )

        results = []
        for account in accounts:
            balance_info = get_account_balance(account)
            balance = balance_info.get('balance', 0)
            if isinstance(balance, str):
                balance = float(balance.replace(',', ''))
            else:
                balance = float(balance)

            available_balance = balance_info.get('available_balance', 0)
            if isinstance(available_balance, str):
                available_balance = float(available_balance.replace(',', ''))
            else:
                available_balance = float(available_balance)

            blocked_amount = balance_info.get('blocked_amount', 0)
            if isinstance(blocked_amount, str):
                blocked_amount = float(blocked_amount.replace(',', ''))
            else:
                blocked_amount = float(blocked_amount)

            last_trans = SavingAccountTransactions.objects.filter(
                customer_account=account
            ).order_by('-transaction__record_date').first()

            results.append({
                'account_id': account.id,
                'account_number': account.account_no,
                'customer_id': account.account_customer.id,
                'customer_name': account.account_customer.name,
                'customer_member_number': account.account_customer.member_number,
                'product_id': account.account_product.id,
                'product_name': account.account_product.product_name,
                'current_balance': balance,
                'available_balance': available_balance,
                'blocked_amount': blocked_amount,
                'currency': 'UGX',
                'status': account.status,
                'interest_rate': 0.0,  # Interest is in separate SavingProductInterest model
                'date_opened': account.date_added.date(),
                'last_transaction_date': last_trans.transaction.record_date if last_trans else None,
                'branch_id': account.customer_branch.id,
                'branch_name': account.customer_branch.name
            })

        return Response({
            'status': True,
            'message': 'Savings accounts retrieved successfully',
            'data': {
                'count': len(results),
                'results': results
            }
        }, status=status.HTTP_200_OK)


class OfficerSavingsAccountDetailView(APIView):
    """Get detailed information about a specific savings account"""
    permission_classes = [IsAuthenticated]

    def get(self, request, account_id):
        try:
            account = SavingAccount.objects.select_related(
                'account_customer', 'account_product', 'customer_branch'
            ).get(id=account_id)
        except SavingAccount.DoesNotExist:
            return Response({
                'status': False,
                'message': 'Account not found'
            }, status=status.HTTP_404_NOT_FOUND)

        balance_info = get_account_balance(account)
        
        def to_float(val):
            if isinstance(val, str):
                return float(val.replace(',', ''))
            return float(val or 0)

        customer = account.account_customer
        product = account.account_product

        return Response({
            'status': True,
            'message': 'Account details retrieved successfully',
            'data': {
                'account_id': account.id,
                'account_number': account.account_no,
                'customer': {
                    'customer_id': customer.id,
                    'name': customer.name,
                    'member_number': customer.member_number,
                    'phone': customer.telephone,
                    'email': customer.email,
                    'id_number': customer.id_number
                },
                'product': {
                    'product_id': product.id,
                    'product_name': product.product_name,
                    'product_code': '',  # Field doesn't exist in model
                    'minimum_balance': float(product.min_balance or 0),
                    'interest_rate': 0.0,  # Interest is in separate SavingProductInterest model
                    'interest_calculation_method': ''  # Field doesn't exist in model
                },
                'balances': {
                    'current_balance': to_float(balance_info.get('balance', 0)),
                    'available_balance': to_float(balance_info.get('available_balance', 0)),
                    'blocked_amount': to_float(balance_info.get('blocked_amount', 0)),
                    'pending_deposits': 0.0,
                    'pending_withdrawals': 0.0
                },
                'account_info': {
                    'currency': 'UGX',
                    'status': account.status,
                    'date_opened': account.date_added.date(),
                    'last_transaction_date': None,
                    'branch_id': account.customer_branch.id,
                    'branch_name': account.customer_branch.name
                },
                'limits': {
                    'daily_withdrawal_limit': 1000000.0,  # Default value, field doesn't exist
                    'minimum_withdrawal': float(product.min_withdraws or 5000),
                    'minimum_deposit': float(product.opening_balance or 1000)
                }
            }
        }, status=status.HTTP_200_OK)


class OfficerSavingsDepositView(APIView):
    """Register a deposit transaction"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        from ledgers.models import CashAccounts
        
        account_id = request.data.get('account_id')
        amount = request.data.get('amount')
        transaction_date = request.data.get('transaction_date')
        debit_chart = request.data.get('debit_chart')  # Can be CashAccounts ID or OrganisationSubAccount ID
        payment_method = request.data.get('payment_method', 'cash')
        reference_number = request.data.get('reference_number', '')
        notes = request.data.get('notes', '')

        # Validation
        if not all([account_id, amount, transaction_date, debit_chart]):
            return Response({
                'status': False,
                'message': 'account_id, amount, transaction_date, and debit_chart are required'
            }, status=status.HTTP_400_BAD_REQUEST)

        try:
            account = SavingAccount.objects.get(id=account_id)
        except SavingAccount.DoesNotExist:
            return Response({
                'status': False,
                'message': 'Account not found'
            }, status=status.HTTP_404_NOT_FOUND)

        assert_savings_account_can_credit(account, 'deposits', as_at=transaction_date)

        # Try to get OrganisationSubAccount - first check if it's a CashAccounts ID
        cash_account = None
        try:
            cash_account = OrganisationSubAccount.objects.get(id=debit_chart)
        except OrganisationSubAccount.DoesNotExist:
            # Try to find it in CashAccounts table and get the chart
            try:
                cash_acc = CashAccounts.objects.get(id=debit_chart)
                cash_account = cash_acc.chart
            except CashAccounts.DoesNotExist:
                return Response({
                    'status': False,
                    'message': 'Cash account not found'
                }, status=status.HTTP_404_NOT_FOUND)

        if account.status != 'active':
            return Response({
                'status': False,
                'message': 'Account is not active',
                'errors': {'account_id': ['Account is closed or inactive']}
            }, status=status.HTTP_400_BAD_REQUEST)

        amount = float(amount)
        if amount <= 0:
            return Response({
                'status': False,
                'message': 'Invalid amount',
                'errors': {'amount': ['Amount must be greater than 0']}
            }, status=status.HTTP_400_BAD_REQUEST)

        min_deposit = float(account.account_product.opening_balance or 0)
        if amount < min_deposit:
            return Response({
                'status': False,
                'message': 'Amount below minimum deposit',
                'errors': {'amount': [f'Amount must be at least UGX {min_deposit:,.0f}']}
            }, status=status.HTTP_400_BAD_REQUEST)

        balance_info = get_account_balance(account)
        previous_balance = balance_info.get('balance', 0)
        if isinstance(previous_balance, str):
            previous_balance = float(previous_balance.replace(',', ''))
        else:
            previous_balance = float(previous_balance)

        # Generate reference number
        organisation_id = get_current_user(request, 'organisation_id', None)
        reference_number = generate_reference_no(
            account.account_product.accounts_chart.account_line,
            organisation_id,
            'dep'
        )

        # Create transaction
        with db_transaction.atomic():
            system_trans = SystemTransactions.objects.create(
                heading=f"Deposit - {account.account_no} - {account.account_customer.name}",
                amount=amount,
                record_date=transaction_date,
                debit_chart=cash_account,
                credit_chart=account.account_product.accounts_chart,
                reference_no=reference_number,
                payment_method=payment_method,
                coment=notes,
                branch=account.customer_branch,
                added_by=request.user
            )

            save_trans = SavingAccountTransactions.objects.create(
                customer_account=account,
                transaction=system_trans,
                transaction_type='deposit',
                deleted=False
            )

            mark_savings_account_for_aml_deposit(
                account,
                amount,
                transaction=system_trans,
            )

            new_balance = previous_balance + amount

        return Response({
            'status': True,
            'message': 'Deposit registered successfully',
            'data': {
                'transaction_id': system_trans.id,
                'transaction_number': f"DEP-{system_trans.id}",
                'account_id': account.id,
                'account_number': account.account_no,
                'customer_name': account.account_customer.name,
                'transaction_type': 'deposit',
                'amount': amount,
                'previous_balance': previous_balance,
                'new_balance': new_balance,
                'transaction_date': transaction_date,
                'transaction_time': system_trans.record_date,
                'payment_method': payment_method,
                'reference_number': reference_number,
                'processed_by': {
                    'officer_id': request.user.id,
                    'officer_name': request.user.user_staff.name if hasattr(request.user, 'user_staff') else request.user.username,
                    'username': request.user.username
                },
                'status': 'completed'
            }
        }, status=status.HTTP_201_CREATED)


class OfficerSavingsWithdrawalView(APIView):
    """Register a withdrawal transaction"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        from ledgers.models import CashAccounts
        
        account_id = request.data.get('account_id')
        amount = request.data.get('amount')
        transaction_date = request.data.get('transaction_date')
        credit_chart = request.data.get('credit_chart')  # Can be CashAccounts ID or OrganisationSubAccount ID
        payment_method = request.data.get('payment_method', 'cash')
        reference_number = request.data.get('reference_number', '')
        notes = request.data.get('notes', '')

        if not all([account_id, amount, transaction_date, credit_chart]):
            return Response({
                'status': False,
                'message': 'account_id, amount, transaction_date, and credit_chart are required'
            }, status=status.HTTP_400_BAD_REQUEST)

        try:
            account = SavingAccount.objects.get(id=account_id)
        except SavingAccount.DoesNotExist:
            return Response({
                'status': False,
                'message': 'Account not found'
            }, status=status.HTTP_404_NOT_FOUND)

        assert_savings_account_can_debit(account, 'withdrawals', as_at=transaction_date)

        # Try to get OrganisationSubAccount - first check if it's a CashAccounts ID
        cash_account = None
        try:
            cash_account = OrganisationSubAccount.objects.get(id=credit_chart)
        except OrganisationSubAccount.DoesNotExist:
            # Try to find it in CashAccounts table and get the chart
            try:
                cash_acc = CashAccounts.objects.get(id=credit_chart)
                cash_account = cash_acc.chart
            except CashAccounts.DoesNotExist:
                return Response({
                    'status': False,
                    'message': 'Cash account not found'
                }, status=status.HTTP_404_NOT_FOUND)

        if account.status != 'active':
            return Response({
                'status': False,
                'message': 'Account is not active'
            }, status=status.HTTP_400_BAD_REQUEST)

        amount = float(amount)
        if amount <= 0:
            return Response({
                'status': False,
                'message': 'Invalid amount'
            }, status=status.HTTP_400_BAD_REQUEST)

        balance_info = get_account_balance(account)
        available_balance = balance_info.get('balance_raw', 0)
        if isinstance(available_balance, str):
            available_balance = float(available_balance.replace(',', ''))
        else:
            available_balance = float(available_balance)

        if amount > available_balance:
            return Response({
                'status': False,
                'message': 'Insufficient balance',
                'errors': {'amount': [f'Insufficient balance. Available: UGX {available_balance:,.0f}']}
            }, status=status.HTTP_400_BAD_REQUEST)

        min_withdrawal = float(account.account_product.min_withdraws or 0)
        if amount < min_withdrawal:
            return Response({
                'status': False,
                'message': 'Amount below minimum withdrawal',
                'errors': {'amount': [f'Amount must be at least UGX {min_withdrawal:,.0f}']}
            }, status=status.HTTP_400_BAD_REQUEST)

        previous_balance = balance_info.get('balance', 0)
        if isinstance(previous_balance, str):
            previous_balance = float(previous_balance.replace(',', ''))
        else:
            previous_balance = float(previous_balance)

        # Generate reference number
        organisation_id = get_current_user(request, 'organisation_id', None)
        reference_number = generate_reference_no(
            account.account_product.accounts_chart.account_line,
            organisation_id,
            'wd'
        )

        # Create transaction
        with db_transaction.atomic():
            system_trans = SystemTransactions.objects.create(
                heading=f"Withdrawal - {account.account_no} - {account.account_customer.name}",
                amount=amount,
                record_date=transaction_date,
                debit_chart=account.account_product.accounts_chart,
                credit_chart=cash_account,
                reference_no=reference_number,
                payment_method=payment_method,
                coment=notes,
                branch=account.customer_branch,
                added_by=request.user
            )

            SavingAccountTransactions.objects.create(
                customer_account=account,
                transaction=system_trans,
                transaction_type='withdrawal',
                deleted=False
            )

            new_balance = previous_balance - amount

        return Response({
            'status': True,
            'message': 'Withdrawal registered successfully',
            'data': {
                'transaction_id': system_trans.id,
                'transaction_number': f"WTH-{system_trans.id}",
                'account_id': account.id,
                'account_number': account.account_no,
                'customer_name': account.account_customer.name,
                'transaction_type': 'withdrawal',
                'amount': amount,
                'previous_balance': previous_balance,
                'new_balance': new_balance,
                'transaction_date': transaction_date,
                'transaction_time': system_trans.record_date,
                'payment_method': payment_method,
                'reference_number': reference_number,
                'processed_by': {
                    'officer_id': request.user.id,
                    'officer_name': request.user.user_staff.name if hasattr(request.user, 'user_staff') else request.user.username,
                    'username': request.user.username
                },
                'status': 'completed'
            }
        }, status=status.HTTP_201_CREATED)


class OfficerSavingsDailyTransactionsView(APIView):
    """Get daily transactions"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        trans_date = request.GET.get('date', date.today().strftime('%Y-%m-%d'))
        branch_id = request.GET.get('branch_id') or get_current_user(request, 'organisation_branch_id', None)

        filters = {'transaction__record_date__date': trans_date}
        if branch_id:
            filters['customer_account__customer_branch__id'] = branch_id

        transactions = SavingAccountTransactions.objects.filter(**filters).select_related(
            'customer_account__account_customer',
            'customer_account__customer_branch',
            'transaction__transaction_added_by__user_staff'
        ).order_by('-transaction__record_date')

        results = []
        for trans in transactions:
            chart_id = trans.customer_account.account_product.accounts_chart.id
            is_credit = trans.transaction.credit_chart.id == chart_id
            
            results.append({
                'transaction_id': trans.transaction.id,
                'transaction_number': f"TXN-{trans.transaction.id}",
                'transaction_type': 'deposit' if is_credit else 'withdrawal',
                'account_id': trans.customer_account.id,
                'account_number': trans.customer_account.account_no,
                'customer_id': trans.customer_account.account_customer.id,
                'customer_name': trans.customer_account.account_customer.name,
                'customer_member_number': trans.customer_account.account_customer.member_number,
                'amount': float(trans.transaction.amount),
                'transaction_date': trans.transaction.record_date.date(),
                'transaction_time': trans.transaction.record_date,
                'processed_by': {
                    'officer_id': trans.transaction.transaction_added_by.id,
                    'officer_name': trans.transaction.transaction_added_by.user_staff.name if hasattr(trans.transaction.transaction_added_by, 'user_staff') else trans.transaction.transaction_added_by.username,
                    'username': trans.transaction.transaction_added_by.username
                },
                'branch_id': trans.customer_account.customer_branch.id,
                'branch_name': trans.customer_account.customer_branch.name,
                'status': 'completed',
                'notes': trans.transaction.heading
            })

        # Calculate summary
        deposits = [t for t in results if t['transaction_type'] == 'deposit']
        withdrawals = [t for t in results if t['transaction_type'] == 'withdrawal']
        
        summary = {
            'total_transactions': len(results),
            'total_deposits': len(deposits),
            'total_withdrawals': len(withdrawals),
            'total_deposit_amount': sum(t['amount'] for t in deposits),
            'total_withdrawal_amount': sum(t['amount'] for t in withdrawals),
            'net_amount': sum(t['amount'] for t in deposits) - sum(t['amount'] for t in withdrawals)
        }

        return Response({
            'status': True,
            'message': 'Daily transactions retrieved successfully',
            'data': {
                'date': trans_date,
                'summary': summary,
                'count': len(results),
                'results': results
            }
        }, status=status.HTTP_200_OK)


class OfficerSavingsAccountTransactionsView(APIView):
    """Get transaction history for a specific account"""
    permission_classes = [IsAuthenticated]

    def get(self, request, account_id):
        try:
            account = SavingAccount.objects.get(id=account_id)
        except SavingAccount.DoesNotExist:
            return Response({
                'status': False,
                'message': 'Account not found'
            }, status=status.HTTP_404_NOT_FOUND)

        transactions = SavingAccountTransactions.objects.filter(
            customer_account=account
        ).select_related('transaction').order_by('-transaction__record_date')

        results = []
        for trans in transactions:
            chart_id = account.account_product.accounts_chart.id
            is_credit = trans.transaction.credit_chart.id == chart_id
            
            results.append({
                'transaction_id': trans.transaction.id,
                'transaction_number': f"TXN-{trans.transaction.id}",
                'transaction_type': 'deposit' if is_credit else 'withdrawal',
                'amount': float(trans.transaction.amount),
                'transaction_date': trans.transaction.record_date.date(),
                'transaction_time': trans.transaction.record_date,
                'processed_by': trans.transaction.transaction_added_by.username,
                'status': 'completed',
                'notes': trans.transaction.heading
            })

        balance_info = get_account_balance(account)
        current_balance = balance_info.get('balance', 0)
        if isinstance(current_balance, str):
            current_balance = float(current_balance.replace(',', ''))
        else:
            current_balance = float(current_balance)

        return Response({
            'status': True,
            'message': 'Transaction history retrieved successfully',
            'data': {
                'account_id': account.id,
                'account_number': account.account_no,
                'customer_name': account.account_customer.name,
                'current_balance': current_balance,
                'count': len(results),
                'results': results
            }
        }, status=status.HTTP_200_OK)
