"""HTTP client wrapping the Go API server."""

from __future__ import annotations

import os
import uuid

import httpx


class CorpoClient:
    """Synchronous HTTP client for the corpo Go API."""

    def __init__(self, base_url: str, timeout: float = 30.0, api_key: str = ""):
        self.base_url = base_url.rstrip("/")
        headers = {}
        if api_key:
            headers["Authorization"] = f"Bearer {api_key}"
        self._client = httpx.Client(base_url=self.base_url, timeout=timeout, headers=headers)

    def close(self):
        self._client.close()

    # --- Read endpoints ---

    def get_realm(self, address: str) -> dict:
        return self._get(f"/api/v1/realm/{address}")

    def get_realm_by_name(self, name: str) -> dict:
        return self._get(f"/api/v1/realm/by-name/{name}")

    def list_realms(self) -> list:
        return self._get("/api/v1/realms")

    def get_governance(self, address: str) -> dict:
        return self._get(f"/api/v1/governance/{address}")

    def list_governances(self, realm: str) -> list:
        return self._get("/api/v1/governances", params={"realm": realm})

    def get_proposal(self, address: str) -> dict:
        return self._get(f"/api/v1/proposal/{address}")

    def list_proposals(self, governance: str) -> list:
        return self._get("/api/v1/proposals", params={"governance": governance})

    def get_member(self, realm: str, mint: str, owner: str) -> dict:
        return self._get(f"/api/v1/member/{realm}/{mint}/{owner}")

    def list_members(self, realm: str) -> list:
        return self._get("/api/v1/members", params={"realm": realm})

    def get_treasury(self, governance: str) -> dict:
        return self._get(f"/api/v1/treasury/{governance}")

    # --- Account endpoints ---

    def register_account(self, email: str) -> dict:
        return self._post("/api/v1/accounts", {"email": email})

    def get_account(self, account_id: str) -> dict:
        return self._get(f"/api/v1/accounts/{account_id}")

    # --- Transaction endpoints ---

    def tx_create_realm(self, data: dict) -> dict:
        return self._post("/api/v1/tx/create-realm", data)

    def tx_deposit_tokens(self, data: dict) -> dict:
        return self._post("/api/v1/tx/deposit-tokens", data)

    def tx_withdraw_tokens(self, data: dict) -> dict:
        return self._post("/api/v1/tx/withdraw-tokens", data)

    def tx_create_governance(self, data: dict) -> dict:
        return self._post("/api/v1/tx/create-governance", data)

    def tx_create_proposal(self, data: dict) -> dict:
        return self._post("/api/v1/tx/create-proposal", data)

    def tx_cast_vote(self, data: dict) -> dict:
        return self._post("/api/v1/tx/cast-vote", data)

    def tx_cancel_proposal(self, data: dict) -> dict:
        return self._post("/api/v1/tx/cancel-proposal", data)

    def tx_sign_off_proposal(self, data: dict) -> dict:
        return self._post("/api/v1/tx/sign-off-proposal", data)

    def tx_add_signatory(self, data: dict) -> dict:
        return self._post("/api/v1/tx/add-signatory", data)

    def tx_finalize_vote(self, data: dict) -> dict:
        return self._post("/api/v1/tx/finalize-vote", data)

    def tx_relinquish_vote(self, data: dict) -> dict:
        return self._post("/api/v1/tx/relinquish-vote", data)

    def tx_execute_transaction(self, data: dict) -> dict:
        return self._post("/api/v1/tx/execute-transaction", data)

    def tx_set_governance_delegate(self, data: dict) -> dict:
        return self._post("/api/v1/tx/set-governance-delegate", data)

    def tx_create_token_owner_record(self, data: dict) -> dict:
        return self._post("/api/v1/tx/create-token-owner-record", data)

    def tx_create_native_treasury(self, data: dict) -> dict:
        return self._post("/api/v1/tx/create-native-treasury", data)

    def mint_tokens(self, data: dict) -> dict:
        """Create mint, mint supply, and distribute to recipients."""
        return self._post("/api/v1/tx/mint-tokens", data)

    # --- Quote endpoints ---

    def create_quote(self, entity_name: str, entity_type: str = "dao_llc", wallet_address: str = "") -> dict:
        body: dict = {"entity_name": entity_name, "entity_type": entity_type}
        if wallet_address:
            body["wallet_address"] = wallet_address
        return self._post("/api/v1/quotes", body)

    def get_quote(self, quote_id: str) -> dict:
        return self._get(f"/api/v1/quotes/{quote_id}")

    def pay_quote(self, quote_id: str, method: str = "credit_card") -> dict:
        return self._post(f"/api/v1/quotes/{quote_id}/pay", {"method": method})

    # --- Corporate Actions endpoints ---

    def get_actions_catalog(self) -> list:
        return self._get("/api/v1/actions/catalog")

    def list_available_actions(self, entity_id: str) -> list:
        return self._get(f"/api/v1/entities/{entity_id}/actions/available")

    def list_actions(self, entity_id: str) -> list:
        return self._get(f"/api/v1/entities/{entity_id}/actions")

    def initiate_action(self, entity_id: str, action_type: str, params: dict | None = None) -> dict:
        body: dict = {"action_type": action_type}
        if params:
            body["params"] = params
        return self._post(f"/api/v1/entities/{entity_id}/actions", body)

    def get_action(self, entity_id: str, action_id: str) -> dict:
        return self._get(f"/api/v1/entities/{entity_id}/actions/{action_id}")

    def get_action_forms(self, entity_id: str, action_id: str) -> list:
        return self._get(f"/api/v1/entities/{entity_id}/actions/{action_id}/forms")

    def execute_action(self, entity_id: str, action_id: str) -> dict:
        return self._post(f"/api/v1/entities/{entity_id}/actions/{action_id}/execute", {})

    # --- Recipe endpoints ---

    def list_recipes(self) -> list:
        return self._get("/api/v1/recipes")

    def get_recipe(self, recipe_id: str) -> dict:
        return self._get(f"/api/v1/recipes/{recipe_id}")

    def execute_recipe(self, entity_id: str, recipe_id: str, step_params: dict | None = None) -> dict:
        body: dict = {}
        if step_params:
            body["step_params"] = step_params
        return self._post(f"/api/v1/entities/{entity_id}/recipes/{recipe_id}/execute", body)

    def setup_governance(self, entity_id: str, realm_address: str, governance_program_id: str) -> dict:
        return self._put(f"/api/v1/entities/{entity_id}/governance", {
            "realm_address": realm_address,
            "governance_program_id": governance_program_id,
        })

    def update_action_status(self, entity_id: str, action_id: str, status: str) -> dict:
        return self._put(f"/api/v1/entities/{entity_id}/actions/{action_id}", {"status": status})

    # --- Internal helpers ---

    def _get(self, path: str, params: dict | None = None) -> dict | list:
        resp = self._client.get(path, params=params)
        resp.raise_for_status()
        return resp.json()

    def _put(self, path: str, data: dict) -> dict:
        resp = self._client.put(path, json=data)
        if resp.status_code >= 400:
            body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
            raise CorpoAPIError(resp.status_code, body.get("error", resp.text))
        return resp.json()

    def _post(self, path: str, data: dict) -> dict:
        resp = self._client.post(path, json=data)
        if resp.status_code >= 400:
            body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
            raise CorpoAPIError(resp.status_code, body.get("error", resp.text))
        return resp.json()


class MockCorpoClient:
    """In-memory mock that returns synthetic responses. No network calls.

    Activate via ``CORPO_MOCK_MODE=1`` environment variable.
    """

    def __init__(self, base_url: str = "http://mock", timeout: float = 30.0, api_key: str = ""):
        self.base_url = base_url
        self._api_key = api_key
        self._counter = 0

    def close(self):
        pass

    def _id(self, prefix: str = "") -> str:
        self._counter += 1
        return f"{prefix}mock-{self._counter:04d}"

    def _tx(self) -> dict:
        return {"transaction": "MOCK_TX_BASE64", "blockhash": self._id("bh-"), "fee_payer": "MockPayer1111"}

    # --- Read endpoints ---

    def get_realm(self, address: str) -> dict:
        return {"address": address, "realm": {"name": "MockRealm", "authority": "MockAuth1111"}}

    def get_realm_by_name(self, name: str) -> dict:
        return {"address": self._id("realm-"), "realm": {"name": name, "authority": "MockAuth1111"}}

    def list_realms(self) -> list:
        return [{"address": self._id("realm-"), "name": "MockRealm"}]

    def get_governance(self, address: str) -> dict:
        return {"address": address, "governance": {"realm": "MockRealm1111"}}

    def list_governances(self, realm: str) -> list:
        return [{"address": self._id("gov-"), "realm": realm}]

    def get_proposal(self, address: str) -> dict:
        return {"address": address, "proposal": {"name": "Mock Proposal", "state": "voting"}}

    def list_proposals(self, governance: str) -> list:
        return [{"address": self._id("prop-"), "governance": governance, "name": "Mock Proposal", "state": "voting"}]

    def get_member(self, realm: str, mint: str, owner: str) -> dict:
        return {"realm": realm, "mint": mint, "owner": owner, "deposit": 1000}

    def list_members(self, realm: str) -> list:
        return [{"owner": "MockOwner1111", "deposit": 1000}]

    def get_treasury(self, governance: str) -> dict:
        return {"governance": governance, "balance": 0}

    # --- Account endpoints ---

    def register_account(self, email: str) -> dict:
        return {
            "api_key": f"mock-key-{uuid.uuid4().hex[:12]}",
            "account_id": self._id("acct-"),
            "email": email,
        }

    def get_account(self, account_id: str) -> dict:
        return {"account_id": account_id, "email": "mock@example.com", "status": "active"}

    # --- Transaction endpoints ---

    def tx_create_realm(self, data: dict) -> dict:
        return self._tx()

    def tx_deposit_tokens(self, data: dict) -> dict:
        return self._tx()

    def tx_withdraw_tokens(self, data: dict) -> dict:
        return self._tx()

    def tx_create_governance(self, data: dict) -> dict:
        return self._tx()

    def tx_create_proposal(self, data: dict) -> dict:
        return self._tx()

    def tx_cast_vote(self, data: dict) -> dict:
        return self._tx()

    def tx_cancel_proposal(self, data: dict) -> dict:
        return self._tx()

    def tx_sign_off_proposal(self, data: dict) -> dict:
        return self._tx()

    def tx_add_signatory(self, data: dict) -> dict:
        return self._tx()

    def tx_finalize_vote(self, data: dict) -> dict:
        return self._tx()

    def tx_relinquish_vote(self, data: dict) -> dict:
        return self._tx()

    def tx_execute_transaction(self, data: dict) -> dict:
        return self._tx()

    def tx_set_governance_delegate(self, data: dict) -> dict:
        return self._tx()

    def tx_create_token_owner_record(self, data: dict) -> dict:
        return self._tx()

    def tx_create_native_treasury(self, data: dict) -> dict:
        return self._tx()

    def mint_tokens(self, data: dict) -> dict:
        """Mock mint: create token, split supply between governance and corpo addresses."""
        amount = data.get("amount", 0)
        fee_bps = data.get("corpo_fee_bps", 300)
        corpo_amount = amount * fee_bps // 10_000
        governance_amount = amount - corpo_amount

        mint_addr = self._id("mint-")
        return {
            **self._tx(),
            "mint": mint_addr,
            "total_supply": amount,
            "distribution": [
                {
                    "recipient": data.get("governance_address", "GovernanceAddr1111"),
                    "amount": governance_amount,
                    "label": "governance",
                },
                {
                    "recipient": data.get("corpo_address", "CorpoFee1111111111111111111111111111111111111"),
                    "amount": corpo_amount,
                    "label": "corpo_fee",
                },
            ],
        }

    # --- Quote endpoints ---

    def create_quote(self, entity_name: str, entity_type: str = "dao_llc", wallet_address: str = "") -> dict:
        return {
            "quote_id": self._id("q-"),
            "entity_name": entity_name,
            "entity_type": entity_type,
            "name_available": True,
            "pricing": {
                "filing_fee": 100_00,
                "service_fee": 399_00,
                "registered_agent": 100_00,
                "total_cents": 599_00,
                "total_display": "$599.00",
                "currency": "USD",
            },
            "status": "quoted",
        }

    def get_quote(self, quote_id: str) -> dict:
        return {"quote_id": quote_id, "status": "pending", "total": 499_00, "currency": "usd"}

    def pay_quote(self, quote_id: str, method: str = "credit_card") -> dict:
        return {"quote_id": quote_id, "status": "paid", "method": method}

    # --- Corporate Actions endpoints ---

    def get_actions_catalog(self) -> list:
        return [
            {"id": "add_member", "name": "Add Member", "type": "membership", "description": "Add a new member to the entity", "requires_vote": True},
            {"id": "remove_member", "name": "Remove Member", "type": "membership", "description": "Remove a member from the entity", "requires_vote": True},
            {"id": "transfer_tokens", "name": "Transfer Tokens", "type": "treasury", "description": "Transfer governance tokens", "requires_vote": False},
            {"id": "distribute_funds", "name": "Distribute Funds", "type": "treasury", "description": "Distribute tokens from treasury to one or more recipients", "requires_vote": True,
             "params_schema": {
                 "recipients": {"type": "array", "description": "List of {address, amount} objects", "required": True},
                 "memo": {"type": "string", "description": "Reason for distribution", "required": False},
             }},
            {"id": "mint_tokens", "name": "Mint Tokens", "type": "treasury", "description": "Mint additional governance tokens (requires mint authority)", "requires_vote": True,
             "params_schema": {
                 "amount": {"type": "integer", "description": "Number of tokens to mint", "required": True},
                 "recipient": {"type": "string", "description": "Recipient address", "required": True},
             }},
        ]

    def list_available_actions(self, entity_id: str) -> list:
        return self.get_actions_catalog()

    def list_actions(self, entity_id: str) -> list:
        return []

    def initiate_action(self, entity_id: str, action_type: str, params: dict | None = None) -> dict:
        aid = self._id("act-")
        return {"id": aid, "action_id": aid, "entity_id": entity_id, "action_type": action_type, "status": "pending"}

    def get_action(self, entity_id: str, action_id: str) -> dict:
        return {"id": action_id, "action_id": action_id, "entity_id": entity_id, "action_type": "unknown", "status": "pending", "created_at": "2026-01-01T00:00:00Z"}

    def get_action_forms(self, entity_id: str, action_id: str) -> list:
        return []

    def execute_action(self, entity_id: str, action_id: str) -> dict:
        return {"id": action_id, "action_id": action_id, "status": "executed"}

    # --- Recipe endpoints ---

    def list_recipes(self) -> list:
        return [
            {
                "id": "full-formation", "name": "Full Formation", "description": "Complete entity formation workflow",
                "tags": ["formation"],
                "steps": [
                    {"action_type": "create_entity", "description": "File with Wyoming SOS", "optional": False},
                    {"action_type": "setup_governance", "description": "Configure on-chain governance", "optional": False},
                    {"action_type": "mint_tokens", "description": "Mint governance tokens", "optional": False},
                ],
            },
            {
                "id": "add-member-vote", "name": "Add Member (with vote)", "description": "Propose and vote on new member",
                "tags": ["membership"],
                "steps": [
                    {"action_type": "create_proposal", "description": "Create add-member proposal", "optional": False},
                    {"action_type": "cast_vote", "description": "Vote on proposal", "optional": False},
                ],
            },
        ]

    def get_recipe(self, recipe_id: str) -> dict:
        return {
            "id": recipe_id, "name": recipe_id.replace("-", " ").title(),
            "description": "Mock recipe", "category": "general", "version": "1.0.0",
            "steps": [
                {"action_type": "step_one", "description": "First step", "optional": False},
            ],
        }

    def execute_recipe(self, entity_id: str, recipe_id: str, step_params: dict | None = None) -> dict:
        return {"recipe_id": recipe_id, "entity_id": entity_id, "status": "completed"}

    def setup_governance(self, entity_id: str, realm_address: str, governance_program_id: str) -> dict:
        return {"entity_id": entity_id, "realm_address": realm_address, "governance_program_id": governance_program_id, "status": "configured"}

    def update_action_status(self, entity_id: str, action_id: str, status: str) -> dict:
        return {"action_id": action_id, "status": status}


def is_mock_mode() -> bool:
    """Check if mock mode is enabled via CORPO_MOCK_MODE env var."""
    return os.environ.get("CORPO_MOCK_MODE", "").strip().lower() in ("1", "true", "yes")


def get_client(base_url: str = "http://mock", timeout: float = 30.0, api_key: str = "") -> CorpoClient | MockCorpoClient:
    """Factory: returns MockCorpoClient when CORPO_MOCK_MODE=1, else CorpoClient."""
    if is_mock_mode():
        return MockCorpoClient(base_url, timeout, api_key)
    return CorpoClient(base_url, timeout, api_key)


class CorpoAPIError(Exception):
    """Error from the corpo Go API."""

    def __init__(self, status_code: int, message: str):
        self.status_code = status_code
        self.message = message
        super().__init__(f"API error {status_code}: {message}")
