"""Shopify Admin OAuth for shopify_app projects (platform Partner app)."""
from __future__ import annotations

import re
import urllib.parse
from datetime import datetime, timedelta, timezone
from typing import Optional
from uuid import UUID

import httpx
from jose import JWTError, jwt

from src.config import get_settings  # used for jwt_secret + oauth_scopes
from src.core.exceptions import ValidationError

_STATE_ALG = "HS256"
_STATE_TTL_MIN = 30
_SHOP_RE = re.compile(r"^[a-z0-9][a-z0-9-]*\.myshopify\.com$")


def normalize_shop_domain(raw: str) -> str:
    value = (raw or "").strip().lower().rstrip("/")
    if value.startswith("https://"):
        value = value[8:]
    if value.startswith("http://"):
        value = value[7:]
    if value and "." not in value:
        value = f"{value}.myshopify.com"
    if not value or not _SHOP_RE.match(value):
        raise ValidationError("Enter a valid shop domain (e.g. mystore.myshopify.com)")
    return value


def encode_oauth_state(
    *,
    workspace_id: UUID,
    project_id: UUID,
    user_id: UUID,
    shop_domain: str,
    return_url: str,
) -> str:
    settings = get_settings()
    now = datetime.now(timezone.utc)
    payload = {
        "ws": str(workspace_id),
        "pid": str(project_id),
        "sub": str(user_id),
        "shop": shop_domain,
        "ru": return_url,
        "iat": now,
        "exp": now + timedelta(minutes=_STATE_TTL_MIN),
    }
    return jwt.encode(payload, settings.jwt_secret, algorithm=_STATE_ALG)


def decode_oauth_state(token: str) -> dict:
    settings = get_settings()
    try:
        payload = jwt.decode(token, settings.jwt_secret, algorithms=[_STATE_ALG])
    except JWTError as e:
        raise ValidationError("Invalid or expired OAuth state") from e
    ws = payload.get("ws")
    pid = payload.get("pid")
    sub = payload.get("sub")
    shop = payload.get("shop")
    if not ws or not pid or not sub or not shop:
        raise ValidationError("Invalid OAuth state payload")
    ru = payload.get("ru")
    if ru is not None and not isinstance(ru, str):
        ru = None
    return {
        "workspace_id": UUID(ws),
        "project_id": UUID(pid),
        "user_id": UUID(sub),
        "shop_domain": str(shop),
        "return_url": ru,
    }


def shopify_oauth_callback_url(api_base: str) -> str:
    return f"{api_base.rstrip('/')}/api/integrations/shopify-app/callback"


def build_authorize_url(*, shop_domain: str, redirect_uri: str, state: str) -> str:
    """Build the Shopify OAuth authorize URL using the P2HS platform Partner App credentials."""
    settings = get_settings()
    client_id = (settings.shopify_platform_client_id or "").strip()
    if not client_id:
        raise ValidationError(
            "Shopify OAuth is not configured. Set SHOPIFY_PLATFORM_CLIENT_ID on the server."
        )
    scopes = (settings.shopify_oauth_scopes or "read_products,write_products").strip()
    params = {
        "client_id": client_id,
        "scope": scopes,
        "redirect_uri": redirect_uri,
        "state": state,
    }
    return f"https://{shop_domain}/admin/oauth/authorize?{urllib.parse.urlencode(params)}"


async def exchange_access_token(*, shop_domain: str, code: str) -> str:
    """Exchange an OAuth code for a per-store access token using platform credentials."""
    settings = get_settings()
    client_id = (settings.shopify_platform_client_id or "").strip()
    client_secret = (settings.shopify_platform_client_secret or "").strip()
    if not client_id or not client_secret:
        raise ValidationError(
            "Shopify OAuth is not configured. Set SHOPIFY_PLATFORM_CLIENT_ID and SHOPIFY_PLATFORM_CLIENT_SECRET."
        )
    async with httpx.AsyncClient(timeout=60.0) as client:
        r = await client.post(
            f"https://{shop_domain}/admin/oauth/access_token",
            json={"client_id": client_id, "client_secret": client_secret, "code": code},
        )
    if r.status_code >= 400:
        raise ValidationError("Shopify token exchange failed")
    data = r.json()
    token = (data.get("access_token") or "").strip()
    if not token:
        raise ValidationError("Shopify did not return an access token")
    return token
