from __future__ import annotations

from urllib.parse import urlparse

from pydantic import BaseModel

from ebay_client.core.http.transport import ApiTransport
from ebay_client.generated.fulfillment.models import (
    AcceptPaymentDisputeRequest,
    AddEvidencePaymentDisputeRequest,
    AddEvidencePaymentDisputeResponse,
    ContestPaymentDisputeRequest,
    DisputeSummaryResponse,
    FileEvidence,
    IssueRefundRequest,
    Order,
    OrderSearchPagedCollection,
    PaymentDispute,
    PaymentDisputeActivityHistory,
    Refund,
    ShippingFulfillment,
    ShippingFulfillmentDetails,
    ShippingFulfillmentPagedCollection,
    UpdateEvidencePaymentDisputeRequest,
)

FULFILLMENT_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment"
FULFILLMENT_READ_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly"
FULFILLMENT_READ_SCOPE_OPTIONS = [[FULFILLMENT_READ_SCOPE], [FULFILLMENT_SCOPE]]
FINANCES_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.finances"
PAYMENT_DISPUTE_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.payment.dispute"
PAYMENT_DISPUTE_BASE_URL = "https://apiz.ebay.com/sell/fulfillment/v1"


class CreatedShippingFulfillment(BaseModel):
    location: str | None = None
    fulfillment_id: str | None = None


class EvidenceFileDownload(BaseModel):
    content: bytes
    content_disposition: str | None = None
    file_name: str | None = None


def extract_fulfillment_id(location: str | None) -> str | None:
    if not location:
        return None

    path = urlparse(location).path.rstrip("/")
    if not path:
        return None

    _, _, resource_id = path.rpartition("/")
    return resource_id or None


def _extract_file_name(content_disposition: str | None) -> str | None:
    if not content_disposition or "filename=" not in content_disposition:
        return None

    value = content_disposition.split("filename=", 1)[1].strip()
    if value.startswith('"') and value.endswith('"'):
        return value[1:-1]
    return value


class FulfillmentClient:
    def __init__(self, transport: ApiTransport) -> None:
        self.transport = transport

    def _dispute_path(self, path: str) -> str:
        return f"{PAYMENT_DISPUTE_BASE_URL}{path}"

    def get_order(self, order_id: str) -> Order:
        return self.transport.request_model(
            Order,
            "GET",
            f"/sell/fulfillment/v1/order/{order_id}",
            scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
        )

    def get_orders(self, *, limit: int | None = None, offset: int | None = None) -> OrderSearchPagedCollection:
        return self.transport.request_model(
            OrderSearchPagedCollection,
            "GET",
            "/sell/fulfillment/v1/order",
            scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
            params={"limit": limit, "offset": offset},
        )

    def issue_refund(self, order_id: str, payload: IssueRefundRequest) -> Refund:
        return self.transport.request_model(
            Refund,
            "POST",
            f"/sell/fulfillment/v1/order/{order_id}/issue_refund",
            scopes=[FINANCES_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )

    def get_shipping_fulfillments(self, order_id: str) -> ShippingFulfillmentPagedCollection:
        return self.transport.request_model(
            ShippingFulfillmentPagedCollection,
            "GET",
            f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment",
            scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
        )

    def get_shipping_fulfillment(self, order_id: str, fulfillment_id: str) -> ShippingFulfillment:
        return self.transport.request_model(
            ShippingFulfillment,
            "GET",
            f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment/{fulfillment_id}",
            scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
        )

    def create_shipping_fulfillment(
        self,
        order_id: str,
        payload: ShippingFulfillmentDetails,
    ) -> CreatedShippingFulfillment:
        response = self.transport.request(
            "POST",
            f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment",
            scopes=[FULFILLMENT_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )
        location = response.headers.get("Location")
        return CreatedShippingFulfillment(location=location, fulfillment_id=extract_fulfillment_id(location))

    def get_payment_dispute(self, payment_dispute_id: str) -> PaymentDispute:
        return self.transport.request_model(
            PaymentDispute,
            "GET",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
        )

    def fetch_evidence_content(
        self,
        payment_dispute_id: str,
        *,
        evidence_id: str,
        file_id: str,
    ) -> EvidenceFileDownload:
        response = self.transport.request(
            "GET",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/fetch_evidence_content"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            params={"evidence_id": evidence_id, "file_id": file_id},
        )
        content_disposition = response.headers.get("content-disposition")
        return EvidenceFileDownload(
            content=response.content,
            content_disposition=content_disposition,
            file_name=_extract_file_name(content_disposition),
        )

    def get_payment_dispute_activities(self, payment_dispute_id: str) -> PaymentDisputeActivityHistory:
        return self.transport.request_model(
            PaymentDisputeActivityHistory,
            "GET",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/activity"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
        )

    def get_payment_dispute_summaries(
        self,
        *,
        order_id: str | None = None,
        buyer_username: str | None = None,
        open_date_from: str | None = None,
        open_date_to: str | None = None,
        payment_dispute_status: list[str] | None = None,
        limit: int | None = None,
        offset: int | None = None,
    ) -> DisputeSummaryResponse:
        params: dict[str, object] = {
            "order_id": order_id,
            "buyer_username": buyer_username,
            "open_date_from": open_date_from,
            "open_date_to": open_date_to,
            "limit": limit,
            "offset": offset,
        }
        if payment_dispute_status is not None:
            params["payment_dispute_status"] = payment_dispute_status

        return self.transport.request_model(
            DisputeSummaryResponse,
            "GET",
            self._dispute_path("/payment_dispute_summary"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            params=params,
        )

    def contest_payment_dispute(
        self,
        payment_dispute_id: str,
        payload: ContestPaymentDisputeRequest,
    ) -> None:
        self.transport.request_json(
            "POST",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/contest"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )

    def accept_payment_dispute(
        self,
        payment_dispute_id: str,
        payload: AcceptPaymentDisputeRequest,
    ) -> None:
        self.transport.request_json(
            "POST",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/accept"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )

    def upload_evidence_file(
        self,
        payment_dispute_id: str,
        *,
        file_name: str,
        content: bytes,
        content_type: str = "image/jpeg",
    ) -> FileEvidence:
        return self.transport.request_model(
            FileEvidence,
            "POST",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/upload_evidence_file"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            files={"file": (file_name, content, content_type)},
        )

    def add_evidence(
        self,
        payment_dispute_id: str,
        payload: AddEvidencePaymentDisputeRequest,
    ) -> AddEvidencePaymentDisputeResponse:
        return self.transport.request_model(
            AddEvidencePaymentDisputeResponse,
            "POST",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/add_evidence"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )

    def update_evidence(
        self,
        payment_dispute_id: str,
        payload: UpdateEvidencePaymentDisputeRequest,
    ) -> None:
        self.transport.request_json(
            "POST",
            self._dispute_path(f"/payment_dispute/{payment_dispute_id}/update_evidence"),
            scopes=[PAYMENT_DISPUTE_SCOPE],
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )
