import Foundation

/// Thin wrapper around the `/api/auth/*` endpoints. `AppState` is the only
/// caller that should hold state derived from these — feature screens call
/// through `AppState`, not this type directly, so there is exactly one place
/// that decides what "signed in" means.
enum AuthAPI {
    struct SignupBody: Encodable {
        let name: String?
        let email: String
        let password: String
        let lang: String
    }

    struct SignupResponse: Decodable {
        let status: String
        /// Only ever present against a local dev backend with no SMTP
        /// configured (see src/app/api/auth/signup/route.ts) — never in
        /// production. Useful for the simulator during development.
        let devLink: String?
    }

    struct LoginBody: Encodable {
        let email: String
        let password: String
        let remember: Bool
    }

    struct AppleSignInBody: Encodable {
        let identityToken: String
        let fullName: String?
    }

    /// `POST /api/auth/apple` — native Sign in with Apple. `identityToken` is
    /// `ASAuthorizationAppleIDCredential.identityToken` decoded to a UTF-8
    /// string; `fullName` is `credential.fullName` formatted for display,
    /// which Apple only ever supplies on the user's FIRST authorization with
    /// this app (pass `nil` on every subsequent sign-in — there is nothing to
    /// send). The backend verifies the token itself; nothing here is trusted
    /// data, it's just what gets forwarded for verification.
    static func appleSignIn(identityToken: String, fullName: String?) async throws -> LoginResponse {
        let request = try APIRequest.json(
            path: "/api/auth/apple",
            method: .post,
            body: AppleSignInBody(identityToken: identityToken, fullName: fullName),
            requiresAuth: false
        )
        return try await APIClient.shared.send(request)
    }

    /// `POST /api/auth/signup`. No session is created — the account is inert
    /// until the emailed confirmation link is opened, exactly like the web
    /// flow. The response is deliberately neutral (see the route's doc
    /// comment): callers cannot tell a new signup apart from "already
    /// registered" from this response alone.
    static func signup(name: String?, email: String, password: String) async throws -> SignupResponse {
        let request = try APIRequest.json(
            path: "/api/auth/signup",
            method: .post,
            body: SignupBody(name: name, email: email, password: password, lang: "de"),
            requiresAuth: false
        )
        return try await APIClient.shared.send(request)
    }

    /// `POST /api/auth/login`. On success the backend returns the raw session
    /// token in the body (because the request carries `X-Client: ios` — see
    /// APIClient) instead of only a Set-Cookie header. The caller is
    /// responsible for persisting it via `KeychainTokenStore`.
    static func login(email: String, password: String, remember: Bool = true) async throws -> LoginResponse {
        let request = try APIRequest.json(
            path: "/api/auth/login",
            method: .post,
            body: LoginBody(email: email, password: password, remember: remember),
            requiresAuth: false
        )
        return try await APIClient.shared.send(request)
    }

    /// `GET /api/auth/session` — "who am I". Never throws for "signed out";
    /// that's `{ user: null }`, HTTP 200.
    static func session() async throws -> SessionResponse {
        try await APIClient.shared.send(APIRequest(path: "/api/auth/session", method: .get))
    }

    /// `POST /api/auth/logout`. Best-effort: the caller should clear the
    /// local token and navigate to the login screen regardless of whether
    /// this succeeds (mirrors the web route's own "logout always proceeds"
    /// philosophy).
    static func logout() async throws {
        try await APIClient.shared.sendDiscardingResponse(
            APIRequest(path: "/api/auth/logout", method: .post)
        )
    }
}
