from __future__ import annotations

import json
from typing import Any
from urllib.parse import urlencode

import httpx

from ._auth import build_signature_header
from .errors import LinWheelApiError, LinWheelAuthError

DEFAULT_BASE_URL = "https://www.linwheel.io"
DEFAULT_TIMEOUT = 60.0


class _PostsResource:
    def __init__(self, client: LinWheel) -> None:
        self._client = client

    def list(
        self,
        *,
        approved: bool | None = None,
        scheduled: bool | None = None,
        published: bool | None = None,
        type: str | None = None,
        limit: int | None = None,
        offset: int | None = None,
    ) -> dict[str, Any]:
        params: dict[str, str] = {}
        if approved is not None:
            params["approved"] = str(approved).lower()
        if scheduled is not None:
            params["scheduled"] = str(scheduled).lower()
        if published is not None:
            params["published"] = str(published).lower()
        if type is not None:
            params["type"] = type
        if limit is not None:
            params["limit"] = str(limit)
        if offset is not None:
            params["offset"] = str(offset)
        return self._client._request("GET", "/api/agent/posts", params=params)

    def get(self, post_id: str) -> dict[str, Any]:
        return self._client._request("GET", f"/api/agent/posts/{post_id}")

    def update(
        self,
        post_id: str,
        *,
        full_text: str | None = None,
        hook: str | None = None,
        auto_publish: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {}
        if full_text is not None:
            body["fullText"] = full_text
        if hook is not None:
            body["hook"] = hook
        if auto_publish is not None:
            body["autoPublish"] = auto_publish
        return self._client._request(
            "PATCH", f"/api/agent/posts/{post_id}", body=body
        )

    def approve(self, post_id: str, approved: bool = True) -> dict[str, Any]:
        return self._client._request(
            "POST",
            f"/api/agent/posts/{post_id}/approve",
            body={"approved": approved},
        )

    def schedule(
        self,
        post_id: str,
        *,
        scheduled_at: str | None,
        auto_publish: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"scheduledAt": scheduled_at}
        if auto_publish is not None:
            body["autoPublish"] = auto_publish
        return self._client._request(
            "PATCH", f"/api/agent/posts/{post_id}/schedule", body=body
        )

    def image(
        self,
        post_id: str,
        *,
        headline_text: str,
        style_preset: str | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"headlineText": headline_text}
        if style_preset is not None:
            body["stylePreset"] = style_preset
        return self._client._request(
            "POST", f"/api/agent/posts/{post_id}/image", body=body
        )

    def carousel(
        self,
        post_id: str,
        *,
        slides: list[dict[str, Any]],
        style_preset: str | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"slides": slides}
        if style_preset is not None:
            body["stylePreset"] = style_preset
        return self._client._request(
            "POST", f"/api/agent/posts/{post_id}/carousel", body=body
        )


class _VoiceProfilesResource:
    def __init__(self, client: LinWheel) -> None:
        self._client = client

    def list(self) -> dict[str, Any]:
        return self._client._request("GET", "/api/agent/voice-profiles")

    def create(
        self,
        *,
        name: str,
        samples: list[str],
        description: str | None = None,
        is_active: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"name": name, "samples": samples}
        if description is not None:
            body["description"] = description
        if is_active is not None:
            body["isActive"] = is_active
        return self._client._request("POST", "/api/agent/voice-profiles", body=body)

    def delete(self, profile_id: str) -> dict[str, Any]:
        return self._client._request(
            "DELETE", f"/api/agent/voice-profiles/{profile_id}"
        )

    def activate(self, profile_id: str) -> dict[str, Any]:
        return self._client._request(
            "POST", f"/api/agent/voice-profiles/{profile_id}/activate"
        )


class LinWheel:
    def __init__(
        self,
        *,
        api_key: str,
        base_url: str = DEFAULT_BASE_URL,
        signing_secret: str | None = None,
        timeout: float = DEFAULT_TIMEOUT,
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._api_key = api_key
        self._signing_secret = signing_secret
        self._client = httpx.Client(timeout=timeout)
        self.posts = _PostsResource(self)
        self.voice_profiles = _VoiceProfilesResource(self)

    def close(self) -> None:
        self._client.close()

    def __enter__(self) -> LinWheel:
        return self

    def __exit__(self, *args: Any) -> None:
        self.close()

    # ── Content processing ────────────────────────────────────

    def analyze(self, *, text: str, context: str | None = None) -> dict[str, Any]:
        body: dict[str, Any] = {"text": text}
        if context is not None:
            body["context"] = context
        return self._request("POST", "/api/agent/analyze", body=body)

    def reshape(
        self,
        *,
        text: str,
        angles: list[str],
        pre_edit: bool | None = None,
        instructions: str | None = None,
        save_drafts: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"text": text, "angles": angles}
        if pre_edit is not None:
            body["preEdit"] = pre_edit
        if instructions is not None:
            body["instructions"] = instructions
        if save_drafts is not None:
            body["saveDrafts"] = save_drafts
        return self._request("POST", "/api/agent/reshape", body=body)

    def refine(
        self,
        *,
        text: str,
        intensity: str | None = None,
        instructions: str | None = None,
        post_type: str | None = None,
        save_draft: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"text": text}
        if intensity is not None:
            body["intensity"] = intensity
        if instructions is not None:
            body["instructions"] = instructions
        if post_type is not None:
            body["postType"] = post_type
        if save_draft is not None:
            body["saveDraft"] = save_draft
        return self._request("POST", "/api/agent/refine", body=body)

    def split(
        self,
        *,
        text: str,
        max_posts: int | None = None,
        instructions: str | None = None,
        save_drafts: bool | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"text": text}
        if max_posts is not None:
            body["maxPosts"] = max_posts
        if instructions is not None:
            body["instructions"] = instructions
        if save_drafts is not None:
            body["saveDrafts"] = save_drafts
        return self._request("POST", "/api/agent/split", body=body)

    # ── Drafting ──────────────────────────────────────────────

    def draft(
        self,
        *,
        full_text: str,
        hook: str | None = None,
        post_type: str | None = None,
        approved: bool | None = None,
        auto_publish: bool | None = None,
        scheduled_at: str | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"fullText": full_text}
        if hook is not None:
            body["hook"] = hook
        if post_type is not None:
            body["postType"] = post_type
        if approved is not None:
            body["approved"] = approved
        if auto_publish is not None:
            body["autoPublish"] = auto_publish
        if scheduled_at is not None:
            body["scheduledAt"] = scheduled_at
        return self._request("POST", "/api/agent/draft", body=body)

    def bundle(
        self,
        *,
        full_text: str,
        hook: str | None = None,
        post_type: str | None = None,
        approved: bool | None = None,
        auto_publish: bool | None = None,
        scheduled_at: str | None = None,
        image_headline_text: str | None = None,
        image_style_preset: str | None = None,
        carousel_slides: list[dict[str, Any]] | None = None,
    ) -> dict[str, Any]:
        body: dict[str, Any] = {"fullText": full_text}
        if hook is not None:
            body["hook"] = hook
        if post_type is not None:
            body["postType"] = post_type
        if approved is not None:
            body["approved"] = approved
        if auto_publish is not None:
            body["autoPublish"] = auto_publish
        if scheduled_at is not None:
            body["scheduledAt"] = scheduled_at
        if image_headline_text is not None:
            body["imageHeadlineText"] = image_headline_text
        if image_style_preset is not None:
            body["imageStylePreset"] = image_style_preset
        if carousel_slides is not None:
            body["carouselSlides"] = carousel_slides
        return self._request("POST", "/api/agent/bundle", body=body)

    # ── Internal HTTP layer ───────────────────────────────────

    def _request(
        self,
        method: str,
        path: str,
        body: dict[str, Any] | None = None,
        params: dict[str, str] | None = None,
    ) -> dict[str, Any]:
        url = f"{self._base_url}{path}"
        if params:
            url = f"{url}?{urlencode(params)}"

        body_str = json.dumps(body) if body is not None else ""
        headers: dict[str, str] = {
            "Authorization": f"Bearer {self._api_key}",
            "Content-Type": "application/json",
        }

        if self._signing_secret:
            # Parse path from url for canonical payload
            from urllib.parse import urlparse

            parsed_path = urlparse(url).path
            headers["X-LW-Signature"] = build_signature_header(
                method, parsed_path, body_str, self._signing_secret
            )

        response = self._client.request(
            method,
            url,
            content=body_str if method != "GET" else None,
            headers=headers,
        )

        try:
            data = response.json()
        except Exception:
            data = {"raw": response.text}

        if not response.is_success:
            error_msg = data.get("error", f"HTTP {response.status_code}") if isinstance(data, dict) else f"HTTP {response.status_code}"
            if response.status_code == 401:
                raise LinWheelAuthError(error_msg, data)
            raise LinWheelApiError(error_msg, response.status_code, data)

        return data
