"""
DeFi Remediation Engine for CrashSense.

Provides concrete, actionable fix suggestions for common DeFi failure patterns.
Each fix is human-readable and includes wallet-action instructions compatible
with MetaMask, Rabby, and CLI tools (cast / web3).
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional


# ── Fix database ──────────────────────────────────────────────────────────
# Maps failure pattern → structured fix instructions

REMEDIATION_DB: Dict[str, Dict[str, Any]] = {
    "INSUFFICIENT_BALANCE": {
        "title": "Insufficient Token Balance",
        "explanation": (
            "Your wallet does not hold enough tokens to complete this transaction. "
            "The smart contract's require() check on balanceOf() failed."
        ),
        "steps": [
            "1. Check your wallet balance for the token you are trying to send/swap.",
            "2. If your balance is too low, acquire more tokens via a swap or bridge.",
            "3. Account for gas fees — keep enough native token (ETH/MATIC/BNB) for gas.",
            "4. Retry the transaction with the correct amount.",
        ],
        "severity": "high",
    },
    "INSUFFICIENT_ALLOWANCE": {
        "title": "Token Allowance Too Low",
        "explanation": (
            "The DeFi contract needs your permission (allowance) to spend tokens on your behalf. "
            "You either haven't approved the contract or the approved amount is too small."
        ),
        "steps": [
            "1. Approve the contract to spend your tokens:",
            "   - In MetaMask/Rabby: go to the dApp, click 'Approve' when prompted.",
            "   - CLI: cast send <token> 'approve(address,uint256)' <spender> <amount> --private-key <key>",
            "2. Set a sufficient allowance (exact amount or unlimited).",
            "3. Wait for the approval transaction to confirm.",
            "4. Retry the original transaction.",
        ],
        "severity": "medium",
    },
    "OUT_OF_GAS": {
        "title": "Transaction Ran Out of Gas",
        "explanation": (
            "The transaction consumed all allocated gas before completing execution. "
            "This often happens with complex DeFi interactions (multi-hop swaps, flash loans)."
        ),
        "steps": [
            "1. Increase the gas limit by 20-50% in your wallet settings.",
            "2. If the call is inherently too expensive, consider:",
            "   - Splitting into smaller operations.",
            "   - Using a different DEX route with fewer hops.",
            "3. Check if the contract has an infinite loop or expensive logic — this may be a contract bug.",
            "4. Retry with the higher gas limit.",
        ],
        "severity": "medium",
    },
    "NEAR_GAS_LIMIT": {
        "title": "Transaction Nearly Ran Out of Gas",
        "explanation": (
            "Gas usage was very close to the limit. The transaction may have succeeded this time, "
            "but future attempts could fail if conditions change slightly."
        ),
        "steps": [
            "1. Increase your gas limit by at least 30% as a safety margin.",
            "2. Consider simulating the transaction first with tools like Tenderly.",
        ],
        "severity": "low",
    },
    "SLIPPAGE_EXCEEDED": {
        "title": "Slippage Tolerance Exceeded",
        "explanation": (
            "The price moved between when you submitted the swap and when it was executed. "
            "The actual output tokens were less than your minimum acceptable amount."
        ),
        "steps": [
            "1. Increase your slippage tolerance (e.g., from 0.5% to 1-3%).",
            "   Warning: Higher slippage = more vulnerable to MEV/sandwich attacks.",
            "2. For volatile tokens, set slippage to 5-12%.",
            "3. Some fee-on-transfer tokens need higher slippage (12%+).",
            "4. Use a DEX aggregator (1inch, Paraswap) for better routing.",
            "5. Retry the swap with the adjusted setting.",
        ],
        "severity": "medium",
    },
    "DEADLINE_EXPIRED": {
        "title": "Transaction Deadline Expired",
        "explanation": (
            "The swap's deadline timestamp has passed. This is a safety mechanism "
            "to prevent stale transactions from executing at unfavorable prices."
        ),
        "steps": [
            "1. Simply retry the transaction — a fresh deadline will be set.",
            "2. If using a custom deadline, increase it (e.g., 20+ minutes).",
            "3. During network congestion, increase gas price for faster confirmation.",
        ],
        "severity": "low",
    },
    "INSUFFICIENT_LIQUIDITY": {
        "title": "Insufficient Liquidity in Pool",
        "explanation": (
            "The liquidity pool does not have enough tokens to fill your order. "
            "This is common with low-cap tokens or very large swaps."
        ),
        "steps": [
            "1. Reduce the swap amount and try again.",
            "2. Use a DEX aggregator to route through multiple pools.",
            "3. Try a different DEX or trading pair.",
            "4. Wait for more liquidity to be added by providers.",
        ],
        "severity": "high",
    },
    "ACCESS_DENIED": {
        "title": "Permission / Access Denied",
        "explanation": (
            "The contract requires a specific role or ownership to execute this function. "
            "Your wallet address is not authorized."
        ),
        "steps": [
            "1. Verify you are using the correct wallet address.",
            "2. Check if the function is owner-only or requires a specific role.",
            "3. If you need access, contact the contract owner/governance.",
        ],
        "severity": "high",
    },
    "CONTRACT_PAUSED": {
        "title": "Contract Is Paused",
        "explanation": (
            "The smart contract has been paused by its admin, likely for maintenance or a security incident."
        ),
        "steps": [
            "1. Check the project's official channels (Twitter, Discord) for announcements.",
            "2. Wait until the contract is unpaused.",
            "3. Do NOT interact with unofficial 'rescue' contracts.",
        ],
        "severity": "high",
    },
    "ARITHMETIC_OVERFLOW": {
        "title": "Arithmetic Overflow Error",
        "explanation": (
            "A number in the contract exceeded its maximum value (overflow). "
            "This is usually a contract bug caught by Solidity 0.8+ overflow checks."
        ),
        "steps": [
            "1. This is typically a contract bug — report it to the project team.",
            "2. Do NOT retry with the same parameters.",
            "3. Check if a newer version of the contract is available.",
        ],
        "severity": "critical",
    },
    "ARITHMETIC_UNDERFLOW": {
        "title": "Arithmetic Underflow Error",
        "explanation": (
            "A subtraction result went below zero (underflow). "
            "Common when trying to transfer more tokens than available."
        ),
        "steps": [
            "1. Check your token balance — you may not have enough.",
            "2. If your balance is sufficient, this may be a contract bug.",
            "3. Report the issue to the project team with the tx hash.",
        ],
        "severity": "high",
    },
    "SAFE_TRANSFER_FAILED": {
        "title": "SafeTransfer / SafeTransferFrom Failed",
        "explanation": (
            "A safe token transfer call failed. This often means the recipient contract "
            "does not implement the required callback, or the token transfer itself reverted."
        ),
        "steps": [
            "1. Check if the destination address supports receiving this token type.",
            "2. Ensure you have approved the correct spender contract.",
            "3. For ERC-721/1155 NFTs, the receiver must implement onERC721Received.",
        ],
        "severity": "medium",
    },
    "NONCE_TOO_LOW": {
        "title": "Nonce Too Low",
        "explanation": (
            "The transaction nonce is lower than expected. This means a transaction with "
            "this nonce has already been mined."
        ),
        "steps": [
            "1. In MetaMask: Settings > Advanced > Clear Activity Tab Data.",
            "2. Retry — your wallet will assign the correct nonce automatically.",
            "3. If manually setting nonces, query the current nonce first.",
        ],
        "severity": "low",
    },
    "BLACKLISTED_ADDRESS": {
        "title": "Address Is Blacklisted",
        "explanation": (
            "Your address or the recipient has been blacklisted by the token contract "
            "(common with USDT, USDC and other regulated tokens)."
        ),
        "steps": [
            "1. Check if the token contract maintains a blacklist.",
            "2. Contact the token issuer if you believe this is an error.",
            "3. Use a different address if possible.",
        ],
        "severity": "critical",
    },
    "REENTRANCY_GUARD": {
        "title": "Reentrancy Guard Triggered",
        "explanation": (
            "The contract detected a re-entrant call and blocked it. "
            "This is a safety mechanism against reentrancy attacks."
        ),
        "steps": [
            "1. Do NOT try to bypass this — it's a security protection.",
            "2. Simplify your transaction (avoid complex contract interactions that call back).",
            "3. If you're a developer, restructure your contract to avoid reentrancy.",
        ],
        "severity": "medium",
    },
    "PANIC_ERROR": {
        "title": "Solidity Panic Error",
        "explanation": (
            "A Solidity Panic was raised indicating an internal invariant violation "
            "(assertion failure, overflow, array out-of-bounds, etc.)."
        ),
        "steps": [
            "1. This is almost certainly a contract bug.",
            "2. Report the issue to the project team with the tx hash.",
            "3. Do NOT retry with the same parameters.",
        ],
        "severity": "critical",
    },
}


class RemediationEngine:
    """Generate actionable fix suggestions for DeFi transaction failures."""

    def get_remediation(self, patterns: List[str]) -> List[Dict[str, Any]]:
        """
        Return remediation instructions for each matched pattern.
        Ordered by severity (critical > high > medium > low).
        """
        severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
        results: List[Dict[str, Any]] = []
        seen: set = set()

        for pattern in patterns:
            if pattern in seen:
                continue
            seen.add(pattern)
            fix = REMEDIATION_DB.get(pattern)
            if fix:
                results.append({"pattern": pattern, **fix})

        results.sort(key=lambda r: severity_order.get(r.get("severity", "low"), 9))
        return results

    def format_fixes(self, patterns: List[str]) -> str:
        """Return a human-readable text summarizing all applicable fixes."""
        fixes = self.get_remediation(patterns)
        if not fixes:
            return "No specific remediation found for the detected patterns."

        parts: List[str] = []
        for fix in fixes:
            severity_icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🟢"}.get(
                fix.get("severity", "low"), "⚪"
            )
            lines = [
                f"{severity_icon} **{fix['title']}** [{fix.get('severity', '').upper()}]",
                fix["explanation"],
                "",
                "**Steps to fix:**",
            ]
            lines.extend(fix["steps"])
            parts.append("\n".join(lines))

        return "\n\n---\n\n".join(parts)
