"""Thin MyFatoorah HTTP client for the supplier portal.

One master API key per merchant account is used. Tenancy is enforced by the
calling code (every request is scoped by SupplierCode).
"""

from __future__ import annotations

import logging
from typing import Any

import requests
from django.conf import settings

from apps.suppliers.exceptions import (
    MyFatoorahError,
    MyFatoorahNotConfigured,
    MyFatoorahValidationError,
)

logger = logging.getLogger(__name__)


class MyFatoorahClient:
    """Wraps MyFatoorah's V2 supplier-facing endpoints."""

    def __init__(
        self,
        base_url: str | None = None,
        api_key: str | None = None,
        timeout: int | None = None,
    ):
        cfg = getattr(settings, "MYFATOORAH", {}) or {}
        self.base_url = (base_url or cfg.get("BASE_URL", "")).rstrip("/")
        self.api_key = api_key or cfg.get("API_KEY", "")
        self.timeout = timeout or cfg.get("TIMEOUT", 15)

        if not self.base_url:
            raise MyFatoorahNotConfigured("MYFATOORAH.BASE_URL is not set.")
        if not self.api_key:
            raise MyFatoorahNotConfigured("MYFATOORAH.API_KEY is not set.")

    # --------------------------- HTTP plumbing ---------------------------

    def _headers(self) -> dict[str, str]:
        return {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
            "Accept": "application/json",
        }

    def _request(
        self,
        method: str,
        path: str,
        *,
        params: dict | None = None,
        json: dict | None = None,
    ) -> Any:
        url = f"{self.base_url}{path}"
        try:
            resp = requests.request(
                method,
                url,
                headers=self._headers(),
                params=params,
                json=json,
                timeout=self.timeout,
            )
        except requests.RequestException as exc:
            logger.exception("MyFatoorah request failed: %s %s", method, path)
            raise MyFatoorahError(detail=str(exc)) from exc

        try:
            payload = resp.json()
        except ValueError as exc:
            raise MyFatoorahError(
                detail=f"Non-JSON response ({resp.status_code}): {resp.text[:200]}"
            ) from exc

        if not resp.ok:
            raise MyFatoorahError(detail=f"HTTP {resp.status_code}: {payload}")

        if isinstance(payload, dict) and payload.get("IsSuccess") is False:
            errors = (
                payload.get("ValidationErrors")
                or payload.get("FieldsErrors")
                or []
            )
            raise MyFatoorahValidationError(
                detail={
                    "message": payload.get("Message", "Validation error"),
                    "errors": errors,
                }
            )

        if isinstance(payload, dict) and "Data" in payload:
            return payload["Data"]
        return payload

    # --------------------------- Suppliers (read) ---------------------------

    def get_supplier(self, supplier_code: int) -> dict:
        """GET /v2/GetSupplierDetails — supplier profile.

        Note: the doc-side query param is misspelled `suppplierCode` (3 p's).
        We send both spellings for safety.
        """
        return self._request(
            "GET",
            "/v2/GetSupplierDetails",
            params={
                "suppplierCode": supplier_code,
                "supplierCode": supplier_code,
            },
        )

    def get_supplier_dashboard(self, supplier_code: int) -> dict:
        """GET /v2/GetSupplierDashboard — totals and balances."""
        return self._request(
            "GET",
            "/v2/GetSupplierDashboard",
            params={"SupplierCode": supplier_code},
        )

    def get_supplier_deposits(
        self,
        supplier_code: int,
        *,
        search: str | None = None,
        start: int | None = None,
        length: int | None = None,
        sort_column: str | None = None,
        sort_direction: str | None = None,
    ) -> dict:
        """GET /v2/GetSupplierDeposits — deposit history."""
        params: dict[str, Any] = {"SupplierCode": supplier_code}
        if search is not None:
            params["search"] = search
        if start is not None:
            params["start"] = start
        if length is not None:
            params["length"] = length
        if sort_column is not None:
            params["sortColumn"] = sort_column
        if sort_direction is not None:
            params["sortDirection"] = sort_direction
        return self._request("GET", "/v2/GetSupplierDeposits", params=params)

    def get_supplier_documents(self, supplier_code: int) -> list[dict]:
        """GET /v2/GetSupplierDocuments — uploaded KYC documents."""
        return (
            self._request(
                "GET",
                "/v2/GetSupplierDocuments",
                params={"SupplierCode": supplier_code},
            )
            or []
        )

    # --------------------------- Suppliers (write) ---------------------------

    def upload_supplier_document(self, payload: dict) -> dict:
        """PUT /v2/UploadSupplierDocument — upload a KYC document.

        The payload includes a base64-encoded file in `FileUpload.Buffer`.
        """
        return self._request("PUT", "/v2/UploadSupplierDocument", json=payload)

    # --------------------------- Payments ---------------------------

    def send_payment(self, payload: dict) -> dict:
        """POST /v2/SendPayment — create a payment link."""
        return self._request("POST", "/v2/SendPayment", json=payload)

    def get_payment_status(self, key: str, key_type: str = "InvoiceId") -> dict:
        """POST /v2/GetPaymentStatus — check payment/invoice status."""
        return self._request(
            "POST",
            "/v2/GetPaymentStatus",
            json={"Key": str(key), "KeyType": key_type},
        )

    def initiate_payment(self, invoice_amount: float, currency_iso: str = "AED") -> dict:
        """POST /v2/InitiatePayment — list available payment methods."""
        return self._request(
            "POST",
            "/v2/InitiatePayment",
            json={"InvoiceAmount": invoice_amount, "CurrencyIso": currency_iso},
        )

    # --------------------------- Reporting ---------------------------

    def get_refund_status(self, key: str, key_type: str) -> dict:
        """POST /v2/GetRefundStatus.

        key_type ∈ {"InvoiceId", "RefundReference", "RefundId"}.
        """
        return self._request(
            "POST",
            "/v2/GetRefundStatus",
            json={"Key": str(key), "KeyType": key_type},
        )

    def get_deposited_invoices(
        self,
        deposit_reference: str,
        *,
        type_: str = "Supplier",
        supplier_code: int | None = None,
    ) -> list[dict]:
        """POST /v2/GetDepositedInvoices — invoices included in a deposit."""
        payload: dict = {"DepositReference": deposit_reference, "Type": type_}
        if supplier_code is not None:
            payload["SupplierCode"] = supplier_code
        return (
            self._request("POST", "/v2/GetDepositedInvoices", json=payload)
            or []
        )


def get_client() -> MyFatoorahClient:
    """Factory used by views. Override in tests via dependency injection."""
    return MyFatoorahClient()
