from typing import Any

from .._base import (
    LONG_TIMEOUT,
    AsyncBaseResource,
    AsyncPageIterator,
    BaseResource,
    PageIterator,
    unwrap_data,
    unwrap_response,
)
from ..models import (
    AccommodationLoyaltyProgramme,
    AccommodationReview,
    NegotiatedRate,
    StaysAccommodation,
    StaysBooking,
    StaysBrand,
    StaysQuote,
    StaysSearchResult,
)


class StaysSearch(BaseResource):
    _path = "/stays/search"

    def create(
        self,
        *,
        check_in_date: str,
        check_out_date: str,
        guests: list[dict[str, Any]],
        rooms: int,
        accommodation: dict[str, Any] | None = None,
        location: dict[str, Any] | None = None,
        free_cancellation_only: bool | None = None,
        mobile: bool | None = None,
        negotiated_rate_ids: list[str] | None = None,
    ) -> list[StaysSearchResult]:
        data: dict[str, Any] = {
            "check_in_date": check_in_date,
            "check_out_date": check_out_date,
            "guests": guests,
            "rooms": rooms,
        }
        if accommodation is not None:
            data["accommodation"] = accommodation
        if location is not None:
            data["location"] = location
        if free_cancellation_only is not None:
            data["free_cancellation_only"] = free_cancellation_only
        if mobile is not None:
            data["mobile"] = mobile
        if negotiated_rate_ids is not None:
            data["negotiated_rate_ids"] = negotiated_rate_ids

        resp = self._transport.request("POST", self._path, json_body={"data": data})
        body = unwrap_response(resp)
        results = body.get("data", {}).get("results", [])
        return [StaysSearchResult.model_validate(r) for r in results]


class StaysSearchResults(BaseResource):
    _path = "/stays/search_results"

    def fetch_all_rates(self, search_result_id: str) -> StaysSearchResult:
        resp = self._transport.request(
            "POST", f"{self._path}/{search_result_id}/actions/fetch_all_rates",
        )
        return unwrap_data(resp, StaysSearchResult)


class StaysQuotes(BaseResource):
    _path = "/stays/quotes"

    def get(self, id: str) -> StaysQuote:
        return self._get(id, StaysQuote)

    def create(self, *, rate_id: str) -> StaysQuote:
        return self._create(StaysQuote, {"rate_id": rate_id})


class StaysBookings(BaseResource):
    _path = "/stays/bookings"

    def list(self, *, user_id: str | None = None, limit: int | None = None) -> PageIterator[StaysBooking]:
        return self._list(StaysBooking, params={"user_id": user_id, "limit": limit})

    def get(self, id: str) -> StaysBooking:
        return self._get(id, StaysBooking)

    def create(
        self,
        *,
        quote_id: str,
        email: str,
        phone_number: str,
        guests: "list[dict[str, Any]]",
        users: "list[str] | None" = None,
        loyalty_programme_account_number: str | None = None,
        payment: dict[str, Any] | None = None,
        accommodation_special_requests: str | None = None,
        metadata: dict[str, str] | None = None,
    ) -> StaysBooking:
        data: dict[str, Any] = {
            "quote_id": quote_id,
            "email": email,
            "phone_number": phone_number,
            "guests": guests,
        }
        if users is not None:
            data["users"] = users
        if loyalty_programme_account_number is not None:
            data["loyalty_programme_account_number"] = loyalty_programme_account_number
        if payment is not None:
            data["payment"] = payment
        if accommodation_special_requests is not None:
            data["accommodation_special_requests"] = accommodation_special_requests
        if metadata is not None:
            data["metadata"] = metadata
        return self._create(StaysBooking, data, timeout=LONG_TIMEOUT)

    def update(self, id: str, *, users: "list[str]") -> StaysBooking:
        return self._update(id, StaysBooking, {"users": users})

    def cancel(self, id: str) -> StaysBooking:
        resp = self._transport.request("POST", f"{self._path}/{id}/actions/cancel")
        return unwrap_data(resp, StaysBooking)


class Accommodation(BaseResource):
    _path = "/stays/accommodation"

    def list(
        self,
        *,
        latitude: float,
        longitude: float,
        radius: int = 5,
        limit: int | None = None,
    ) -> PageIterator[StaysAccommodation]:
        return self._list(
            StaysAccommodation,
            params={"latitude": latitude, "longitude": longitude, "radius": radius, "limit": limit},
        )

    def get(self, id: str) -> StaysAccommodation:
        return self._get(id, StaysAccommodation)

    def suggestions(self, *, query: str, location: dict[str, Any] | None = None) -> "list[dict]":
        data: dict[str, Any] = {"query": query}
        if location is not None:
            data["location"] = location
        resp = self._transport.request(
            "POST", f"{self._path}/suggestions", json_body={"data": data},
        )
        body = unwrap_response(resp)
        return body.get("data", [])

    def reviews(
        self, id: str, *, limit: int | None = None,
    ) -> "list[AccommodationReview]":
        resp = self._transport.request(
            "GET", f"{self._path}/{id}/reviews", params={"limit": limit},
        )
        body = unwrap_response(resp)
        reviews_data = body.get("data", {}).get("reviews", [])
        return [AccommodationReview.model_validate(r) for r in reviews_data]


class NegotiatedRates(BaseResource):
    _path = "/stays/negotiated_rates"

    def list(self, *, limit: int | None = None) -> PageIterator[NegotiatedRate]:
        return self._list(NegotiatedRate, params={"limit": limit})

    def get(self, id: str) -> NegotiatedRate:
        return self._get(id, NegotiatedRate)

    def create(
        self, *, rate_access_code: str, display_name: str, accommodation_ids: "list[str]",
    ) -> NegotiatedRate:
        return self._create(NegotiatedRate, {
            "rate_access_code": rate_access_code,
            "display_name": display_name,
            "accommodation_ids": accommodation_ids,
        })

    def update(
        self, id: str, *, display_name: str, accommodation_ids: "list[str]",
    ) -> NegotiatedRate:
        return self._update(id, NegotiatedRate, {
            "display_name": display_name, "accommodation_ids": accommodation_ids,
        })

    def delete(self, id: str) -> None:
        self._delete(id)


class StaysBrands(BaseResource):
    _path = "/stays/brands"

    def list(self) -> list[StaysBrand]:
        resp = self._transport.request("GET", self._path)
        body = unwrap_response(resp)
        return [StaysBrand.model_validate(b) for b in body.get("data", [])]

    def get(self, id: str) -> StaysBrand:
        return self._get(id, StaysBrand)


class AccommodationLoyaltyProgrammes(BaseResource):
    _path = "/stays/loyalty_programmes"

    def list(self) -> list[AccommodationLoyaltyProgramme]:
        resp = self._transport.request("GET", self._path)
        body = unwrap_response(resp)
        return [AccommodationLoyaltyProgramme.model_validate(p) for p in body.get("data", [])]


class AsyncStaysSearch(AsyncBaseResource):
    _path = "/stays/search"

    async def create(
        self,
        *,
        check_in_date: str,
        check_out_date: str,
        guests: list[dict[str, Any]],
        rooms: int,
        accommodation: dict[str, Any] | None = None,
        location: dict[str, Any] | None = None,
        free_cancellation_only: bool | None = None,
        mobile: bool | None = None,
        negotiated_rate_ids: list[str] | None = None,
    ) -> list[StaysSearchResult]:
        data: dict[str, Any] = {
            "check_in_date": check_in_date,
            "check_out_date": check_out_date,
            "guests": guests,
            "rooms": rooms,
        }
        if accommodation is not None:
            data["accommodation"] = accommodation
        if location is not None:
            data["location"] = location
        if free_cancellation_only is not None:
            data["free_cancellation_only"] = free_cancellation_only
        if mobile is not None:
            data["mobile"] = mobile
        if negotiated_rate_ids is not None:
            data["negotiated_rate_ids"] = negotiated_rate_ids

        resp = await self._transport.request("POST", self._path, json_body={"data": data})
        body = unwrap_response(resp)
        results = body.get("data", {}).get("results", [])
        return [StaysSearchResult.model_validate(r) for r in results]


class AsyncStaysSearchResults(AsyncBaseResource):
    _path = "/stays/search_results"

    async def fetch_all_rates(self, search_result_id: str) -> StaysSearchResult:
        resp = await self._transport.request(
            "POST", f"{self._path}/{search_result_id}/actions/fetch_all_rates",
        )
        return unwrap_data(resp, StaysSearchResult)


class AsyncStaysQuotes(AsyncBaseResource):
    _path = "/stays/quotes"

    async def get(self, id: str) -> StaysQuote:
        return await self._get(id, StaysQuote)

    async def create(self, *, rate_id: str) -> StaysQuote:
        return await self._create(StaysQuote, {"rate_id": rate_id})


class AsyncStaysBookings(AsyncBaseResource):
    _path = "/stays/bookings"

    def list(self, *, user_id: str | None = None, limit: int | None = None) -> AsyncPageIterator[StaysBooking]:
        return self._list(StaysBooking, params={"user_id": user_id, "limit": limit})

    async def get(self, id: str) -> StaysBooking:
        return await self._get(id, StaysBooking)

    async def create(
        self,
        *,
        quote_id: str,
        email: str,
        phone_number: str,
        guests: "list[dict[str, Any]]",
        users: "list[str] | None" = None,
        loyalty_programme_account_number: str | None = None,
        payment: dict[str, Any] | None = None,
        accommodation_special_requests: str | None = None,
        metadata: dict[str, str] | None = None,
    ) -> StaysBooking:
        data: dict[str, Any] = {
            "quote_id": quote_id,
            "email": email,
            "phone_number": phone_number,
            "guests": guests,
        }
        if users is not None:
            data["users"] = users
        if loyalty_programme_account_number is not None:
            data["loyalty_programme_account_number"] = loyalty_programme_account_number
        if payment is not None:
            data["payment"] = payment
        if accommodation_special_requests is not None:
            data["accommodation_special_requests"] = accommodation_special_requests
        if metadata is not None:
            data["metadata"] = metadata
        return await self._create(StaysBooking, data, timeout=LONG_TIMEOUT)

    async def update(self, id: str, *, users: "list[str]") -> StaysBooking:
        return await self._update(id, StaysBooking, {"users": users})

    async def cancel(self, id: str) -> StaysBooking:
        resp = await self._transport.request("POST", f"{self._path}/{id}/actions/cancel")
        return unwrap_data(resp, StaysBooking)


class AsyncAccommodation(AsyncBaseResource):
    _path = "/stays/accommodation"

    def list(
        self,
        *,
        latitude: float,
        longitude: float,
        radius: int = 5,
        limit: int | None = None,
    ) -> AsyncPageIterator[StaysAccommodation]:
        return self._list(
            StaysAccommodation,
            params={"latitude": latitude, "longitude": longitude, "radius": radius, "limit": limit},
        )

    async def get(self, id: str) -> StaysAccommodation:
        return await self._get(id, StaysAccommodation)

    async def suggestions(self, *, query: str, location: dict[str, Any] | None = None) -> "list[dict]":
        data: dict[str, Any] = {"query": query}
        if location is not None:
            data["location"] = location
        resp = await self._transport.request(
            "POST", f"{self._path}/suggestions", json_body={"data": data},
        )
        body = unwrap_response(resp)
        return body.get("data", [])

    async def reviews(
        self, id: str, *, limit: int | None = None,
    ) -> "list[AccommodationReview]":
        resp = await self._transport.request(
            "GET", f"{self._path}/{id}/reviews", params={"limit": limit},
        )
        body = unwrap_response(resp)
        reviews_data = body.get("data", {}).get("reviews", [])
        return [AccommodationReview.model_validate(r) for r in reviews_data]


class AsyncNegotiatedRates(AsyncBaseResource):
    _path = "/stays/negotiated_rates"

    def list(self, *, limit: int | None = None) -> AsyncPageIterator[NegotiatedRate]:
        return self._list(NegotiatedRate, params={"limit": limit})

    async def get(self, id: str) -> NegotiatedRate:
        return await self._get(id, NegotiatedRate)

    async def create(
        self, *, rate_access_code: str, display_name: str, accommodation_ids: "list[str]",
    ) -> NegotiatedRate:
        return await self._create(NegotiatedRate, {
            "rate_access_code": rate_access_code,
            "display_name": display_name,
            "accommodation_ids": accommodation_ids,
        })

    async def update(
        self, id: str, *, display_name: str, accommodation_ids: "list[str]",
    ) -> NegotiatedRate:
        return await self._update(id, NegotiatedRate, {
            "display_name": display_name, "accommodation_ids": accommodation_ids,
        })

    async def delete(self, id: str) -> None:
        await self._delete(id)


class AsyncStaysBrands(AsyncBaseResource):
    _path = "/stays/brands"

    async def list(self) -> list[StaysBrand]:
        resp = await self._transport.request("GET", self._path)
        body = unwrap_response(resp)
        return [StaysBrand.model_validate(b) for b in body.get("data", [])]

    async def get(self, id: str) -> StaysBrand:
        return await self._get(id, StaysBrand)


class AsyncAccommodationLoyaltyProgrammes(AsyncBaseResource):
    _path = "/stays/loyalty_programmes"

    async def list(self) -> list[AccommodationLoyaltyProgramme]:
        resp = await self._transport.request("GET", self._path)
        body = unwrap_response(resp)
        return [AccommodationLoyaltyProgramme.model_validate(p) for p in body.get("data", [])]
