"""
Sophi Compiler
Transforms a verified SwapProgram into Oneseam-compatible trade intent dicts.

Bridges directly to Oneseam's internal structures:
  - create_trade_intent() fields: intent_id, maker_client_id, maker_wallet,
    sell_asset, buy_asset, amount, price_min, price_max, expiration, status, metadata
  - secret / secret_hash pattern from _ensure_swap_for_match()
  - HTLC coordination fields used by start_htlc_coordination()
"""

import secrets
import hashlib
import uuid
import time
from typing import List, Dict, Any, Tuple
from .parser import SwapProgram, PartyDecl, PlainValue, BlindValue

# ── Helpers ────────────────────────────────────────────────────────────────────

def _generate_secret() -> Tuple[str, str]:
    """
    Mirrors Oneseam's _ensure_swap_for_match():
        secret = secrets.token_hex(32)
        secret_hash = hashlib.sha256(secret.encode('utf-8')).hexdigest()
    """
    secret = secrets.token_hex(32)
    secret_hash = hashlib.sha256(secret.encode('utf-8')).hexdigest()
    return secret, secret_hash

def _asset_str(value: object) -> str:
    if isinstance(value, PlainValue):
        return value.asset.upper()
    return "BLIND"   # asset type hidden in blind swaps

def _amount(value: object) -> float:
    if isinstance(value, PlainValue):
        return value.amount
    return 0.0  # blind — amount unknown until reveal

def _default_expiration(program: SwapProgram) -> int:
    """Convert Sophi expires decl to a Unix timestamp."""
    if program.expires is None:
        return int(time.time()) + 86400  # default: 24 hours

    if program.expires.kind == 'timestamp':
        return program.expires.value

    # block height — approximate: current time + blocks_remaining * 10 min
    # In production this would call a Bitcoin node for current block height.
    # Here we use a conservative estimate.
    APPROX_CURRENT_BLOCK = 840000
    blocks_remaining = max(0, program.expires.value - APPROX_CURRENT_BLOCK)
    return int(time.time()) + (blocks_remaining * 600)

# ── Main compiler ──────────────────────────────────────────────────────────────

class CompiledSwap:
    """
    Output of the Sophi compiler.
    Contains one Oneseam trade intent per party,
    plus the shared HTLC coordination parameters.
    """
    def __init__(
        self,
        intents: List[Dict[str, Any]],
        htlc_params: Dict[str, Any],
        warnings: List[str],
    ):
        self.intents = intents          # list of Oneseam intent dicts
        self.htlc_params = htlc_params  # ready for start_htlc_coordination()
        self.warnings = warnings

    def summary(self) -> str:
        lines = ["── Sophi compiled swap ──────────────────────────"]
        for intent in self.intents:
            blind = intent['metadata'].get('blind', False)
            amount_str = (
                f"blind({intent['metadata']['blind_hash'][:8]}...)"
                if blind else
                f"{intent['amount']} {intent['sell_asset']}"
            )
            lines.append(
                f"  {intent['metadata']['sophi_party']:12s}  "
                f"gives {amount_str:30s}  "
                f"wants {intent['buy_asset']}"
            )
        lines.append(f"\n  secret_hash : {self.htlc_params['secret_hash']}")
        lines.append(f"  expiration  : {self.htlc_params['expiration']}")
        lines.append(f"  parties     : {self.htlc_params['party_count']}")
        lines.append(f"  batch       : {self.htlc_params['batch_allow_partial']}")
        if self.warnings:
            lines.append("")
            for w in self.warnings:
                lines.append(f"  ⚠ {w}")
        return "\n".join(lines)


def compile_swap(program: SwapProgram, warnings: List[str] = None) -> CompiledSwap:
    """
    Core compiler. Takes a verified SwapProgram, produces a CompiledSwap.
    """
    warnings = warnings or []
    expiration = _default_expiration(program)

    # One shared secret for the whole swap (all HTLCs use the same preimage)
    # This is the atomic guarantee: all legs share one secret_hash.
    secret, secret_hash = _generate_secret()

    intents = []
    has_partial = False

    for party in program.parties:
        blind = isinstance(party.gives.value, BlindValue)

        # Map Sophi sides → Oneseam intent fields
        intent = {
            "intent_id":       str(uuid.uuid4()),
            "maker_client_id": party.name,          # caller fills real client_id
            "maker_wallet":    None,                 # caller fills wallet address
            "sell_asset":      _asset_str(party.gives.value),
            "buy_asset":       _asset_str(party.wants.value),
            "amount":          _amount(party.gives.value),
            "price_min":       None,                 # blind: unknown until reveal
            "price_max":       None,
            "expiration":      expiration,
            "status":          "pending",
            "metadata": {
                "sophi_party":       party.name,
                "sophi_version":     "0.1",
                "blind":             blind,
                "blind_hash":        party.gives.value.hash if blind else None,
                "wants_blind":       isinstance(party.wants.value, BlindValue),
                "wants_blind_hash":  party.wants.value.hash if isinstance(party.wants.value, BlindValue) else None,
                "partial":           party.gives.partial or party.wants.partial,
                "secret_hash":       secret_hash,   # shared across all intents
            }
        }

        if party.gives.partial or party.wants.partial:
            has_partial = True

        intents.append(intent)

    # HTLC coordination params — ready to pass to start_htlc_coordination()
    htlc_params = {
        "secret":             secret,       # keep safe — never log or transmit
        "secret_hash":        secret_hash,
        "expiration":         expiration,
        "party_count":        len(program.parties),
        "batch_allow_partial": has_partial,
        "timeout_lock":       expiration,
        "timeout_claim":      expiration - 3600,  # 1 hour before lock expires
        "htlc_legs": [
            {
                "party":       intent["metadata"]["sophi_party"],
                "sell_asset":  intent["sell_asset"],
                "buy_asset":   intent["buy_asset"],
                "amount":      intent["amount"],
                "blind":       intent["metadata"]["blind"],
                "blind_hash":  intent["metadata"]["blind_hash"],
            }
            for intent in intents
        ]
    }

    return CompiledSwap(
        intents=intents,
        htlc_params=htlc_params,
        warnings=warnings,
    )
