#!/usr/bin/env python3
"""
Data mapper for NEFT SFTP feed.
Maps parsed NEFT transactions (dicts) to NEFTInwardRecord for database insertion.
- No padding of account numbers (kept as-is, trimmed).
"""

from datetime import datetime
from decimal import Decimal
from typing import Dict, Any, List

from logging_config import get_logger
from db.models import NEFTInwardRecord  # Ensure NEFTInwardRecord has bank_code field

logger = get_logger(__name__)


class NEFTDataMapper:
    """Maps parsed NEFT transactions to NEFTInwardRecord objects."""

    # -------------------------
    # Helpers
    # -------------------------

    @staticmethod
    def convert_date(date_str: str) -> str:
        """
        Convert NEFT date (YYYYMMDD) to DDMMYYYY.

        Input : '20260306'
        Output: '06032026'

        On error, returns today's date in DDMMYYYY format.
        """
        try:
            if not date_str or len(date_str.strip()) != 8 or not date_str.isdigit():
                raise ValueError(f"Invalid NEFT date format: {date_str!r}")
            dt = datetime.strptime(date_str, "%Y%m%d")
            return dt.strftime("%d%m%Y")
        except Exception as e:
            logger.error(f"Error converting date '{date_str}': {e}")
            return datetime.now().strftime("%d%m%Y")

    @staticmethod
    def calculate_txnind(amount_in: Any) -> str:
        """
        Calculate transaction indicator from amount.
        'CR' for credit (>= 0), 'DR' for debit (< 0).

        Accepts Decimal or string. Defaults to 'CR' on error.
        """
        try:
            if isinstance(amount_in, Decimal):
                amount = amount_in
            else:
                txt = (str(amount_in) or "").strip()
                amount = Decimal(txt) if txt else Decimal("0")
            return 'DR' if amount < 0 else 'CR'
        except Exception as e:
            logger.error(f"Error calculating TXNIND for amount '{amount_in}': {e}")
            return 'CR'

    @staticmethod
    def process_status(status: str) -> str:
        """
        Normalize status field.

        - If contains 'processed' (case-insensitive) -> 'Processed'
        - If equals 'PROS' (common NEFT code)        -> 'Processed'
        - If equals 'WAIT'                            -> 'Waiting'
        - Else return original status (trimmed)
        """
        try:
            if not status:
                return ''
            s = status.strip()
            sl = s.lower()
            if 'processed' in sl or s.upper() == 'PROS':
                return 'PROCESSED'
            if s.upper() == 'WAIT':
                return 'Waiting'
            return s
        except Exception as e:
            logger.error(f"Error processing status: {e}")
            return status

    @staticmethod
    def convert_amount(amount_in: Any) -> Decimal:
        """
        Convert amount to Decimal and return absolute value.
        Use TXNIND to capture the sign semantics.
        """
        try:
            if isinstance(amount_in, Decimal):
                val = amount_in
            else:
                txt = (str(amount_in) or '').replace(',', '').strip()
                val = Decimal(txt) if txt else Decimal('0')
            return abs(val)
        except Exception as e:
            logger.error(f"Error converting amount '{amount_in}': {e}")
            return Decimal('0')

    # -------------------------
    # Mapping
    # -------------------------

    @classmethod
    def map_transaction(cls, parsed_txn: Dict[str, Any], bankcode: str) -> NEFTInwardRecord:
        """
        Map a single parsed NEFT transaction (dict) to NEFTInwardRecord.

        Args:
            parsed_txn: Dict emitted by SFTPUtrParser
            bankcode  : Bank code for this transaction (mapped to NEFTInwardRecord.bank_code)
        """
        try:
            # Amount handling
            amount_in = parsed_txn.get('amount', '0')
            txn_amt = cls.convert_amount(amount_in)
            txnind = cls.calculate_txnind(amount_in)

            # Date handling
            txn_date_raw = parsed_txn.get('tran_date', '') or ''
            txn_date_ddmmyyyy = cls.convert_date(txn_date_raw)

            # Account numbers: NO padding, just trim
            sender_acct = (parsed_txn.get('remitter_acct_no') or '').strip()
            recvr_acct = (parsed_txn.get('benef_acct_no') or '').strip()

            # Status normalization
            status_norm = cls.process_status(parsed_txn.get('status', ''))

            # Receiver account name: best available proxy is beneficiary_details
            recvr_acct_name = (parsed_txn.get('beneficiary_details') or '').strip()

            record = NEFTInwardRecord(
                
                bank_code=bankcode,
                txnind=txnind,
                jrnl_id=(parsed_txn.get('journal_no') or '').strip(),
                ref_no=(parsed_txn.get('utr') or '').strip(),
                txn_date=txn_date_ddmmyyyy,
                txn_amt=txn_amt,
                sender_ifsc=(parsed_txn.get('ifsc_sender') or '').strip(),
                reciever_ifsc=(parsed_txn.get('ifsc_recvr') or '').strip(),
                sender_acct_no=sender_acct,
                sender_acct_name=(parsed_txn.get('sender_acct_name') or '').strip(),
                remitter_detail=(parsed_txn.get('remitter_detail') or '').strip(),
                remitter_info=(parsed_txn.get('remmiter_info') or '').strip(),
                recvr_acct_no=recvr_acct,
                recvr_acct_name=recvr_acct_name,
                status=status_norm,
                reject_code=(parsed_txn.get('reject_code') or '').strip(),
                reject_reason=(parsed_txn.get('reject_reason') or '').strip(),
                benef_address=(parsed_txn.get('benef_address') or '').strip(),
                msg_type=(parsed_txn.get('sub_msg_type') or '').strip(),
            )

            return record

        except Exception as e:
            logger.error(f"Error mapping NEFT transaction: {e}", exc_info=True)
            raise

    @classmethod
    def map_transactions(cls, parsed_transactions: List[Dict[str, Any]], bankcode: str) -> List[NEFTInwardRecord]:
        """
        Map a list of parsed NEFT transactions to NEFTInwardRecord objects.

        Args:
            parsed_transactions: List of dicts from SFTPUtrParser
            bankcode           : Bank code to be applied to each record
        """
        records: List[NEFTInwardRecord] = []
        for txn in parsed_transactions:
            try:
                rec = cls.map_transaction(txn, bankcode)
                records.append(rec)
            except Exception as e:
                logger.warning(f"Skipping transaction due to error: {e}")
        logger.info(f"Mapped {len(records)} NEFT transactions for bank {bankcode}")
        return records
