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.feed.models import (
    CreateTaskRequest,
    CreateUserScheduleRequest,
    ScheduleTemplateResponse,
    ScheduleTemplateCollection,
    Task,
    TaskCollection,
    UserScheduleResponse,
    UserScheduleCollection,
    UpdateUserScheduleRequest,
)

FEED_INVENTORY_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.inventory"
FEED_FULFILLMENT_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment"
FEED_MARKETING_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.marketing"
FEED_CATALOG_READ_SCOPE = "https://api.ebay.com/oauth/api_scope/commerce.catalog.readonly"
FEED_ANALYTICS_READ_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.analytics.readonly"
FEED_READ_SCOPE_OPTIONS = [
    [FEED_INVENTORY_SCOPE],
    [FEED_FULFILLMENT_SCOPE],
    [FEED_MARKETING_SCOPE],
    [FEED_CATALOG_READ_SCOPE],
    [FEED_ANALYTICS_READ_SCOPE],
]


class CreatedFeedResource(BaseModel):
    location: str | None = None
    resource_id: str | None = None


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


def extract_feed_resource_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:
        return None

    marker = "filename="
    if marker not in content_disposition:
        return None

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


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

    def get_tasks(
        self,
        *,
        feed_type: str | None = None,
        schedule_id: str | None = None,
        date_range: str | None = None,
        look_back_days: int | None = None,
        limit: int | None = None,
        offset: int | None = None,
    ) -> TaskCollection:
        return self.transport.request_model(
            TaskCollection,
            "GET",
            "/sell/feed/v1/task",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            params={
                "feed_type": feed_type,
                "schedule_id": schedule_id,
                "date_range": date_range,
                "look_back_days": look_back_days,
                "limit": limit,
                "offset": offset,
            },
        )

    def create_task(
        self,
        payload: CreateTaskRequest,
        *,
        marketplace_id: str,
        accept_language: str | None = None,
    ) -> CreatedFeedResource:
        headers = {
            "Content-Type": "application/json",
            "X-EBAY-C-MARKETPLACE-ID": marketplace_id,
        }
        if accept_language is not None:
            headers["Accept-Language"] = accept_language

        response = self.transport.request(
            "POST",
            "/sell/feed/v1/task",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            headers=headers,
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )
        location = response.headers.get("Location")
        return CreatedFeedResource(location=location, resource_id=extract_feed_resource_id(location))

    def get_task(self, task_id: str) -> Task:
        return self.transport.request_model(
            Task,
            "GET",
            f"/sell/feed/v1/task/{task_id}",
            scope_options=FEED_READ_SCOPE_OPTIONS,
        )

    def get_input_file(self, task_id: str) -> FeedFileDownload:
        return self._download_file(f"/sell/feed/v1/task/{task_id}/download_input_file")

    def get_result_file(self, task_id: str) -> FeedFileDownload:
        return self._download_file(f"/sell/feed/v1/task/{task_id}/download_result_file")

    def upload_file(
        self,
        task_id: str,
        *,
        file_name: str,
        content: bytes,
        content_type: str = "application/octet-stream",
    ) -> None:
        self.transport.request_json(
            "POST",
            f"/sell/feed/v1/task/{task_id}/upload_file",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            files={"file": (file_name, content, content_type)},
        )

    def get_schedule_templates(
        self,
        *,
        feed_type: str,
        limit: int | None = None,
        offset: int | None = None,
    ) -> ScheduleTemplateCollection:
        return self.transport.request_model(
            ScheduleTemplateCollection,
            "GET",
            "/sell/feed/v1/schedule_template",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            params={"feed_type": feed_type, "limit": limit, "offset": offset},
        )

    def get_schedule_template(self, schedule_template_id: str) -> ScheduleTemplateResponse:
        return self.transport.request_model(
            ScheduleTemplateResponse,
            "GET",
            f"/sell/feed/v1/schedule_template/{schedule_template_id}",
            scope_options=FEED_READ_SCOPE_OPTIONS,
        )

    def get_schedules(
        self,
        *,
        feed_type: str,
        limit: int | None = None,
        offset: int | None = None,
    ) -> UserScheduleCollection:
        return self.transport.request_model(
            UserScheduleCollection,
            "GET",
            "/sell/feed/v1/schedule",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            params={"feed_type": feed_type, "limit": limit, "offset": offset},
        )

    def create_schedule(self, payload: CreateUserScheduleRequest) -> CreatedFeedResource:
        response = self.transport.request(
            "POST",
            "/sell/feed/v1/schedule",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )
        location = response.headers.get("Location")
        return CreatedFeedResource(location=location, resource_id=extract_feed_resource_id(location))

    def get_schedule(self, schedule_id: str) -> UserScheduleResponse:
        return self.transport.request_model(
            UserScheduleResponse,
            "GET",
            f"/sell/feed/v1/schedule/{schedule_id}",
            scope_options=FEED_READ_SCOPE_OPTIONS,
        )

    def update_schedule(self, schedule_id: str, payload: UpdateUserScheduleRequest) -> None:
        self.transport.request_json(
            "PUT",
            f"/sell/feed/v1/schedule/{schedule_id}",
            scope_options=FEED_READ_SCOPE_OPTIONS,
            headers={"Content-Type": "application/json"},
            json_body=payload.model_dump(by_alias=True, exclude_none=True),
        )

    def delete_schedule(self, schedule_id: str) -> None:
        self.transport.request_json(
            "DELETE",
            f"/sell/feed/v1/schedule/{schedule_id}",
            scope_options=FEED_READ_SCOPE_OPTIONS,
        )

    def get_latest_result_file(self, schedule_id: str) -> FeedFileDownload:
        return self._download_file(f"/sell/feed/v1/schedule/{schedule_id}/download_result_file")

    def _download_file(self, path: str) -> FeedFileDownload:
        response = self.transport.request("GET", path, scope_options=FEED_READ_SCOPE_OPTIONS)
        content_disposition = response.headers.get("content-disposition")
        return FeedFileDownload(
            content=response.content,
            content_disposition=content_disposition,
            file_name=_extract_file_name(content_disposition),
        )
