"""Identity broker: no ProviderConnection or connector credential writes."""

import base64
import hashlib
import json
from datetime import UTC, datetime
from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit

from src.core.exceptions import ValidationError
from src.core.security.encryption import decrypt_value, encrypt_value
from src.domain.provider_auth.schemas import StartAuthorization
from src.domain.provider_auth.service import digest


async def start_identity(
    service, *, project, return_to, cloud_binding, completion_url, allowed_origins
):
    parsed = urlsplit(return_to)
    if (
        parsed.scheme != "https"
        or parsed.username
        or parsed.password
        or parsed.fragment
        or "\\" in return_to
        or any(ord(c) < 32 for c in return_to)
        or f"{parsed.scheme}://{parsed.netloc}" not in allowed_origins
    ):
        raise ValidationError("Identity return origin is not registered for this project")
    result = await service.start(
        workspace_id=project.workspace_id,
        project_id=project.id,
        initiated_by="cloud",
        body=StartAuthorization(grant_family="identity", browser_binding=cloud_binding),
    )
    state = parse_qs(urlsplit(result.authorize_url).query)["state"][0]
    row = await service.repo.session_by_state(digest(state))
    row.purpose = "identity"
    row.actor_type = "user"
    row.actor_id = "pending"
    row.browser_binding_hash = ""
    row.context_enc = encrypt_value(
        json.dumps(
            {
                "return_to": return_to,
                "cloud_binding_hash": digest(cloud_binding),
                "completion_url": completion_url,
            }
        )
    )
    dash = urlsplit(service.settings.google_platform_redirect_uri)
    result.authorize_url = urlunsplit(
        (dash.scheme, dash.netloc, "/api/oauth/google/start", urlencode({"state": state}), "")
    )
    return result


async def bind_identity(service, state, browser_binding):
    row = await service.repo.session_by_state(digest(state))
    if (
        not row
        or row.purpose != "identity"
        or row.status != "pending"
        or row.browser_binding_hash
        or row.expires_at <= datetime.now(UTC)
    ):
        raise ValidationError("Invalid identity start session")
    row.browser_binding_hash = digest(browser_binding)
    verifier = decrypt_value(row.verifier_enc)
    challenge = (
        base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip("=")
    )
    return {
        "authorize_url": "https://accounts.google.com/o/oauth2/v2/auth?"
        + urlencode(
            {
                "client_id": service.settings.google_platform_client_id,
                "redirect_uri": row.redirect_uri,
                "response_type": "code",
                "scope": "openid email profile",
                "state": state,
                "code_challenge": challenge,
                "code_challenge_method": "S256",
            }
        )
    }


async def consume_identity(service, project_id, ticket, cloud_binding):
    row = await service.repo.session_by_completion(digest(ticket), project_id)
    if (
        not row
        or row.purpose != "identity"
        or row.status == "consumed"
        or row.expires_at <= datetime.now(UTC)
    ):
        raise ValidationError("Invalid identity completion")
    context = json.loads(decrypt_value(row.context_enc))
    if context["cloud_binding_hash"] != digest(cloud_binding):
        raise ValidationError("Invalid identity browser binding")
    result = {
        "status": row.status,
        "return_to": context["return_to"],
        "identity": context.get("identity"),
    }
    row.status = "consumed"
    row.context_enc = None
    row.completion_hash = None
    return result
