#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from typing import Any, Dict, List, Optional

from pydantic import AwareDatetime
from typing_extensions import TypedDict, Unpack

from pendingai.api.models import ApiResource, ApiResourceList, ResponseModel
from pendingai.api_resources.interfaces import ResourceInterface
from pendingai.exceptions import InternalServerErrorException, NotFoundError
from pendingai.requestor.response import Response

# region Data Models


class Search(ApiResource):
    id: str
    object: str
    name: Optional[str] = None
    description: Optional[str] = None
    targets: List[str]
    databases: List[str]
    created_at: AwareDatetime
    updated_at: AwareDatetime
    status: str


class SearchStatus(ResponseModel):
    id: str
    status: str
    num_targets: int
    num_databases: int
    num_targets_completed: int
    num_databases_completed: int


class SearchResult(Search):
    targets: List[Dict[str, Any]]


class CreateSearch(TypedDict):
    targets: List[str]
    databases: Optional[List[str]]
    name: Optional[str]
    description: Optional[str]


class UpdateSearch(TypedDict):
    name: Optional[str]
    description: Optional[str]


class PaginationParams(TypedDict):
    limit: int
    starting_after: Optional[str]
    ending_before: Optional[str]


# endregion


class SearchInterface(ResourceInterface):
    def create(self, **request: Unpack[CreateSearch]) -> Search:
        response: Response = self._requestor.post("/search/v1/searches", json=request)
        try:
            search_id = response.headers["Location"].split("/")[-1]
        except KeyError:
            raise InternalServerErrorException("Missing 'Location' header in response.")

        response = self._requestor.get(f"/search/v1/searches/{search_id}")
        return Search.from_response(response)

    def list(self, **request: Unpack[PaginationParams]) -> ApiResourceList[Search]:
        response: Response = self._requestor.get("/search/v1/searches", params=request)
        return ApiResourceList[Search].from_response(response)

    def retrieve(self, id: str) -> Search:
        response: Response = self._requestor.get(f"/search/v1/searches/{id}")
        if response.status_code == 404:
            raise NotFoundError(id, "Search")
        return Search.from_response(response)

        try:
            return Search.model_validate(response.json())
        except Exception as e:
            raise ValueError(f"Failed to parse Search object from response: {e}")

    def status(self, id: str) -> SearchStatus:
        response: Response = self._requestor.get(f"/search/v1/searches/{id}/status")
        if response.status_code == 404:
            raise NotFoundError(id, "Search")
        return SearchStatus.from_response(response)

    def result(self, id: str) -> SearchResult:
        response: Response = self._requestor.get(f"/search/v1/searches/{id}/result")
        if response.status_code == 404:
            raise NotFoundError(id, "Search")
        return SearchResult.from_response(response)

    def update(self, id: str, **request: Unpack[UpdateSearch]) -> Search:
        response: Response = self._requestor.put(
            f"/search/v1/searches/{id}", json=request
        )
        if response.status_code == 404:
            raise NotFoundError(id, "Search")

        return Search.from_response(response)

    def delete(self, id: str) -> None:
        response: Response = self._requestor.delete(f"/search/v1/searches/{id}")
        if response.status_code == 404:
            raise NotFoundError(id, "Search")
