from http import HTTPStatus as status
from typing import Self

import httpx

import config
from api import errors, schemas


class AdNovaClient:
    def __init__(self) -> None:
        self.base_url = config.API_ENDPOINT

    async def __aenter__(self) -> Self:
        self.client = httpx.AsyncClient(base_url=self.base_url)
        return self

    async def __aexit__(
        self, exc_type: object, exc_val: object, exc_tb: object
    ) -> None:
        await self.client.aclose()

    def _handle_response(self, response: httpx.Response) -> httpx.Response:
        if response.status_code == status.BAD_REQUEST:
            error = schemas.BadRequestError.model_validate(response.json())
            raise errors.BadRequestError(error.detail)
        if response.status_code == status.FORBIDDEN:
            error = schemas.ForbiddenError.model_validate(response.json())
            raise errors.ForbiddenError(error.detail)
        if response.status_code == status.NOT_FOUND:
            error = schemas.NotFoundError.model_validate(response.json())
            raise errors.NotFoundError(error.detail)

        response.raise_for_status()

        return response

    def sync_get_advertiser(self, advertiser_id: str) -> schemas.Advertiser:
        client = httpx.Client(base_url=self.base_url)
        response = client.get(f"/advertisers/{advertiser_id}")
        self._handle_response(response)
        return schemas.Advertiser.model_validate(response.json())

    async def get_advertiser(self, advertiser_id: str) -> schemas.Advertiser:
        response = await self.client.get(f"/advertisers/{advertiser_id}")
        self._handle_response(response)
        return schemas.Advertiser.model_validate(response.json())

    async def create_campaign(
        self, advertiser_id: str, data: schemas.CampaignCreateIn
    ) -> schemas.CampaignOut:
        response = await self.client.post(
            f"/advertisers/{advertiser_id}/campaigns", json=data.model_dump()
        )
        self._handle_response(response)
        return schemas.CampaignOut.model_validate(response.json())

    async def list_campaigns(
        self, advertiser_id: str, page: int = 1, size: int = 100
    ) -> list[schemas.CampaignOut]:
        params = {"page": page, "size": size}
        response = await self.client.get(
            f"/advertisers/{advertiser_id}/campaigns", params=params
        )
        self._handle_response(response)
        return [
            schemas.CampaignOut.model_validate(item)
            for item in response.json()
        ]

    async def get_campaign(
        self, advertiser_id: str, campaign_id: str
    ) -> schemas.CampaignOut:
        response = await self.client.get(
            f"/advertisers/{advertiser_id}/campaigns/{campaign_id}"
        )
        self._handle_response(response)
        return schemas.CampaignOut.model_validate(response.json())

    async def update_campaign(
        self,
        advertiser_id: str,
        campaign_id: str,
        data: schemas.CampaignUpdateIn,
    ) -> schemas.CampaignOut:
        response = await self.client.put(
            f"/advertisers/{advertiser_id}/campaigns/{campaign_id}",
            json=data.model_dump(),
        )
        self._handle_response(response)
        return schemas.CampaignOut.model_validate(response.json())

    async def delete_campaign(
        self, advertiser_id: str, campaign_id: str
    ) -> None:
        response = await self.client.delete(
            f"/advertisers/{advertiser_id}/campaigns/{campaign_id}"
        )
        self._handle_response(response)

    async def upload_ad_image(
        self, advertiser_id: str, campaign_id: str, file: bytes
    ) -> schemas.CampaignOut:
        files = {"ad_image": file}
        response = await self.client.post(
            f"/advertisers/{advertiser_id}/campaigns/{campaign_id}/ad_image/upload",
            files=files,
        )
        self._handle_response(response)
        return schemas.CampaignOut.model_validate(response.json())

    async def delete_ad_image(
        self, advertiser_id: str, campaign_id: str
    ) -> None:
        response = await self.client.delete(
            f"/advertisers/{advertiser_id}/campaigns/{campaign_id}/ad_image/delete"
        )
        self._handle_response(response)

    async def get_advertiser_statistics(
        self, advertiser_id: str
    ) -> schemas.Stat:
        response = await self.client.get(f"/stats/advertisers/{advertiser_id}")
        self._handle_response(response)
        return schemas.Stat.model_validate(response.json())

    async def get_daily_advertiser_statistics(
        self, advertiser_id: str
    ) -> list[schemas.DailyStat]:
        response = await self.client.get(
            f"/stats/advertisers/{advertiser_id}/daily"
        )
        self._handle_response(response)
        return [
            schemas.DailyStat.model_validate(item) for item in response.json()
        ]

    async def get_campaign_statistics(self, campaign_id: str) -> schemas.Stat:
        response = await self.client.get(f"/stats/campaigns/{campaign_id}")
        self._handle_response(response)
        return schemas.Stat.model_validate(response.json())

    async def get_daily_campaign_statistics(
        self, campaign_id: str
    ) -> list[schemas.DailyStat]:
        response = await self.client.get(
            f"/stats/campaigns/{campaign_id}/daily"
        )
        self._handle_response(response)
        return [
            schemas.DailyStat.model_validate(item) for item in response.json()
        ]

    async def generate_ad_text(
        self, data: schemas.GenerateAdTextIn
    ) -> schemas.GenerateAdTextResult:
        response = await self.client.post(
            "/generate/ad_text", json=data.model_dump()
        )
        self._handle_response(response)
        return schemas.GenerateAdTextResult.model_validate(response.json())

    async def get_generate_ad_text_result(
        self, task_id: str
    ) -> schemas.GenerateAdTextResult:
        response = await self.client.get(f"/generate/ad_text/{task_id}/result")
        self._handle_response(response)
        return schemas.GenerateAdTextResult.model_validate(response.json())
